From 996986bed5f2dd97a3d76f584d8eddc1203f8396 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 12:11:48 +0200 Subject: first batch of changes --- src/afl-cc.c | 1544 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1544 insertions(+) create mode 100644 src/afl-cc.c (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c new file mode 100644 index 00000000..e11ce40a --- /dev/null +++ b/src/afl-cc.c @@ -0,0 +1,1544 @@ +/* + 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-ngram-coverage.h" + +#include +#include +#include +#include +#include +#include +#include + +#if (LLVM_MAJOR - 0 == 0) + #undef LLVM_MAJOR +#endif +#if !defined(LLVM_MAJOR) + #define LLVM_MAJOR 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 llvm_fullpath[PATH_MAX]; +static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, + compiler_mode, plusplus_mode; +static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto; +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 { + + INSTURMENT_DEFAULT = 0, + INSTRUMENT_CLASSIC = 1, + INSTRUMENT_AFL = 1, + INSTRUMENT_PCGUARD = 2, + INSTRUMENT_INSTRIM = 3, + INSTRUMENT_CFG = 3, + INSTRUMENT_LTO = 4, + INSTRUMENT_OPT_CTX = 8, + INSTRUMENT_OPT_NGRAM = 16 + +}; + +char instrument_mode_string[18][18] = { + + "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "", "", "CTX", "", + "", "", "", "", "", "", "NGRAM", "" + +}; + +enum { + + UNSET = 0, + LTO = 1, + LLVM = 2, + GCC_PLUGIN = 3, + GCC = 4 + +}; + +char compiler_mode_string[6][12] = { + + "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", + "GCC", "" + +}; + +u8 *getthecwd() { + + static u8 fail[] = ""; + if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + return cwd; + +} + +/* Try to find the runtime libraries. If that fails, abort. */ + +static u8 *find_object(u8 *obj, u8 *argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash = NULL, *tmp; + + if (afl_path) { + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/%s", afl_path, obj); +#else + tmp = alloc_printf("%s/%s", afl_path, obj); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = afl_path; + return tmp; + + } + + ck_free(tmp); + + } + + if (argv0) slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir; + + *slash = 0; + dir = ck_strdup(argv0); + *slash = '/'; + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/%s", dir, obj); +#else + tmp = alloc_printf("%s/%s", dir, obj); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = dir; + return tmp; + + } + + ck_free(tmp); + ck_free(dir); + + } + + tmp = alloc_printf("%s/%s", AFL_PATH, obj); +#ifdef __ANDROID__ + if (!access(tmp, R_OK)) { + +#else + if (!access(tmp, R_OK)) { + +#endif + + obj_path = AFL_PATH; + return tmp; + + } + + ck_free(tmp); + return NULL; + +} + +/* Try to find the runtime libraries. If that fails, abort. */ + +static void find_obj(u8 *argv0) { + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash, *tmp; + + if (afl_path) { + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/afl-compiler-rt.so", afl_path); +#else + tmp = alloc_printf("%s/afl-compiler-rt.o", afl_path); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = afl_path; + ck_free(tmp); + return; + + } + + ck_free(tmp); + + } + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir; + + *slash = 0; + dir = ck_strdup(argv0); + *slash = '/'; + +#ifdef __ANDROID__ + tmp = alloc_printf("%s/afl-compiler-rt.so", dir); +#else + tmp = alloc_printf("%s/afl-compiler-rt.o", dir); +#endif + + if (!access(tmp, R_OK)) { + + obj_path = dir; + ck_free(tmp); + return; + + } + + ck_free(tmp); + ck_free(dir); + + } + +#ifdef __ANDROID__ + if (!access(AFL_PATH "/afl-compiler-rt.so", R_OK)) { + +#else + if (!access(AFL_PATH "/afl-compiler-rt.o", R_OK)) { + +#endif + + obj_path = AFL_PATH; + return; + + } + + FATAL( + "Unable to find 'afl-compiler-rt.o' or 'afl-llvm-pass.so'. Please set " + "AFL_PATH"); + +} + +/* 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; + u8 *name; + + cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); + + name = strrchr(argv[0], '/'); + if (!name) + name = argv[0]; + else + ++name; + + 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) { + + 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) { + + 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) { + + cc_params[cc_par_cnt++] = "-B"; + cc_params[cc_par_cnt++] = obj_path; + + } + + if (compiler_mode == GCC_PLUGIN) { + + char *fplugin_arg = + alloc_printf("-fplugin=%s", find_object("afl-gcc-pass.so", argvnull)); + 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) { + + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + + 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) { + + 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) { + + 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) { + + 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) { + + 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/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-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); + + // 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++] = "-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); + + } + + cc_params[cc_par_cnt++] = "-fno-inline"; + + } + + if (lto_mode) { + + 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) + 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 >= 4 + cc_params[cc_par_cnt++] = + "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default +#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); + + } + + u32 idx; + if (lto_mode && argc > 1) { + + 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. */ + + while (--argc) { + + u8 *cur = *(++argv); + + 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 (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) + 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 (!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 (!strncmp(cur, "-O", 2)) have_o = 1; + if (!strncmp(cur, "-f", 2) && strstr(cur, "unroll-loop")) 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") || 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__) + 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[1024000];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + 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, 1024000)) == 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"; + + } + + if (preprocessor_only) { + + /* 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) { + + switch (bit_mode) { + + case 0: + 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: + 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: + 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; + + } + + #ifndef __APPLE__ + if (!shared_linking) + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); + #endif + + } + +#endif + + cc_params[cc_par_cnt] = NULL; + +} + +/* Main entry point */ + +int main(int argc, char **argv, char **envp) { + + int i; + 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 ((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 + + 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; + + } + + 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); + + } + + } + + 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, "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-... 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 can not set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + + } + + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && + 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 can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + + } + + if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; + + 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_INSTRUMENT")) { + + u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + + while (ptr) { + + if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || + strncasecmp(ptr, "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(ptr, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr, "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(ptr, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr, "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(ptr, "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 (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); + + } + + if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) { + + ptr += strlen("ngram"); + while (*ptr && (*ptr < '0' || *ptr > '9')) + ptr++; + + if (!*ptr) { + + if ((ptr = 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(ptr); + 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); + ptr = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1); + + } + + ptr = strtok(NULL, ":,;"); + + } + + } + + 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 (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { + + char *fp; + fp = realpath(argv[0], NULL); + + 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 SNAP DICT LAF " + "CMPLOG SELECT\n" + " [LTO] llvm LTO: %s%s\n" + " PCGUARD DEFAULT yes yes yes yes yes yes " + " yes\n" + " CLASSIC yes yes yes yes yes yes " + " yes\n" + " [LLVM] llvm: %s%s\n" + " PCGUARD %s yes yes yes module yes yes " + "extern\n" + " CLASSIC %s no yes yes module yes yes " + "yes\n" + " - NORMAL\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " INSTRIM no yes yes module yes yes " + " yes\n" + " - NORMAL\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " [GCC_PLUGIN] gcc plugin: %s%s\n" + " CLASSIC DEFAULT no yes yes no no no " + " simple\n" + " [GCC] simple gcc: %s%s\n" + " CLASSIC DEFAULT no 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 ? " [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"); + + 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" + " CLASSIC: decision target instrumentation (README.llvm.md)\n" + " CTX: CLASSIC + 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"); + + 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" + " SNAP: linux lkm snapshot module support [automatic] (speed " + "increase)\n" + " (https://github.com/AFLplusplus/AFL-Snapshot-LKM/)\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_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_INST_RATIO: percentage of branches to instrument\n" +#if LLVM_MAJOR < 9 + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif + " 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_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" + " AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function " + "(LTO only)\n" + " AFL_QUIET: suppress verbose output\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", + BIN_PATH, BIN_PATH); + + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode: CLASSIC, INSTRIM, " + "PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-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_CTX: use context sensitive coverage (for CLASSIC and " + "INSTRIM)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC and INSTRIM)\n"); + +#ifdef AFL_CLANG_FLTO + 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 they are in 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" + "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " + "\"%s\"\n" + "If anything fails - be sure to read README.lto.md!\n", + AFL_REAL_LD, AFL_CLANG_FLTO); +#endif + + } + + SAYF( + "For 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 with the the binary path \"%s\".\n", + LLVM_MAJOR, LLVM_BINDIR); + if (have_lto || have_llvm) SAYF("\n"); +#endif + + 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\n"); + + exit(1); + + } + + if (compiler_mode == LTO) { + + if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || + instrument_mode == INSTRUMENT_CFG) { + + lto_mode = 1; + if (!instrument_mode) { + + instrument_mode = INSTRUMENT_CFG; + 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 (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + + instrument_mode = INSTRUMENT_AFL; + WARNF( + "switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " + "SanitizerCoverage.html#partially-disabling-instrumentation"); + + } else + + instrument_mode = INSTRUMENT_PCGUARD; +#endif + + } + + if (instrument_opt_mode && compiler_mode != LLVM) + FATAL("CTX 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 { + + if (instrument_opt_mode == INSTRUMENT_OPT_CTX) + + ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); + else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) + ptr = alloc_printf("%s + NGRAM-%u", + instrument_mode_string[instrument_mode], ngram_size); + else + ptr = alloc_printf("%s + CTX + NGRAM-%u", + instrument_mode_string[instrument_mode], ngram_size); + + } + +#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_CLASSIC && + instrument_mode != INSTRUMENT_CFG) + FATAL( + "CTX and NGRAM instrumentation options can only be used with CFG " + "(recommended) and CLASSIC instrumentation modes!"); + + 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 (instrument_mode == INSTRUMENT_PCGUARD && + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) + FATAL( + "Instrumentation type PCGUARD does not support " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " + "12+), see " + "https://clang.llvm.org/docs/" + "SanitizerCoverage.html#partially-disabling-instrumentation"); + + 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 && !lto_mode && + ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { + + u32 map_size = atoi(ptr2); + if (map_size != MAP_SIZE) + WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); + + } + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < argc; i++) + SAYF(" \"%s\"", argv[i]); + SAYF("\n"); + + } + + 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 \n"); + +#ifndef __ANDROID__ + find_obj(argv[0]); +#endif + + edit_params(argc, argv, envp); + + if (debug) { + + SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + for (i = 0; i < cc_par_cnt; i++) + SAYF(" \"%s\"", cc_params[i]); + SAYF("\n"); + + } + + execvp(cc_params[0], (char **)cc_params); + + FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + + return 0; + +} + -- cgit 1.4.1 From 8ec41b2585390c54256dd823e1beafd6246d9976 Mon Sep 17 00:00:00 2001 From: aflpp Date: Sun, 6 Sep 2020 11:40:41 +0200 Subject: fix various warnings --- GNUmakefile.llvm | 6 ++---- instrumentation/SanitizerCoverageLTO.so.cc | 12 ++++++++---- instrumentation/afl-llvm-dict2file.so.cc | 2 +- src/afl-cc.c | 6 +----- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 1bb3d265..d432021b 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -422,13 +422,11 @@ document: ./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e afl-compiler-rt-32.o && ln -sf afl-compiler-rt-32.o afl-llvm-rt-64.o + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-32.o afl-llvm-rt-32.o; else echo "failed (that's fine)"; fi ./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " - @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @test -e afl-compiler-rt-64.o && ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o; else echo "failed (that's fine)"; fi .PHONY: test_build test_build: $(PROGS) diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index f4958d80..0a136d6f 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -197,8 +197,8 @@ class ModuleSanitizerCoverage { void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); - std::pair CreateSecStartEnd(Module &M, const char *Section, - Type *Ty); +// std::pair CreateSecStartEnd(Module &M, const char *Section, +// Type *Ty); void SetNoSanitizeMetadata(Instruction *I) { @@ -208,8 +208,8 @@ class ModuleSanitizerCoverage { } std::string getSectionName(const std::string &Section) const; - std::string getSectionStart(const std::string &Section) const; - std::string getSectionEnd(const std::string &Section) const; +// std::string getSectionStart(const std::string &Section) const; +// std::string getSectionEnd(const std::string &Section) const; FunctionCallee SanCovTracePCIndir; FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/; Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, @@ -349,6 +349,7 @@ PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, } +/* std::pair ModuleSanitizerCoverage::CreateSecStartEnd( Module &M, const char *Section, Type *Ty) { @@ -373,6 +374,7 @@ std::pair ModuleSanitizerCoverage::CreateSecStartEnd( return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEndPtr); } +*/ bool ModuleSanitizerCoverage::instrumentModule( Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { @@ -1536,6 +1538,7 @@ std::string ModuleSanitizerCoverage::getSectionName( } +/* std::string ModuleSanitizerCoverage::getSectionStart( const std::string &Section) const { @@ -1553,6 +1556,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( return "__stop___" + Section; } +*/ char ModuleSanitizerCoverageLegacyPass::ID = 0; diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index e87ecce8..ef42756e 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -84,7 +84,7 @@ class AFLdict2filePass : public ModulePass { void dict2file(int fd, u8 *mem, u32 len) { - int i, j, binary = 0; + u32 i, j, binary = 0; char line[MAX_AUTO_EXTRA * 8], tmp[8]; strcpy(line, "\""); diff --git a/src/afl-cc.c b/src/afl-cc.c index e11ce40a..ddda3845 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1153,9 +1153,6 @@ int main(int argc, char **argv, char **envp) { if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { - char *fp; - fp = realpath(argv[0], NULL); - printf("afl-cc" VERSION " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); @@ -1302,8 +1299,7 @@ int main(int argc, char **argv, char **envp) { " 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", - BIN_PATH, BIN_PATH); + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); SAYF( "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " -- cgit 1.4.1 From 9544b3dbf22f1007f7d3f77593ec746a0345a587 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 7 Sep 2020 12:35:31 -0300 Subject: rewrite gcc plugin When we started using AFL, it did not have an integrated GCC plugin. There was one proposed by Austin Seipp, but for various reasons we ended up using some of its infrastructure (runtime and wrapper), but writing the GCC plugin proper from scratch. With AFL++'s renewed interest in a GCC plugin, we rebased ours, with some features that are or were missing in the one that was integrated: * efficient, fully-functional inline and out-of-line instrumentation Inline instrumentation was work in progress in the original plugin. Controlled by AFL_GCC_OUT_OF_LINE. * reproducible instrumentation Obey -frandom-seed for pseudorandom number generation. * licensing clarity and strict compliance GPLv3+ for the plugin, that uses GCC internals; add a copy of the license, as required. * allow/deny list support Copied and adjusted from the LLVM plugin implementation. * neverZero support Not as compact as the asm-wrapper version, but likely more efficient. Both are quite thread-unsafe, with different caveats. Controlled with AFL_GCC_SKIP_NEVERZERO. --- GNUmakefile.llvm | 2 +- docs/INSTALL.md | 5 +- include/envs.h | 5 + instrumentation/COPYING3 | 674 ++++++++++++++++++ instrumentation/README.out_of_line.md | 21 + instrumentation/afl-compiler-rt.o.c | 18 +- instrumentation/afl-gcc-pass.so.cc | 1218 +++++++++++++++++++++------------ src/afl-cc.c | 6 + test/test-performance.sh | 43 +- 9 files changed, 1545 insertions(+), 447 deletions(-) create mode 100644 instrumentation/COPYING3 create mode 100644 instrumentation/README.out_of_line.md (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d432021b..3eefdf90 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -221,7 +221,7 @@ endif ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode" else - AFL_CLANG_DEBUG_PREFIX = "" + AFL_CLANG_DEBUG_PREFIX = endif CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 diff --git a/docs/INSTALL.md b/docs/INSTALL.md index fb7b5642..93a46caf 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,6 +28,8 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. +Likewise, if you are using GCC, please review gcc_plugin/README.md. + You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but afl-fuzz will guide you through that if necessary. @@ -157,7 +159,8 @@ instrumentation mode (`-Q`) will not work. ## 6. Everything else You're on your own. On POSIX-compliant systems, you may be able to compile and -run the fuzzer; and the LLVM mode may offer a way to instrument non-x86 code. +run the fuzzer; and the LLVM and GCC plugin modes may offer a way to instrument +non-x86 code. The fuzzer will run on Windows in WSL only. It will not work under Cygwin on in the normal Windows world. It could be ported to the latter platform fairly easily, but it's a pretty bad diff --git a/include/envs.h b/include/envs.h index d9968fcd..3a06aa2a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -45,7 +45,12 @@ static char *afl_environment_variables[] = { "AFL_EXIT_WHEN_DONE", "AFL_FAST_CAL", "AFL_FORCE_UI", + "AFL_GCC_ALLOWLIST", + "AFL_GCC_DENYLIST", + "AFL_GCC_BLOCKLIST", "AFL_GCC_INSTRUMENT_FILE", + "AFL_GCC_OUT_OF_LINE", + "AFL_GCC_SKIP_NEVERZERO", "AFL_GCJ", "AFL_HANG_TMOUT", "AFL_FORKSRV_INIT_TMOUT", diff --git a/instrumentation/COPYING3 b/instrumentation/COPYING3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/instrumentation/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/instrumentation/README.out_of_line.md b/instrumentation/README.out_of_line.md new file mode 100644 index 00000000..aad215b6 --- /dev/null +++ b/instrumentation/README.out_of_line.md @@ -0,0 +1,21 @@ +=========================================== +Using afl++ without inlined instrumentation +=========================================== + + This file describes how you can disable inlining of instrumentation. + + +By default, the GCC plugin will duplicate the effects of calling +__afl_trace (see afl-gcc-rt.o.c) in instrumented code, instead of +issuing function calls. + +The calls are presumed to be slower, more so because the rt file +itself is not optimized by the compiler. + +Setting AFL_GCC_OUT_OF_LINE=1 in the environment while compiling code +with the plugin will disable this inlining, issuing calls to the +unoptimized runtime instead. + +You probably don't want to do this, but it might be useful in certain +AFL debugging scenarios, and it might work as a fallback in case +something goes wrong with the inlined instrumentation. diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index a3d75b15..05e2d50d 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -38,7 +38,9 @@ #include #include +#if ! __GNUC__ #include "llvm/Config/llvm-config.h" +#endif #ifdef __linux__ #include "snapshot-inl.h" @@ -109,14 +111,22 @@ static u8 _is_sancov; void __afl_trace(const u32 x) { + PREV_LOC_T prev = __afl_prev_loc[0]; + __afl_prev_loc[0] = (x >> 1); + + u8 *p = &__afl_area_ptr[prev ^ x]; + #if 1 /* enable for neverZero feature. */ - __afl_area_ptr[__afl_prev_loc[0] ^ x] += - 1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc[0] ^ x]) == 0); +# if __GNUC__ + u8 c = __builtin_add_overflow (*p, 1, p); + *p += c; +# else + *p += 1 + ((u8)(1 + *p == 0); +# endif #else - ++__afl_area_ptr[__afl_prev_loc[0] ^ x]; + ++*p; #endif - __afl_prev_loc[0] = (x >> 1); return; } diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c5614aca..6d6f4636 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -1,36 +1,23 @@ -// -// There are some TODOs in this file: -// - fix instrumentation via external call -// - fix inline instrumentation -// - implement instrument list feature -// - dont instrument blocks that are uninteresting -// - implement neverZero -// - -/* - american fuzzy lop++ - GCC instrumentation pass - --------------------------------------------- - - Written by Austin Seipp with bits from - Emese Revfy - - Fixed by Heiko Eißfeldt 2019-2020 for AFL++ - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. Some of the boilerplate code below for - afl_pass to adapt to different GCC versions was taken from Emese - Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3. - - (NOTE: this plugin code is under GPLv3, in order to comply with the - GCC runtime library exception, which states that you may distribute - "Target Code" from the compiler under a license of your choice, as - long as the "Compilation Process" is "Eligible", and contains no - GPL-incompatible software in GCC "during the process of - transforming high level code to target code". In this case, the - plugin will be used to generate "Target Code" during the - "Compilation Process", and thus it must be GPLv3 to be "eligible".) - - Copyright (C) 2015 Austin Seipp +/* GCC plugin for instrumentation of code for american fuzzy lop. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AdaCore + + Written by Alexandre Oliva , based on the AFL + LLVM pass by Laszlo Szekeres and Michal + Zalewski , and copying a little boilerplate + from GCC's libcc1 plugin and GCC proper. Aside from the + boilerplate, namely includes and the pass data structure, and pass + initialization code and output messages borrowed and adapted from + the LLVM pass into plugin_init and plugin_finalize, the + implementation of the GCC pass proper is written from scratch, + aiming at similar behavior and performance to that of the LLVM + pass, and also at compatibility with the out-of-line + instrumentation and run times of AFL++, as well as of an earlier + GCC plugin implementation by Austin Seipp . The + implementation of Allow/Deny Lists is adapted from that in the LLVM + plugin. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,555 +34,910 @@ */ -#define BUILD_INLINE_INST +/* This file implements a GCC plugin that introduces an + instrumentation pass for AFL. What follows is the specification + used to rewrite it, extracted from the functional llvm_mode pass + and from an implementation of the gcc_plugin started by Austin + Seipp . + + Declare itself as GPL-compatible. + + Define a 'plugin_init' function. + + Check version against the global gcc_version. + + Register a PLUGIN_INFO object with .version and .help. + + Initialize the random number generator seed with GCC's + random seed. + + Set quiet mode depending on whether stderr is a terminal and + AFL_QUIET is set. + + Output some identification message if not in quiet mode. + + Parse AFL_INST_RATIO, if set, as a number between 0 and 100. Error + out if it's not in range; set up an instrumentation ratio global + otherwise. + + Introduce a single instrumentation pass after SSA. + + The new pass is to be a GIMPLE_PASS. Given the sort of + instrumentation it's supposed to do, its todo_flags_finish will + certainly need TODO_update_ssa, and TODO_cleanup_cfg. + TODO_verify_il is probably desirable, at least during debugging. + TODO_rebuild_cgraph_edges is required only in the out-of-line + instrumentation mode. + + The instrumentation pass amounts to iterating over all basic blocks + and optionally inserting one of the instrumentation sequences below + after its labels, to indicate execution entered the block. + + A block should be skipped if R(100) (from ../types.h) is >= the + global instrumentation ratio. + + A block may be skipped for other reasons, such as if all of its + predecessors have a single successor. + + For an instrumented block, a R(MAP_SIZE) say should be + generated to be used as its location number. Let be a compiler + constant built out of it. + + Count instrumented blocks and print a message at the end of the + compilation, if not in quiet mode. + + Instrumentation in "dumb" or "out-of-line" mode requires calling a + function, passing it the location number. The function to be + called is __afl_trace, implemented in afl-gcc-rt.o.c. Its + declaration needs only be created once. + + Build the call statement (), then add it to the seq to be + inserted. + + Instrumentation in "fast" or "inline" mode performs the computation + of __afl_trace as part of the function. + + It needs to read and write __afl_prev_loc, a TLS u32 variable. Its + declaration

needs only be created once. + + It needs to read and dereference __afl_area_ptr, a pointer to (an + array of) char. Its declaration needs only be created once. + + The instrumentation sequence should then be filled with the + following statements: + + Load from

to a temporary () of the same type. + + Compute ^ in sizetype, converting types as needed. + + Pointer-add (to be introduced at a later point) and into + another temporary . + + Increment the <*A> MEM_REF. + + Store >> 1 in

. + + Temporaries used above need only be created once per function. + + If any block was instrumented in a function, an initializer for + needs to be introduced, loading it from and inserting it in the + entry edge for the entry block. +*/ #include "../include/config.h" #include "../include/debug.h" -/* clear helper macros AFL types pull in, which intervene with gcc-plugin - * headers from GCC-8 */ +#include +#include +#include + #ifdef likely - #undef likely +# undef likely #endif #ifdef unlikely - #undef unlikely +# undef unlikely #endif -#include -#include -#include - #include #include #include +#include +#include + #include #include -#include -#include -#include +#include #include -#include -#include -#include +#include +#include +#include #include #include -#include -#include -#include +#include + #include -#include -#include -#include -#include -/* -------------------------------------------------------------------------- */ -/* -- AFL instrumentation pass ---------------------------------------------- */ +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; -static int be_quiet = 0; -static unsigned int inst_ratio = 100; -static bool inst_ext = true; -static std::list myInstrumentList; +namespace { -static unsigned int ext_call_instrument(function *fun) { +static const struct pass_data afl_pass_data = + { + .type = GIMPLE_PASS, + .name = "afl", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa + | TODO_cleanup_cfg + | TODO_verify_il), + }; + +struct afl_pass : gimple_opt_pass { + afl_pass (bool quiet, unsigned int ratio) + : gimple_opt_pass (afl_pass_data, g), + be_quiet (quiet), + debug (!!getenv ("AFL_DEBUG")), + inst_ratio (ratio), +#ifdef AFL_GCC_OUT_OF_LINE + out_of_line (!!(AFL_GCC_OUT_OF_LINE)), +#else + out_of_line (getenv ("AFL_GCC_OUT_OF_LINE")), +#endif + neverZero (!getenv ("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks (0) + { + initInstrumentList (); + } - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + + /* How likely (%) is a block to be instrumented? */ + const unsigned int inst_ratio; + + /* Should we use slow, out-of-line call-based instrumentation? */ + const bool out_of_line; + + /* Should we make sure the map edge-crossing counters never wrap + around to zero? */ + const bool neverZero; + + /* Count instrumented blocks. */ + int inst_blocks; + + virtual unsigned int + execute (function *fn) + { + if (!isInInstrumentList(fn)) + return 0; + + int blocks = 0; + + /* These are temporaries used by inline instrumentation only, that + are live throughout the function. */ + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, + ntry = NULL, cntr = NULL, xaddc = NULL, xincr = NULL; + + basic_block bb; + FOR_EACH_BB_FN (bb, fn) + { + if (!instrument_block_p (bb)) + continue; + + /* Generate the block identifier. */ + unsigned bid = R (MAP_SIZE); + tree bidt = build_int_cst (sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) + { + static tree afl_trace = get_afl_trace_decl (); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call (afl_trace, 1, bidt); + gimple_seq_add_stmt (&seq, call); + } + else + { + static tree afl_prev_loc = get_afl_prev_loc_decl (); + static tree afl_area_ptr = get_afl_area_ptr_decl (); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var (TREE_TYPE (afl_prev_loc), ".afl_prev_loc"); + gimple *load_loc = gimple_build_assign (ploc, afl_prev_loc); + gimple_seq_add_stmt (&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) + indx = create_tmp_var (TREE_TYPE (bidt), ".afl_index"); + gimple *conv_ploc + = gimple_build_assign (indx, + fold_convert (TREE_TYPE (indx), + ploc)); + gimple_seq_add_stmt (&seq, conv_ploc); + gimple *xor_loc = gimple_build_assign (indx, BIT_XOR_EXPR, + indx, bidt); + gimple_seq_add_stmt (&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) + { + map = afl_area_ptr; + map_ptr = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_ptr"); + ntry = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_entry"); + } + gimple *idx_map = gimple_build_assign (ntry, POINTER_PLUS_EXPR, + map_ptr, indx); + gimple_seq_add_stmt (&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ntry)), + ntry, build_zero_cst (TREE_TYPE (ntry))); + if (blocks == 0) + cntr = create_tmp_var (TREE_TYPE (memref), ".afl_edge_count"); + + gimple *load_cntr = gimple_build_assign (cntr, memref); + gimple_seq_add_stmt (&seq, load_cntr); + + tree cntrb = cntr; + tree incrv = build_one_cst (TREE_TYPE (cntr)); + + if (neverZero) + { + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) + { + xaddc = create_tmp_var (build_complex_type + (TREE_TYPE (memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var (TREE_TYPE (memref), + ".afl_edge_xincr"); + } + + auto_vec vargs (2); + vargs.quick_push (cntr); + vargs.quick_push (incrv); + gcall *add1_cntr + = gimple_build_call_internal_vec (IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs (add1_cntr, xaddc); + gimple_seq_add_stmt (&seq, add1_cntr); + + cntrb = build1 (REALPART_EXPR, TREE_TYPE (cntr), xaddc); + incrv = build1 (IMAGPART_EXPR, TREE_TYPE (xincr), xaddc); + } + + gimple *incr_cntr = gimple_build_assign (cntr, PLUS_EXPR, + cntrb, incrv); + gimple_seq_add_stmt (&seq, incr_cntr); + + gimple *store_cntr = gimple_build_assign (unshare_expr (memref), + cntr); + gimple_seq_add_stmt (&seq, store_cntr); + + /* Store bid >> 1 in __afl_prev_loc. */ + gimple *shift_loc = gimple_build_assign (ploc, + build_int_cst + (TREE_TYPE (ploc), + bid >> 1)); + gimple_seq_add_stmt (&seq, shift_loc); + gimple *store_loc = gimple_build_assign (afl_prev_loc, ploc); + gimple_seq_add_stmt (&seq, store_loc); + } + + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels (bb); + gsi_insert_seq_before (&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + } - tree fntype = build_function_type_list(void_type_node, /* return */ - uint32_type_node, /* args */ - NULL_TREE); /* done */ - tree fndecl = build_fn_decl("__afl_trace", fntype); - TREE_STATIC(fndecl) = 1; /* Defined elsewhere */ - TREE_PUBLIC(fndecl) = 1; /* Public */ - DECL_EXTERNAL(fndecl) = 1; /* External linkage */ - DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */ + /* Aggregate the instrumented block count. */ + inst_blocks += blocks; - FOR_EACH_BB_FN(bb, fun) { + if (blocks) + { + if (out_of_line) + return TODO_rebuild_cgraph_edges; - gimple_seq fcall; - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; + gimple_seq seq = NULL; - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + gimple *load_ptr = gimple_build_assign (map_ptr, map); + gimple_seq_add_stmt (&seq, load_ptr); - int more_than_one = -1; - edge ep; - edge_iterator eip; + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn)); + gsi_insert_seq_on_edge_immediate (e, seq); + } - FOR_EACH_EDGE(ep, eip, bb->preds) { + return 0; + } - int count = 0; - if (more_than_one == -1) more_than_one = 0; + /* Decide whether to instrument block BB. Skip it due to the random + distribution, or if it's the single successor of all its + predecessors. */ + inline bool + instrument_block_p (basic_block bb) + { + if (R (100) >= inst_ratio) + return false; + + edge e; edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->preds) + if (!single_succ_p (e->src)) + return true; + + return false; + } - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { + /* Create and return a declaration for the __afl_trace rt function. */ + static inline tree + get_afl_trace_decl () + { + tree type = build_function_type_list (void_type_node, + uint16_type_node, + NULL_TREE); + tree decl = build_fn_decl ("__afl_trace", type); - basic_block Succ = es->dest; - if (Succ != NULL) count++; + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; - } + return decl; + } - if (count > 1) more_than_one = 1; + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_prev_loc_decl () + { + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_prev_loc"), + uint32_type_node); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + set_decl_tls_model (decl, + (flag_pic + ? TLS_MODEL_INITIAL_EXEC + : TLS_MODEL_LOCAL_EXEC)); + return decl; + } + + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_area_ptr_decl () + { + tree type = build_pointer_type (unsigned_char_type_node); + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_area_ptr"), + type); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + + return decl; + } + /* This is registered as a plugin finalize callback, to print an + instrumentation summary unless in quiet mode. */ + static void + plugin_finalize (void *, void *p) + { + opt_pass *op = (opt_pass *)p; + afl_pass &self = (afl_pass &)*op; + + if (!self.be_quiet) { + if (!self.inst_blocks) + WARNF ("No instrumentation targets found."); + else + OKF ("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); } + } - if (more_than_one != 1) continue; +#define report_fatal_error(msg) BADF(msg) - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; + std::list allowListFiles; + std::list allowListFunctions; + std::list denyListFiles; + std::list denyListFunctions; - /* Make up cur_loc */ - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); + /* Note: this ignore check is also called in isInInstrumentList() */ + bool isIgnoreFunction(function *F) { - /* Update bitmap via external call */ - /* to quote: - * /+ Trace a basic block with some ID +/ - * void __afl_trace(u32 x); - */ + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz - fcall = gimple_build_call( - fndecl, 1, - cur_loc); /* generate the function _call_ to above built reference, with - *1* parameter -> the random const for the location */ - gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */ + static const char *ignoreList[] = { - /* Done - grab the entry to the block and insert sequence */ - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT); + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" - ++finst_blocks; + }; - } + const char *name = IDENTIFIER_POINTER (DECL_NAME (F->decl)); + int len = IDENTIFIER_LENGTH (DECL_NAME (F->decl)); + + for (auto const &ignoreListFunc : ignoreList) { - /* Say something nice. */ - if (!be_quiet) { + if (strncmp (name, ignoreListFunc, len) == 0) { return true; } + + } - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + return false; } - return 0; + void initInstrumentList() { + + char *allowlist = getenv("AFL_GCC_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_GCC_DENYLIST"); + if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); + getline(fileStream, line); -} + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + + } -static unsigned int inline_instrument(function *fun) { - - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; - tree one = build_int_cst(unsigned_char_type_node, 1); - // tree zero = build_int_cst(unsigned_char_type_node, 0); - - /* Set up global type declarations */ - tree map_type = build_pointer_type(unsigned_char_type_node); - tree map_ptr_g = - build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_area_ptr", 14), map_type); - TREE_USED(map_ptr_g) = 1; - TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */ - DECL_PRESERVE_P(map_ptr_g) = 1; - DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */ - rest_of_decl_compilation(map_ptr_g, 1, 0); - - tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_prev_loc", 14), - uint32_type_node); - TREE_USED(prev_loc_g) = 1; - TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */ - DECL_PRESERVE_P(prev_loc_g) = 1; - DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */ - set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */ - rest_of_decl_compilation(prev_loc_g, 1, 0); - - FOR_EACH_BB_FN(bb, fun) { - - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; - - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - - int more_than_one = -1; - edge ep; - edge_iterator eip; - FOR_EACH_EDGE(ep, eip, bb->preds) { - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { - - basic_block Succ = es->dest; - if (Succ != NULL) count++; + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } - if (count > 1) more_than_one = 1; + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } - if (more_than_one != 1) continue; - - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); - - /* Load prev_loc, xor with cur_loc */ - // gimple_assign - tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc"); - gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g); - gimple_seq_add_stmt(&seq, g); // load prev_loc - update_stmt(g); - - // gimple_assign - tree area_off = create_tmp_var_raw(uint32_type_node, "area_off"); - g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc); - gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc - update_stmt(g); - - /* Update bitmap */ - - // gimple_assign - tree map_ptr = create_tmp_var(map_type, "map_ptr"); - tree map_ptr2 = create_tmp_var(map_type, "map_ptr2"); - - g = gimple_build_assign(map_ptr, map_ptr_g); - gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr - update_stmt(g); - -#if 1 - #if 0 - tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off); - g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #else - g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #endif - - // gimple_assign - tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, MEM_REF, map_ptr2); - gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2 - update_stmt(g); -#else - tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off); - tree array_address = build1(ADDR_EXPR, map_type, atIndex); - tree array_access = build1(INDIRECT_REF, map_type, array_address); - tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, array_access); - gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off) - update_stmt(g); -#endif - // gimple_assign - tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2"); - g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one); - gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1 - update_stmt(g); + if (denylist) { - // TODO: neverZero: here we have to check if tmp3 == 0 - // and add 1 if so + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); + getline(fileStream, line); - // gimple_assign - // tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3"); - g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2); - gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2 - update_stmt(g); + while (fileStream) { - /* Set prev_loc to cur_loc >> 1 */ + int is_file = -1; + std::size_t npos; + std::string original_line = line; - // gimple_assign - tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1); - tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2"); - g = gimple_build_assign(prev_loc2, shifted_loc); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); - g = gimple_build_assign(prev_loc_g, prev_loc2); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - /* Done - grab the entry to the block and insert sequence */ + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT); + if (line.compare(0, 4, "fun:") == 0) { - ++finst_blocks; + is_file = 0; + line = line.substr(4); - } + } else if (line.compare(0, 9, "function:") == 0) { - /* Say something nice. */ - if (!be_quiet) { + is_file = 0; + line = line.substr(9); - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + } else if (line.compare(0, 4, "src:") == 0) { - } + is_file = 1; + line = line.substr(4); - return 0; + } else if (line.compare(0, 7, "source:") == 0) { -} + is_file = 1; + line = line.substr(7); -/* -------------------------------------------------------------------------- */ -/* -- Boilerplate and initialization ---------------------------------------- */ + } -static const struct pass_data afl_pass_data = { + if (line.find(":") != std::string::npos) { - .type = GIMPLE_PASS, - .name = "afl-inst", - .optinfo_flags = OPTGROUP_NONE, + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - .tv_id = TV_NONE, - .properties_required = 0, - .properties_provided = 0, - .properties_destroyed = 0, - .todo_flags_start = 0, - // NOTE(aseipp): it's very, very important to include - // at least 'TODO_update_ssa' here so that GCC will - // properly update the resulting SSA form, e.g., to - // include new PHI nodes for newly added symbols or - // names. Do not remove this. Do not taunt Happy Fun - // Ball. - .todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg, + } -}; + if (line.length() > 0) { -namespace { + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function -class afl_pass : public gimple_opt_pass { + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); - private: - bool do_ext_call; + } - public: - afl_pass(bool ext_call, gcc::context *g) - : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) { + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } - unsigned int execute(function *fun) override { + std::string getSourceName(function *F) { + + return DECL_SOURCE_FILE(F->decl); + + } - if (!myInstrumentList.empty()) { + bool isInInstrumentList(function *F) { - bool instrumentBlock = false; - std::string instFilename; - unsigned int instLine = 0; + bool return_default = true; - /* EXPR_FILENAME - This macro returns the name of the file in which the entity was declared, - as a char*. For an entity declared implicitly by the compiler (like - __builtin_ memcpy), this will be the string "". - */ - const char *fname = DECL_SOURCE_FILE(fun->decl); + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (isIgnoreFunction(F)) return false; - if (0 != strncmp("", fname, 10) && - 0 != strncmp("", fname, 10)) { + if (!denyListFiles.empty() || !denyListFunctions.empty()) { - instFilename = fname; - instLine = DECL_SOURCE_LINE(fun->decl); + if (!denyListFunctions.empty()) { - /* Continue only if we know where we actually are */ - if (!instFilename.empty()) { + std::string instFunction = IDENTIFIER_POINTER (DECL_NAME (F->decl)); - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.length() >= it->length()) { + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFilename.compare(instFilename.length() - it->length(), - it->length(), *it) == 0) { + if (instFunction.length() >= it->length()) { - instrumentBlock = true; - break; + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - } + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; - } + } - } + } - } + } } - /* Either we couldn't figure out our location or the location is - * not in the instrument list, so we skip instrumentation. */ - if (!instrumentBlock) { + if (!denyListFiles.empty()) { - if (!be_quiet) { + std::string source_file = getSourceName(F); - if (!instFilename.empty()) - SAYF(cYEL "[!] " cBRI - "Not in instrument list, skipping %s line %u...\n", - instFilename.c_str(), instLine); - else - SAYF(cYEL "[!] " cBRI "No filename information found, skipping it"); + if (!source_file.empty()) { - } + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - return 0; + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun); + return false; - } + } -}; /* class afl_pass */ + } -} // namespace + } -static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) { + } else { - return new afl_pass(ext_call, ctxt); + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); -} + } -/* -------------------------------------------------------------------------- */ -/* -- Initialization -------------------------------------------------------- */ + } -int plugin_is_GPL_compatible = 1; + } -static struct plugin_info afl_plugin_info = { + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - .version = "20200519", - .help = "AFL++ gcc plugin\n", + return_default = false; -}; + if (!allowListFunctions.empty()) { -int plugin_init(struct plugin_name_args * plugin_info, - struct plugin_gcc_version *version) { + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - struct register_pass_info afl_pass_info; - struct timeval tv; - struct timezone tz; - u32 rand_seed; + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { - /* Setup random() so we get Actually Random(TM) outputs from R() */ - gettimeofday(&tv, &tz); - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - SR(rand_seed); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - /* Pass information */ - afl_pass_info.pass = make_afl_pass(inst_ext, g); - afl_pass_info.reference_pass_name = "ssa"; - afl_pass_info.ref_pass_instance_number = 1; - afl_pass_info.pos_op = PASS_POS_INSERT_AFTER; + if (instFunction.length() >= it->length()) { - if (!plugin_default_version_check(version, &gcc_version)) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; - } + } - /* Show a banner */ - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + } - SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST - " initially by , maintainer: hexcoder-\n")); + } - } else + } - be_quiet = 1; + if (!allowListFiles.empty()) { - /* Decide instrumentation ratio */ - char *inst_ratio_str = getenv("AFL_INST_RATIO"); + std::string source_file = getSourceName(F); - if (inst_ratio_str) { + if (!source_file.empty()) { - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); - else { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - if (!be_quiet) - ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!instInstrumentListFilename) - instInstrumentListFilename = getenv("AFL_GCC_WHITELIST"); - if (instInstrumentListFilename) { + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER (DECL_NAME (F->decl)), + source_file.c_str()); + return true; - std::string line; - std::ifstream fileStream; - fileStream.open(instInstrumentListFilename); - if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { + } - myInstrumentList.push_back(line); - getline(fileStream, line); + } - } + } - } else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") || + } else { - getenv("AFL_LLVM_INSTRUMENT_FILE"))) { + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); + return false; - SAYF(cYEL "[-] " cRST - "AFL_LLVM_INSTRUMENT_FILE environment variable detected - did " - "you mean AFL_GCC_INSTRUMENT_FILE?\n"); + } + + } + + } + + return return_default; } - /* Go go gadget */ - register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, - &afl_plugin_info); - register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, - &afl_pass_info); - return 0; + +}; + +static struct plugin_info afl_plugin = + { + .version = "20200907", + .help = G_("AFL gcc plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +\n\ +Set AFL_INST_RATIO in the environment to a number from 0 to 100\n\ +to control how likely a block will be chosen for instrumentation.\n\ +\n\ +Specify -frandom-seed for reproducible instrumentation.\n\ +"), + }; } +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int +plugin_init (struct plugin_name_args *info, + struct plugin_gcc_version *version) +{ + if (!plugin_default_version_check (version, &gcc_version)) + FATAL (G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + + /* Show a banner. */ + bool quiet = false; + if (isatty (2) && !getenv ("AFL_QUIET")) + SAYF (cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); + else + quiet = true; + + /* Decide instrumentation ratio. */ + int inst_ratio = 100; + if (char *inst_ratio_str = getenv ("AFL_INST_RATIO")) + if (sscanf (inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL (G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + + /* Initialize the random number generator with GCC's random seed, in + case it was specified in the command line's -frandom-seed for + reproducible instrumentation. */ + srandom (get_random_seed (false)); + + const char *name = info->base_name; + register_callback (name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass *aflp = new afl_pass (quiet, inst_ratio); + struct register_pass_info pass_info = + { + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + }; + register_callback (name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback (name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); + + if (!quiet) + ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + + return 0; +} diff --git a/src/afl-cc.c b/src/afl-cc.c index ddda3845..78245d4b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1335,6 +1335,12 @@ int main(int argc, char **argv, char **envp) { AFL_REAL_LD, AFL_CLANG_FLTO); #endif + 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"); } SAYF( diff --git a/test/test-performance.sh b/test/test-performance.sh index cee46060..cd6eea64 100755 --- a/test/test-performance.sh +++ b/test/test-performance.sh @@ -117,6 +117,30 @@ test -e ../afl-clang-fast -a -e ../afl-fuzz && { } || $ECHO "$RED[!] llvm_mode instrumentation failed" } || $ECHO "$YELLOW[-] llvm_mode is not compiled, cannot test" +$ECHO "$BLUE[*] Testing: gcc_plugin" +GCCP=x +test -e ../afl-gcc-fast -a -e ../afl-fuzz && { + ../afl-gcc-fast -o test-instr.gccp ../test-instr.c > /dev/null 2>&1 + test -e test-instr.gccp && { + $ECHO "$GREEN[+] gcc_plugin compilation succeeded" + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for gcc_plugin for 30 seconds" + { + ../afl-fuzz -V 30 -s 123 -m ${MEM_LIMIT} -i in -o out-gccp -- ./test-instr.gccp + } >>errors 2>&1 + test -n "$( ls out-gccp/queue/id:000002* 2> /dev/null )" && { + GCCP=`grep execs_done out-gccp/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT---------------------------------------------------------------- + cat errors + echo CUT---------------------------------------------------------------- + $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" + } + rm -rf in out-gccp errors test-instr.gccp + } || $ECHO "$RED[!] gcc_plugin instrumentation failed" +} || $ECHO "$YELLOW[-] gcc_plugin is not compiled, cannot test" + $ECHO "$BLUE[*] Testing: qemu_mode" QEMU=x test -e ../afl-qemu-trace -a -e ../afl-fuzz && { @@ -147,6 +171,9 @@ LAST_GCC= LOW_LLVM= HIGH_LLVM= LAST_LLVM= +LOW_GCCP= +HIGH_GCCP= +LAST_GCCP= LOW_QEMU= HIGH_QEMU= LAST_QEMU= @@ -155,12 +182,15 @@ test -s $FILE && { while read LINE; do G=`echo $LINE | awk '{print$1}'` L=`echo $LINE | awk '{print$2}'` - Q=`echo $LINE | awk '{print$3}'` + P=`echo $LINE | awk '{print$3}'` + Q=`echo $LINE | awk '{print$4}'` test "$G" = x && G= test "$L" = x && L= + test "$P" = x && P= test "$Q" = x && Q= test -n "$G" && LAST_GCC=$G test -n "$L" && LAST_LLVM=$L + test -n "$P" && LAST_GCCP=$P test -n "$Q" && LAST_QEMU=$Q test -n "$G" -a -z "$LOW_GCC" && LOW_GCC=$G || { test -n "$G" -a "$G" -lt "$LOW_GCC" 2> /dev/null && LOW_GCC=$G @@ -168,6 +198,9 @@ test -s $FILE && { test -n "$L" -a -z "$LOW_LLVM" && LOW_LLVM=$L || { test -n "$L" -a "$L" -lt "$LOW_LLVM" 2> /dev/null && LOW_LLVM=$L } + test -n "$P" -a -z "$LOW_GCCP" && LOW_GCCP=$P || { + test -n "$P" -a "$P" -lt "$LOW_GCCP" 2> /dev/null && LOW_GCCP=$P + } test -n "$Q" -a -z "$LOW_QEMU" && LOW_QEMU=$Q || { test -n "$Q" -a "$Q" -lt "$LOW_QEMU" 2> /dev/null && LOW_QEMU=$Q } @@ -177,6 +210,9 @@ test -s $FILE && { test -n "$L" -a -z "$HIGH_LLVM" && HIGH_LLVM=$L || { test -n "$L" -a "$L" -gt "$HIGH_LLVM" 2> /dev/null && HIGH_LLVM=$L } + test -n "$P" -a -z "$HIGH_GCCP" && HIGH_GCCP=$P || { + test -n "$P" -a "$P" -gt "$HIGH_GCCP" 2> /dev/null && HIGH_GCCP=$P + } test -n "$Q" -a -z "$HIGH_QEMU" && HIGH_QEMU=$Q || { test -n "$Q" -a "$Q" -gt "$HIGH_QEMU" 2> /dev/null && HIGH_QEMU=$Q } @@ -184,11 +220,12 @@ test -s $FILE && { $ECHO "$YELLOW[!] Reading saved data from $FILE completed, please compare the results:" $ECHO "$BLUE[!] afl-cc: lowest=$LOW_GCC highest=$HIGH_GCC last=$LAST_GCC current=$GCC" $ECHO "$BLUE[!] llvm_mode: lowest=$LOW_LLVM highest=$HIGH_LLVM last=$LAST_LLVM current=$LLVM" + $ECHO "$BLUE[!] gcc_plugin: lowest=$LOW_GCCP highest=$HIGH_GCCP last=$LAST_GCCP current=$GCCP" $ECHO "$BLUE[!] qemu_mode: lowest=$LOW_QEMU highest=$HIGH_QEMU last=$LAST_QEMU current=$QEMU" } || { $ECHO "$YELLOW[!] First run, just saving data" - $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM qemu_mode=$QEMU" + $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM gcc_plugin=$GCCP qemu_mode=$QEMU" } -echo "$GCC $LLVM $QEMU" >> $FILE +echo "$GCC $LLVM $GCCP $QEMU" >> $FILE $ECHO "$GREY[*] done." $ECHO "$RESET" -- cgit 1.4.1 From c091340a85694c5de1125a93366f2733959487f5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 16:15:31 +0200 Subject: new gcc_plugin integration --- GNUmakefile.llvm | 2 +- README.md | 2 ++ docs/Changelog.md | 2 ++ docs/INSTALL.md | 2 +- src/afl-cc.c | 76 +++++++++++++++++++++++++++---------------------------- 5 files changed, 44 insertions(+), 40 deletions(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 3eefdf90..604fb291 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -229,7 +229,7 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ./include/ -I ./instrumentation -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ - -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ + -Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ diff --git a/README.md b/README.md index c886489d..fb59835c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ behaviours: only one compiler: afl-cc. All previous compilers now symlink to this one compiler. All instrumentation source code is now in the `instrumentation/` folder. + * The gcc_plugin was replaced with a new version submitted by AdaCore, that + supports more features, thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current ninja build tool version and python3 setuptools are required. qemu_mode also got new options like snapshotting, instrumenting specific diff --git a/docs/Changelog.md b/docs/Changelog.md index 73613452..c42ab629 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -25,6 +25,8 @@ sending a mail to . skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule - instrumentation + - We received an enhanced gcc_plugin module from AdaCore, thank you + very much!! - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 93a46caf..8e1e266f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,7 +28,7 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. -Likewise, if you are using GCC, please review gcc_plugin/README.md. +Likewise, if you are using GCC, please review instrumentation/README.gcc_plugin.md. You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but diff --git a/src/afl-cc.c b/src/afl-cc.c index 78245d4b..47a33cd0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1196,7 +1196,7 @@ int main(int argc, char **argv, char **envp) { " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " - " simple\n" + " yes\n" " [GCC] simple gcc: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", @@ -1270,8 +1270,29 @@ int main(int argc, char **argv, char **envp) { " 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_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\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 (have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" #if LLVM_MAJOR < 9 " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else @@ -1288,25 +1309,13 @@ int main(int argc, char **argv, char **envp) { "functions\n" " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " "instrument allow/\n" - " deny listing (selective instrumentation)\n" - " AFL_NO_BUILTIN: no builtins for string compare functions (for " - "libtokencap.so)\n" - " AFL_PATH: path to instrumenting pass and runtime " - "(afl-compiler-rt.*o)\n" - " AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function " - "(LTO only)\n" - " AFL_QUIET: suppress verbose output\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"); + " deny listing (selective instrumentation)\n"); + if (have_llvm) SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - " AFL_LLVM_INSTRUMENT: set instrumentation mode: CLASSIC, INSTRIM, " - "PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-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" @@ -1315,36 +1324,27 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " "INSTRIM)\n" " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC and INSTRIM)\n"); + "CLASSIC & INSTRIM)\n"); #ifdef AFL_CLANG_FLTO - SAYF( + 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. " + " 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 they are in into this file\n" - "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + " 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 " + " 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" - "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " - "\"%s\"\n" - "If anything fails - be sure to read README.lto.md!\n", - AFL_REAL_LD, AFL_CLANG_FLTO); + " 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( - "\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"); } SAYF( - "For any information on the available instrumentations and options " + "\nFor any information on the available instrumentations and options " "please \n" "consult the README.md, especially section 3.1 about instrumenting " "targets.\n\n"); -- cgit 1.4.1 From ab744abc4b3c90bee355807e7b6e40ba86f23e74 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 17:54:01 +0200 Subject: code-format --- instrumentation/afl-compiler-rt.o.c | 12 +- instrumentation/afl-gcc-pass.so.cc | 888 ++++++++++++++++++------------------ src/afl-cc.c | 105 +++-- src/afl-forkserver.c | 6 +- 4 files changed, 505 insertions(+), 506 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 05e2d50d..0e8b97a2 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -38,8 +38,8 @@ #include #include -#if ! __GNUC__ -#include "llvm/Config/llvm-config.h" +#if !__GNUC__ + #include "llvm/Config/llvm-config.h" #endif #ifdef __linux__ @@ -117,12 +117,12 @@ void __afl_trace(const u32 x) { u8 *p = &__afl_area_ptr[prev ^ x]; #if 1 /* enable for neverZero feature. */ -# if __GNUC__ - u8 c = __builtin_add_overflow (*p, 1, p); + #if __GNUC__ + u8 c = __builtin_add_overflow(*p, 1, p); *p += c; -# else + #else *p += 1 + ((u8)(1 + *p == 0); -# endif + #endif #else ++*p; #endif diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index 6d6f4636..f8d5fd9e 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -132,10 +132,10 @@ #include #ifdef likely -# undef likely + #undef likely #endif #ifdef unlikely -# undef unlikely + #undef unlikely #endif #include @@ -166,8 +166,8 @@ int plugin_is_GPL_compatible = 1; namespace { -static const struct pass_data afl_pass_data = - { +static const struct pass_data afl_pass_data = { + .type = GIMPLE_PASS, .name = "afl", .optinfo_flags = OPTGROUP_NONE, @@ -176,26 +176,27 @@ static const struct pass_data afl_pass_data = .properties_provided = 0, .properties_destroyed = 0, .todo_flags_start = 0, - .todo_flags_finish = (TODO_update_ssa - | TODO_cleanup_cfg - | TODO_verify_il), - }; + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il), + +}; struct afl_pass : gimple_opt_pass { - afl_pass (bool quiet, unsigned int ratio) - : gimple_opt_pass (afl_pass_data, g), - be_quiet (quiet), - debug (!!getenv ("AFL_DEBUG")), - inst_ratio (ratio), + + afl_pass(bool quiet, unsigned int ratio) + : gimple_opt_pass(afl_pass_data, g), + be_quiet(quiet), + debug(!!getenv("AFL_DEBUG")), + inst_ratio(ratio), #ifdef AFL_GCC_OUT_OF_LINE - out_of_line (!!(AFL_GCC_OUT_OF_LINE)), + out_of_line(!!(AFL_GCC_OUT_OF_LINE)), #else - out_of_line (getenv ("AFL_GCC_OUT_OF_LINE")), + out_of_line(getenv("AFL_GCC_OUT_OF_LINE")), #endif - neverZero (!getenv ("AFL_GCC_SKIP_NEVERZERO")), - inst_blocks (0) - { - initInstrumentList (); + neverZero(!getenv("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks(0) { + + initInstrumentList(); + } /* Are we outputting to a non-terminal, or running with AFL_QUIET @@ -218,252 +219,240 @@ struct afl_pass : gimple_opt_pass { /* Count instrumented blocks. */ int inst_blocks; - virtual unsigned int - execute (function *fn) - { - if (!isInInstrumentList(fn)) - return 0; + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; int blocks = 0; /* These are temporaries used by inline instrumentation only, that are live throughout the function. */ - tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, - ntry = NULL, cntr = NULL, xaddc = NULL, xincr = NULL; + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, ntry = NULL, + cntr = NULL, xaddc = NULL, xincr = NULL; basic_block bb; - FOR_EACH_BB_FN (bb, fn) - { - if (!instrument_block_p (bb)) - continue; - - /* Generate the block identifier. */ - unsigned bid = R (MAP_SIZE); - tree bidt = build_int_cst (sizetype, bid); - - gimple_seq seq = NULL; - - if (out_of_line) - { - static tree afl_trace = get_afl_trace_decl (); - - /* Call __afl_trace with bid, the new location; */ - gcall *call = gimple_build_call (afl_trace, 1, bidt); - gimple_seq_add_stmt (&seq, call); - } - else - { - static tree afl_prev_loc = get_afl_prev_loc_decl (); - static tree afl_area_ptr = get_afl_area_ptr_decl (); - - /* Load __afl_prev_loc to a temporary ploc. */ - if (blocks == 0) - ploc = create_tmp_var (TREE_TYPE (afl_prev_loc), ".afl_prev_loc"); - gimple *load_loc = gimple_build_assign (ploc, afl_prev_loc); - gimple_seq_add_stmt (&seq, load_loc); - - /* Compute the index into the map referenced by area_ptr - that we're to update: indx = (sizetype) ploc ^ bid. */ - if (blocks == 0) - indx = create_tmp_var (TREE_TYPE (bidt), ".afl_index"); - gimple *conv_ploc - = gimple_build_assign (indx, - fold_convert (TREE_TYPE (indx), - ploc)); - gimple_seq_add_stmt (&seq, conv_ploc); - gimple *xor_loc = gimple_build_assign (indx, BIT_XOR_EXPR, - indx, bidt); - gimple_seq_add_stmt (&seq, xor_loc); - - /* Compute the address of that map element. */ - if (blocks == 0) - { - map = afl_area_ptr; - map_ptr = create_tmp_var (TREE_TYPE (afl_area_ptr), - ".afl_map_ptr"); - ntry = create_tmp_var (TREE_TYPE (afl_area_ptr), - ".afl_map_entry"); - } - gimple *idx_map = gimple_build_assign (ntry, POINTER_PLUS_EXPR, - map_ptr, indx); - gimple_seq_add_stmt (&seq, idx_map); - - /* Increment the counter in idx_map. */ - tree memref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ntry)), - ntry, build_zero_cst (TREE_TYPE (ntry))); - if (blocks == 0) - cntr = create_tmp_var (TREE_TYPE (memref), ".afl_edge_count"); - - gimple *load_cntr = gimple_build_assign (cntr, memref); - gimple_seq_add_stmt (&seq, load_cntr); - - tree cntrb = cntr; - tree incrv = build_one_cst (TREE_TYPE (cntr)); - - if (neverZero) - { - /* NeverZero: if count wrapped around to zero, advance to - one. */ - if (blocks == 0) - { - xaddc = create_tmp_var (build_complex_type - (TREE_TYPE (memref)), - ".afl_edge_xaddc"); - xincr = create_tmp_var (TREE_TYPE (memref), - ".afl_edge_xincr"); - } - - auto_vec vargs (2); - vargs.quick_push (cntr); - vargs.quick_push (incrv); - gcall *add1_cntr - = gimple_build_call_internal_vec (IFN_ADD_OVERFLOW, vargs); - gimple_call_set_lhs (add1_cntr, xaddc); - gimple_seq_add_stmt (&seq, add1_cntr); - - cntrb = build1 (REALPART_EXPR, TREE_TYPE (cntr), xaddc); - incrv = build1 (IMAGPART_EXPR, TREE_TYPE (xincr), xaddc); - } - - gimple *incr_cntr = gimple_build_assign (cntr, PLUS_EXPR, - cntrb, incrv); - gimple_seq_add_stmt (&seq, incr_cntr); - - gimple *store_cntr = gimple_build_assign (unshare_expr (memref), - cntr); - gimple_seq_add_stmt (&seq, store_cntr); - - /* Store bid >> 1 in __afl_prev_loc. */ - gimple *shift_loc = gimple_build_assign (ploc, - build_int_cst - (TREE_TYPE (ploc), - bid >> 1)); - gimple_seq_add_stmt (&seq, shift_loc); - gimple *store_loc = gimple_build_assign (afl_prev_loc, ploc); - gimple_seq_add_stmt (&seq, store_loc); - } - - /* Insert the generated sequence. */ - gimple_stmt_iterator insp = gsi_after_labels (bb); - gsi_insert_seq_before (&insp, seq, GSI_SAME_STMT); - - /* Bump this function's instrumented block counter. */ - blocks++; + FOR_EACH_BB_FN(bb, fn) { + + if (!instrument_block_p(bb)) continue; + + /* Generate the block identifier. */ + unsigned bid = R(MAP_SIZE); + tree bidt = build_int_cst(sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) { + + static tree afl_trace = get_afl_trace_decl(); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call(afl_trace, 1, bidt); + gimple_seq_add_stmt(&seq, call); + + } else { + + static tree afl_prev_loc = get_afl_prev_loc_decl(); + static tree afl_area_ptr = get_afl_area_ptr_decl(); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var(TREE_TYPE(afl_prev_loc), ".afl_prev_loc"); + gimple *load_loc = gimple_build_assign(ploc, afl_prev_loc); + gimple_seq_add_stmt(&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) indx = create_tmp_var(TREE_TYPE(bidt), ".afl_index"); + gimple *conv_ploc = + gimple_build_assign(indx, fold_convert(TREE_TYPE(indx), ploc)); + gimple_seq_add_stmt(&seq, conv_ploc); + gimple *xor_loc = gimple_build_assign(indx, BIT_XOR_EXPR, indx, bidt); + gimple_seq_add_stmt(&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) { + + map = afl_area_ptr; + map_ptr = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_ptr"); + ntry = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_entry"); + + } + + gimple *idx_map = + gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); + gimple_seq_add_stmt(&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + gimple *load_cntr = gimple_build_assign(cntr, memref); + gimple_seq_add_stmt(&seq, load_cntr); + + tree cntrb = cntr; + tree incrv = build_one_cst(TREE_TYPE(cntr)); + + if (neverZero) { + + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) { + + xaddc = create_tmp_var(build_complex_type(TREE_TYPE(memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_xincr"); + + } + + auto_vec vargs(2); + vargs.quick_push(cntr); + vargs.quick_push(incrv); + gcall *add1_cntr = + gimple_build_call_internal_vec(IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs(add1_cntr, xaddc); + gimple_seq_add_stmt(&seq, add1_cntr); + + cntrb = build1(REALPART_EXPR, TREE_TYPE(cntr), xaddc); + incrv = build1(IMAGPART_EXPR, TREE_TYPE(xincr), xaddc); + + } + + gimple *incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntrb, incrv); + gimple_seq_add_stmt(&seq, incr_cntr); + + gimple *store_cntr = gimple_build_assign(unshare_expr(memref), cntr); + gimple_seq_add_stmt(&seq, store_cntr); + + /* Store bid >> 1 in __afl_prev_loc. */ + gimple *shift_loc = + gimple_build_assign(ploc, build_int_cst(TREE_TYPE(ploc), bid >> 1)); + gimple_seq_add_stmt(&seq, shift_loc); + gimple *store_loc = gimple_build_assign(afl_prev_loc, ploc); + gimple_seq_add_stmt(&seq, store_loc); + } + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels(bb); + gsi_insert_seq_before(&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + + } + /* Aggregate the instrumented block count. */ inst_blocks += blocks; - if (blocks) - { - if (out_of_line) - return TODO_rebuild_cgraph_edges; + if (blocks) { - gimple_seq seq = NULL; + if (out_of_line) return TODO_rebuild_cgraph_edges; - /* Load afl_area_ptr into map_ptr. We want to do this only - once per function. */ - gimple *load_ptr = gimple_build_assign (map_ptr, map); - gimple_seq_add_stmt (&seq, load_ptr); + gimple_seq seq = NULL; - /* Insert it in the edge to the entry block. We don't want to - insert it in the first block, since there might be a loop - or a goto back to it. Insert in the edge, which may create - another block. */ - edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn)); - gsi_insert_seq_on_edge_immediate (e, seq); - } + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + gimple *load_ptr = gimple_build_assign(map_ptr, map); + gimple_seq_add_stmt(&seq, load_ptr); + + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(fn)); + gsi_insert_seq_on_edge_immediate(e, seq); + + } return 0; + } /* Decide whether to instrument block BB. Skip it due to the random distribution, or if it's the single successor of all its predecessors. */ - inline bool - instrument_block_p (basic_block bb) - { - if (R (100) >= inst_ratio) - return false; + inline bool instrument_block_p(basic_block bb) { - edge e; edge_iterator ei; - FOR_EACH_EDGE (e, ei, bb->preds) - if (!single_succ_p (e->src)) - return true; + if (R(100) >= inst_ratio) return false; + + edge e; + edge_iterator ei; + FOR_EACH_EDGE(e, ei, bb->preds) + if (!single_succ_p(e->src)) return true; return false; + } /* Create and return a declaration for the __afl_trace rt function. */ - static inline tree - get_afl_trace_decl () - { - tree type = build_function_type_list (void_type_node, - uint16_type_node, - NULL_TREE); - tree decl = build_fn_decl ("__afl_trace", type); - - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; + static inline tree get_afl_trace_decl() { + + tree type = + build_function_type_list(void_type_node, uint16_type_node, NULL_TREE); + tree decl = build_fn_decl("__afl_trace", type); + + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; return decl; + } /* Create and return a declaration for the __afl_prev_loc thread-local variable. */ - static inline tree - get_afl_prev_loc_decl () - { - tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, - get_identifier ("__afl_prev_loc"), - uint32_type_node); - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - TREE_STATIC (decl) = 1; - set_decl_tls_model (decl, - (flag_pic - ? TLS_MODEL_INITIAL_EXEC - : TLS_MODEL_LOCAL_EXEC)); + static inline tree get_afl_prev_loc_decl() { + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_prev_loc"), uint32_type_node); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; + set_decl_tls_model( + decl, (flag_pic ? TLS_MODEL_INITIAL_EXEC : TLS_MODEL_LOCAL_EXEC)); return decl; + } /* Create and return a declaration for the __afl_prev_loc thread-local variable. */ - static inline tree - get_afl_area_ptr_decl () - { - tree type = build_pointer_type (unsigned_char_type_node); - tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, - get_identifier ("__afl_area_ptr"), - type); - TREE_PUBLIC (decl) = 1; - DECL_EXTERNAL (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - TREE_STATIC (decl) = 1; + static inline tree get_afl_area_ptr_decl() { + + tree type = build_pointer_type(unsigned_char_type_node); + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_area_ptr"), type); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; return decl; + } /* This is registered as a plugin finalize callback, to print an instrumentation summary unless in quiet mode. */ - static void - plugin_finalize (void *, void *p) - { + static void plugin_finalize(void *, void *p) { + opt_pass *op = (opt_pass *)p; afl_pass &self = (afl_pass &)*op; if (!self.be_quiet) { + if (!self.inst_blocks) - WARNF ("No instrumentation targets found."); + WARNF("No instrumentation targets found."); else - OKF ("Instrumented %u locations (%s mode, %s, ratio %u%%).", - self.inst_blocks, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), - self.out_of_line ? G_("out of line") : G_("inline"), - self.inst_ratio); + OKF("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); + } + } #define report_fatal_error(msg) BADF(msg) @@ -481,36 +470,36 @@ struct afl_pass : gimple_opt_pass { static const char *ignoreList[] = { - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzer", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" }; - const char *name = IDENTIFIER_POINTER (DECL_NAME (F->decl)); - int len = IDENTIFIER_LENGTH (DECL_NAME (F->decl)); + const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); for (auto const &ignoreListFunc : ignoreList) { - if (strncmp (name, ignoreListFunc, len) == 0) { return true; } + if (strncmp(name, ignoreListFunc, len) == 0) { return true; } } @@ -533,8 +522,8 @@ struct afl_pass : gimple_opt_pass { if (allowlist && denylist) FATAL( - "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " - "but not both!"); + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); if (allowlist) { @@ -546,68 +535,68 @@ struct afl_pass : gimple_opt_pass { while (fileStream) { - int is_file = -1; - std::size_t npos; - std::string original_line = line; + int is_file = -1; + std::size_t npos; + std::string original_line = line; - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - if (line.compare(0, 4, "fun:") == 0) { + if (line.compare(0, 4, "fun:") == 0) { - is_file = 0; - line = line.substr(4); + is_file = 0; + line = line.substr(4); - } else if (line.compare(0, 9, "function:") == 0) { + } else if (line.compare(0, 9, "function:") == 0) { - is_file = 0; - line = line.substr(9); + is_file = 0; + line = line.substr(9); - } else if (line.compare(0, 4, "src:") == 0) { + } else if (line.compare(0, 4, "src:") == 0) { - is_file = 1; - line = line.substr(4); + is_file = 1; + line = line.substr(4); - } else if (line.compare(0, 7, "source:") == 0) { + } else if (line.compare(0, 7, "source:") == 0) { - is_file = 1; - line = line.substr(7); + is_file = 1; + line = line.substr(7); - } + } - if (line.find(":") != std::string::npos) { + if (line.find(":") != std::string::npos) { - FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); - } + } - if (line.length() > 0) { + if (line.length() > 0) { - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function - if (is_file == 1) - allowListFiles.push_back(line); - else - allowListFunctions.push_back(line); - getline(fileStream, line); + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); - } + } } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -621,68 +610,68 @@ struct afl_pass : gimple_opt_pass { while (fileStream) { - int is_file = -1; - std::size_t npos; - std::string original_line = line; + int is_file = -1; + std::size_t npos; + std::string original_line = line; - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - if (line.compare(0, 4, "fun:") == 0) { + if (line.compare(0, 4, "fun:") == 0) { - is_file = 0; - line = line.substr(4); + is_file = 0; + line = line.substr(4); - } else if (line.compare(0, 9, "function:") == 0) { + } else if (line.compare(0, 9, "function:") == 0) { - is_file = 0; - line = line.substr(9); + is_file = 0; + line = line.substr(9); - } else if (line.compare(0, 4, "src:") == 0) { + } else if (line.compare(0, 4, "src:") == 0) { - is_file = 1; - line = line.substr(4); + is_file = 1; + line = line.substr(4); - } else if (line.compare(0, 7, "source:") == 0) { + } else if (line.compare(0, 7, "source:") == 0) { - is_file = 1; - line = line.substr(7); + is_file = 1; + line = line.substr(7); - } + } - if (line.find(":") != std::string::npos) { + if (line.find(":") != std::string::npos) { - FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - } + } - if (line.length() > 0) { + if (line.length() > 0) { - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function - if (is_file == 1) - denyListFiles.push_back(line); - else - denyListFunctions.push_back(line); - getline(fileStream, line); + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); - } + } } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -707,74 +696,74 @@ struct afl_pass : gimple_opt_pass { if (!denyListFunctions.empty()) { - std::string instFunction = IDENTIFIER_POINTER (DECL_NAME (F->decl)); + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - for (std::list::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFunction.length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); - return false; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; - } + } - } + } - } + } } if (!denyListFiles.empty()) { - std::string source_file = getSourceName(F); + std::string source_file = getSourceName(F); - if (!source_file.empty()) { + if (!source_file.empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (source_file.length() >= it->length()) { + if (source_file.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - return false; + return false; - } + } - } + } - } + } - } else { + } else { - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER (DECL_NAME (F->decl))); + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); - } + } } @@ -787,81 +776,81 @@ struct afl_pass : gimple_opt_pass { if (!allowListFunctions.empty()) { - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - for (std::list::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFunction.length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); - return true; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; - } + } - } + } - } + } } if (!allowListFiles.empty()) { - std::string source_file = getSourceName(F); + std::string source_file = getSourceName(F); - if (!source_file.empty()) { + if (!source_file.empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (source_file.length() >= it->length()) { + if (source_file.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - IDENTIFIER_POINTER (DECL_NAME (F->decl)), - source_file.c_str()); - return true; + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); + return true; - } + } - } + } - } + } - } else { + } else { - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER (DECL_NAME (F->decl))); - return false; + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + return false; - } + } } @@ -871,13 +860,12 @@ struct afl_pass : gimple_opt_pass { } - }; -static struct plugin_info afl_plugin = - { - .version = "20200907", - .help = G_("AFL gcc plugin\n\ +static struct plugin_info afl_plugin = { + + .version = "20200907", + .help = G_("AFL gcc plugin\n\ \n\ Set AFL_QUIET in the environment to silence it.\n\ \n\ @@ -886,58 +874,62 @@ to control how likely a block will be chosen for instrumentation.\n\ \n\ Specify -frandom-seed for reproducible instrumentation.\n\ "), - }; -} +}; + +} // namespace /* This is the function GCC calls when loading a plugin. Initialize and register further callbacks. */ -int -plugin_init (struct plugin_name_args *info, - struct plugin_gcc_version *version) -{ - if (!plugin_default_version_check (version, &gcc_version)) - FATAL (G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); /* Show a banner. */ bool quiet = false; - if (isatty (2) && !getenv ("AFL_QUIET")) - SAYF (cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); else quiet = true; /* Decide instrumentation ratio. */ int inst_ratio = 100; - if (char *inst_ratio_str = getenv ("AFL_INST_RATIO")) - if (sscanf (inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL (G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + if (char *inst_ratio_str = getenv("AFL_INST_RATIO")) + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); /* Initialize the random number generator with GCC's random seed, in case it was specified in the command line's -frandom-seed for reproducible instrumentation. */ - srandom (get_random_seed (false)); + srandom(get_random_seed(false)); const char *name = info->base_name; - register_callback (name, PLUGIN_INFO, NULL, &afl_plugin); - - afl_pass *aflp = new afl_pass (quiet, inst_ratio); - struct register_pass_info pass_info = - { - .pass = aflp, - .reference_pass_name = "ssa", - .ref_pass_instance_number = 1, - .pos_op = PASS_POS_INSERT_AFTER, - }; - register_callback (name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); - register_callback (name, PLUGIN_FINISH, afl_pass::plugin_finalize, - pass_info.pass); + register_callback(name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass * aflp = new afl_pass(quiet, inst_ratio); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback(name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); if (!quiet) ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); return 0; + } + diff --git a/src/afl-cc.c b/src/afl-cc.c index 47a33cd0..6bee8b38 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1282,65 +1282,72 @@ int main(int argc, char **argv, char **envp) { " 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 (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 (have_llvm) SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" #if LLVM_MAJOR < 9 - " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else - " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" #endif - " 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_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " - "instrument allow/\n" - " deny listing (selective instrumentation)\n"); + " 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_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: 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, CTX, NGRAM-2 ... NGRAM-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_CTX: use context sensitive coverage (for CLASSIC and " - "INSTRIM)\n" - " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC & INSTRIM)\n"); + SAYF( + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " + "mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-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_CTX: use context sensitive coverage (for CLASSIC and " + "INSTRIM)\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"); + 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( diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index c8056b9e..33b16817 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1043,11 +1043,11 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - if (fsrv->child_pid <= 0) { - + if (fsrv->child_pid <= 0) { + if (*stop_soon_p) { return 0; } FATAL("Fork server is misbehaving (OOM?)"); - + } exec_ms = read_s32_timed(fsrv->fsrv_st_fd, &fsrv->child_status, timeout, -- cgit 1.4.1 From 380051868a7531830d94d312f0f11b0e19e3284f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Sep 2020 15:26:46 +0200 Subject: add libfuzzer custom mutator, minor enhancements and fixes --- GNUmakefile | 4 +- README.md | 5 + custom_mutators/README.md | 4 +- custom_mutators/grammar_mutator/README.md | 6 + .../grammar_mutator/build_grammar_mutator.sh | 17 + custom_mutators/honggfuzz/Makefile | 6 +- custom_mutators/honggfuzz/README.md | 4 +- custom_mutators/libfuzzer/FuzzerBuiltins.h | 35 + custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h | 72 ++ custom_mutators/libfuzzer/FuzzerCommand.h | 178 ++++ custom_mutators/libfuzzer/FuzzerCorpus.h | 581 ++++++++++ custom_mutators/libfuzzer/FuzzerCrossOver.cpp | 60 ++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp | 344 ++++++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.h | 135 +++ custom_mutators/libfuzzer/FuzzerDefs.h | 75 ++ custom_mutators/libfuzzer/FuzzerDictionary.h | 118 ++ custom_mutators/libfuzzer/FuzzerDriver.cpp | 1122 ++++++++++++++++++++ custom_mutators/libfuzzer/FuzzerExtFunctions.def | 50 + custom_mutators/libfuzzer/FuzzerExtFunctions.h | 34 + .../libfuzzer/FuzzerExtFunctionsDlsym.cpp | 60 ++ .../libfuzzer/FuzzerExtFunctionsWeak.cpp | 63 ++ .../libfuzzer/FuzzerExtFunctionsWindows.cpp | 95 ++ custom_mutators/libfuzzer/FuzzerExtraCounters.cpp | 71 ++ custom_mutators/libfuzzer/FuzzerFlags.def | 197 ++++ custom_mutators/libfuzzer/FuzzerFork.cpp | 501 +++++++++ custom_mutators/libfuzzer/FuzzerFork.h | 24 + custom_mutators/libfuzzer/FuzzerIO.cpp | 248 +++++ custom_mutators/libfuzzer/FuzzerIO.h | 112 ++ custom_mutators/libfuzzer/FuzzerIOPosix.cpp | 223 ++++ custom_mutators/libfuzzer/FuzzerIOWindows.cpp | 513 +++++++++ custom_mutators/libfuzzer/FuzzerInterface.h | 79 ++ custom_mutators/libfuzzer/FuzzerInternal.h | 173 +++ custom_mutators/libfuzzer/FuzzerLoop.cpp | 1087 +++++++++++++++++++ custom_mutators/libfuzzer/FuzzerMerge.cpp | 485 +++++++++ custom_mutators/libfuzzer/FuzzerMerge.h | 87 ++ custom_mutators/libfuzzer/FuzzerMutate.cpp | 720 +++++++++++++ custom_mutators/libfuzzer/FuzzerMutate.h | 158 +++ custom_mutators/libfuzzer/FuzzerOptions.h | 90 ++ custom_mutators/libfuzzer/FuzzerPlatform.h | 163 +++ custom_mutators/libfuzzer/FuzzerRandom.h | 38 + custom_mutators/libfuzzer/FuzzerSHA1.cpp | 269 +++++ custom_mutators/libfuzzer/FuzzerSHA1.h | 32 + custom_mutators/libfuzzer/FuzzerTracePC.cpp | 819 ++++++++++++++ custom_mutators/libfuzzer/FuzzerTracePC.h | 291 +++++ custom_mutators/libfuzzer/FuzzerUtil.cpp | 314 ++++++ custom_mutators/libfuzzer/FuzzerUtil.h | 117 ++ custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp | 205 ++++ custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp | 658 ++++++++++++ custom_mutators/libfuzzer/FuzzerUtilLinux.cpp | 43 + custom_mutators/libfuzzer/FuzzerUtilPosix.cpp | 239 +++++ custom_mutators/libfuzzer/FuzzerUtilWindows.cpp | 279 +++++ custom_mutators/libfuzzer/FuzzerValueBitMap.h | 73 ++ custom_mutators/libfuzzer/Makefile | 81 ++ custom_mutators/libfuzzer/README.md | 24 + custom_mutators/libfuzzer/libfuzzer.cpp | 147 +++ custom_mutators/libfuzzer/libfuzzer.inc | 36 + custom_mutators/symcc/symcc.c | 7 +- docs/Changelog.md | 2 + include/afl-prealloc.h | 2 +- include/alloc-inl.h | 4 +- include/list.h | 1 + src/afl-cc.c | 8 +- 62 files changed, 11668 insertions(+), 20 deletions(-) create mode 100644 custom_mutators/grammar_mutator/README.md create mode 100755 custom_mutators/grammar_mutator/build_grammar_mutator.sh create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltins.h create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h create mode 100644 custom_mutators/libfuzzer/FuzzerCommand.h create mode 100644 custom_mutators/libfuzzer/FuzzerCorpus.h create mode 100644 custom_mutators/libfuzzer/FuzzerCrossOver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.h create mode 100644 custom_mutators/libfuzzer/FuzzerDefs.h create mode 100644 custom_mutators/libfuzzer/FuzzerDictionary.h create mode 100644 custom_mutators/libfuzzer/FuzzerDriver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.def create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.h create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtraCounters.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFlags.def create mode 100644 custom_mutators/libfuzzer/FuzzerFork.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFork.h create mode 100644 custom_mutators/libfuzzer/FuzzerIO.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIO.h create mode 100644 custom_mutators/libfuzzer/FuzzerIOPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIOWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerInterface.h create mode 100644 custom_mutators/libfuzzer/FuzzerInternal.h create mode 100644 custom_mutators/libfuzzer/FuzzerLoop.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.h create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.h create mode 100644 custom_mutators/libfuzzer/FuzzerOptions.h create mode 100644 custom_mutators/libfuzzer/FuzzerPlatform.h create mode 100644 custom_mutators/libfuzzer/FuzzerRandom.h create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.h create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilLinux.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerValueBitMap.h create mode 100644 custom_mutators/libfuzzer/Makefile create mode 100644 custom_mutators/libfuzzer/README.md create mode 100644 custom_mutators/libfuzzer/libfuzzer.cpp create mode 100644 custom_mutators/libfuzzer/libfuzzer.inc (limited to 'src/afl-cc.c') diff --git a/GNUmakefile b/GNUmakefile index b9a017d2..d47f8247 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -465,9 +465,9 @@ code-format: ./.custom-format.py -i instrumentation/*.h ./.custom-format.py -i instrumentation/*.cc ./.custom-format.py -i instrumentation/*.c - ./.custom-format.py -i custom_mutators/*/*.c + ./.custom-format.py -i custom_mutators/*/*.c* @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( - ./.custom-format.py -i examples/*/*.c + ./.custom-format.py -i examples/*/*.c* ./.custom-format.py -i examples/*/*.h ./.custom-format.py -i test/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.c diff --git a/README.md b/README.md index 2fc9d807..c2108e93 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,11 @@ How to do this is described below. Then build the target. (Usually with `make`) +**NOTE**: sometimes configure and build systems are fickle and do not like +stderr output (and think this means a test failure) - which is something +afl++ like to do to show statistics. It is recommended to disable them via +`export AFL_QUIET=1`. + ##### configure For `configure` build systems this is usually done by: diff --git a/custom_mutators/README.md b/custom_mutators/README.md index 993ccaa1..0cf52746 100644 --- a/custom_mutators/README.md +++ b/custom_mutators/README.md @@ -12,9 +12,7 @@ git submodule init git submodule update ``` -otherwise just checkout the repository here with either -`git clone https://github.com/AFLplusplus/Grammar-Mutator` or -`svn co https://github.com/AFLplusplus/Grammar-Mutator`. +otherwise just use the script: `grammar_mutator/build_grammar_mutator.sh` Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use it. diff --git a/custom_mutators/grammar_mutator/README.md b/custom_mutators/grammar_mutator/README.md new file mode 100644 index 00000000..a015744c --- /dev/null +++ b/custom_mutators/grammar_mutator/README.md @@ -0,0 +1,6 @@ +# Grammar-Mutator + +This is just a stub directory that will clone the real grammar mutator +directory. + +Execute `./build_grammar_mutator.sh` to set everything up. diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh new file mode 100755 index 00000000..f3f5e164 --- /dev/null +++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test -d Grammar-Mutator || git clone --depth=1 https://github.com/AFLplusplus/Grammar-Mutator + +cd Grammar-Mutator || exit 1 +git stash ; git pull + +wget -c https://www.antlr.org/download/antlr-4.8-complete.jar + +echo +echo +echo "All successfully prepared!" +echo "To build for your grammar just do:" +echo " cd Grammar_Mutator" +echo " make GRAMMAR_FILE=/path/to/your/grammar" +echo "You will find a JSON and RUBY grammar in Grammar_Mutator/grammars to play with." +echo diff --git a/custom_mutators/honggfuzz/Makefile b/custom_mutators/honggfuzz/Makefile index 1d46f163..5c2fcddb 100644 --- a/custom_mutators/honggfuzz/Makefile +++ b/custom_mutators/honggfuzz/Makefile @@ -1,10 +1,10 @@ CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic -all: honggfuzz.so +all: honggfuzz-mutator.so -honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c - $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c +honggfuzz-mutator.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c + $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz-mutator.so honggfuzz.c mangle.c ../../src/afl-performance.c update: @# seriously? --unlink is a dud option? sigh ... diff --git a/custom_mutators/honggfuzz/README.md b/custom_mutators/honggfuzz/README.md index 8824976f..e1cab281 100644 --- a/custom_mutators/honggfuzz/README.md +++ b/custom_mutators/honggfuzz/README.md @@ -1,12 +1,12 @@ # custum mutator: honggfuzz mangle -this is the very good honggfuzz mutator in mangle.c as a custom mutator +this is the honggfuzz mutator in mangle.c as a custom mutator module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h with a lot of mocking around it :-) just type `make` to build -```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz.so afl-fuzz ...``` +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz-mutator.so afl-fuzz ...``` > Original repository: https://github.com/google/honggfuzz > Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5 diff --git a/custom_mutators/libfuzzer/FuzzerBuiltins.h b/custom_mutators/libfuzzer/FuzzerBuiltins.h new file mode 100644 index 00000000..4c0ada82 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltins.h @@ -0,0 +1,35 @@ +//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos around builtin functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_H +#define LLVM_FUZZER_BUILTINS_H + +#include "FuzzerPlatform.h" + +#if !LIBFUZZER_MSVC +#include + +#define GET_CALLER_PC() __builtin_return_address(0) + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } +inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } +inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } + +} // namespace fuzzer + +#endif // !LIBFUZZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_H diff --git a/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h new file mode 100644 index 00000000..c5bec978 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h @@ -0,0 +1,72 @@ +//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos that use intrinsics instead of builtin functions +// which cannot be compiled by MSVC. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_MSVC_H +#define LLVM_FUZZER_BUILTINS_MSVC_H + +#include "FuzzerPlatform.h" + +#if LIBFUZZER_MSVC +#include +#include +#include + +// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent +// from +#define GET_CALLER_PC() _ReturnAddress() + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +// Use alternatives to __builtin functions from and on +// Windows since the builtins are not supported by MSVC. +inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); } +inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); } +inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); } + +// The functions below were mostly copied from +// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used +// outside of Windows. +inline uint32_t Clzll(uint64_t X) { + unsigned long LeadZeroIdx = 0; + +#if !defined(_M_ARM) && !defined(_M_X64) + // Scan the high 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X >> 32))) + return static_cast(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X))) + return static_cast(63 - LeadZeroIdx); + +#else + if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx; +#endif + return 64; +} + +inline uint32_t Clz(uint32_t X) { + unsigned long LeadZeroIdx = 0; + if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; + return 32; +} + +inline int Popcountll(unsigned long long X) { +#if !defined(_M_ARM) && !defined(_M_X64) + return __popcnt(X) + __popcnt(X >> 32); +#else + return __popcnt64(X); +#endif +} + +} // namespace fuzzer + +#endif // LIBFUZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_MSVC_H diff --git a/custom_mutators/libfuzzer/FuzzerCommand.h b/custom_mutators/libfuzzer/FuzzerCommand.h new file mode 100644 index 00000000..87308864 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCommand.h @@ -0,0 +1,178 @@ +//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerCommand represents a command to run in a subprocess. It allows callers +// to manage command line arguments and output and error streams. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_COMMAND_H +#define LLVM_FUZZER_COMMAND_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +class Command final { +public: + // This command line flag is used to indicate that the remaining command line + // is immutable, meaning this flag effectively marks the end of the mutable + // argument list. + static inline const char *ignoreRemainingArgs() { + return "-ignore_remaining_args=1"; + } + + Command() : CombinedOutAndErr(false) {} + + explicit Command(const Vector &ArgsToAdd) + : Args(ArgsToAdd), CombinedOutAndErr(false) {} + + explicit Command(const Command &Other) + : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), + OutputFile(Other.OutputFile) {} + + Command &operator=(const Command &Other) { + Args = Other.Args; + CombinedOutAndErr = Other.CombinedOutAndErr; + OutputFile = Other.OutputFile; + return *this; + } + + ~Command() {} + + // Returns true if the given Arg is present in Args. Only checks up to + // "-ignore_remaining_args=1". + bool hasArgument(const std::string &Arg) const { + auto i = endMutableArgs(); + return std::find(Args.begin(), i, Arg) != i; + } + + // Gets all of the current command line arguments, **including** those after + // "-ignore-remaining-args=1". + const Vector &getArguments() const { return Args; } + + // Adds the given argument before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArgument(const std::string &Arg) { + Args.insert(endMutableArgs(), Arg); + } + + // Adds all given arguments before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArguments(const Vector &ArgsToAdd) { + Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); + } + + // Removes the given argument from the command argument list. Ignores any + // occurrences after "-ignore_remaining_args=1", if present. + void removeArgument(const std::string &Arg) { + auto i = endMutableArgs(); + Args.erase(std::remove(Args.begin(), i, Arg), i); + } + + // Like hasArgument, but checks for "-[Flag]=...". + bool hasFlag(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + return std::any_of(Args.begin(), endMutableArgs(), IsMatch); + } + + // Returns the value of the first instance of a given flag, or an empty string + // if the flag isn't present. Ignores any occurrences after + // "-ignore_remaining_args=1", if present. + std::string getFlagValue(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + auto j = std::find_if(Args.begin(), i, IsMatch); + std::string result; + if (j != i) { + result = j->substr(Arg.length()); + } + return result; + } + + // Like AddArgument, but adds "-[Flag]=[Value]". + void addFlag(const std::string &Flag, const std::string &Value) { + addArgument("-" + Flag + "=" + Value); + } + + // Like RemoveArgument, but removes "-[Flag]=...". + void removeFlag(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); + } + + // Returns whether the command's stdout is being written to an output file. + bool hasOutputFile() const { return !OutputFile.empty(); } + + // Returns the currently set output file. + const std::string &getOutputFile() const { return OutputFile; } + + // Configures the command to redirect its output to the name file. + void setOutputFile(const std::string &FileName) { OutputFile = FileName; } + + // Returns whether the command's stderr is redirected to stdout. + bool isOutAndErrCombined() const { return CombinedOutAndErr; } + + // Sets whether to redirect the command's stderr to its stdout. + void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } + + // Returns a string representation of the command. On many systems this will + // be the equivalent command line. + std::string toString() const { + std::stringstream SS; + for (auto arg : getArguments()) + SS << arg << " "; + if (hasOutputFile()) + SS << ">" << getOutputFile() << " "; + if (isOutAndErrCombined()) + SS << "2>&1 "; + std::string result = SS.str(); + if (!result.empty()) + result = result.substr(0, result.length() - 1); + return result; + } + +private: + Command(Command &&Other) = delete; + Command &operator=(Command &&Other) = delete; + + Vector::iterator endMutableArgs() { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + Vector::const_iterator endMutableArgs() const { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + // The command arguments. Args[0] is the command name. + Vector Args; + + // True indicates stderr is redirected to stdout. + bool CombinedOutAndErr; + + // If not empty, stdout is redirected to the named file. + std::string OutputFile; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_COMMAND_H diff --git a/custom_mutators/libfuzzer/FuzzerCorpus.h b/custom_mutators/libfuzzer/FuzzerCorpus.h new file mode 100644 index 00000000..daea4f52 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCorpus.h @@ -0,0 +1,581 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + std::chrono::microseconds TimeOfUnit; + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool NeverReduce = false; + bool MayDeleteFile = false; + bool Reduced = false; + bool HasFocusFunction = false; + Vector UniqFeatureSet; + Vector DataFlowTraceForFocusFunction; + // Power schedule. + bool NeedsEnergyUpdate = false; + double Energy = 0.0; + size_t SumIncidence = 0; + Vector> FeatureFreqs; + + // Delete feature Idx and its frequency from FeatureFreqs. + bool DeleteFeatureFreq(uint32_t Idx) { + if (FeatureFreqs.empty()) + return false; + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + FeatureFreqs.erase(Lower); + return true; + } + return false; + } + + // Assign more energy to a high-entropy seed, i.e., that reveals more + // information about the globally rare features in the neighborhood of the + // seed. Since we do not know the entropy of a seed that has never been + // executed we assign fresh seeds maximum entropy and let II->Energy approach + // the true entropy from above. If ScalePerExecTime is true, the computed + // entropy is scaled based on how fast this input executes compared to the + // average execution time of inputs. The faster an input executes, the more + // energy gets assigned to the input. + void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime, + std::chrono::microseconds AverageUnitExecutionTime) { + Energy = 0.0; + SumIncidence = 0; + + // Apply add-one smoothing to locally discovered features. + for (auto F : FeatureFreqs) { + size_t LocalIncidence = F.second + 1; + Energy -= LocalIncidence * logl(LocalIncidence); + SumIncidence += LocalIncidence; + } + + // Apply add-one smoothing to locally undiscovered features. + // PreciseEnergy -= 0; // since logl(1.0) == 0) + SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size()); + + // Add a single locally abundant feature apply add-one smoothing. + size_t AbdIncidence = NumExecutedMutations + 1; + Energy -= AbdIncidence * logl(AbdIncidence); + SumIncidence += AbdIncidence; + + // Normalize. + if (SumIncidence != 0) + Energy = (Energy / SumIncidence) + logl(SumIncidence); + + if (ScalePerExecTime) { + // Scaling to favor inputs with lower execution time. + uint32_t PerfScore = 100; + if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10) + PerfScore = 10; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4) + PerfScore = 25; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2) + PerfScore = 50; + else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4) + PerfScore = 75; + else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count()) + PerfScore = 300; + else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count()) + PerfScore = 200; + else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count()) + PerfScore = 150; + + Energy *= PerfScore; + } + } + + // Increment the frequency of the feature Idx. + void UpdateFeatureFrequency(uint32_t Idx) { + NeedsEnergyUpdate = true; + + // The local feature frequencies is an ordered vector of pairs. + // If there are no local feature frequencies, push_back preserves order. + // Set the feature frequency for feature Idx32 to 1. + if (FeatureFreqs.empty()) { + FeatureFreqs.push_back(std::pair(Idx, 1)); + return; + } + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + // If feature Idx32 already exists, increment its frequency. + // Otherwise, insert a new pair right after the next lower index. + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + Lower->second++; + } else { + FeatureFreqs.insert(Lower, std::pair(Idx, 1)); + } + } +}; + +struct EntropicOptions { + bool Enabled; + size_t NumberOfRarestFeatures; + size_t FeatureFrequencyThreshold; + bool ScalePerExecTime; +}; + +class InputCorpus { + static const uint32_t kFeatureSetSize = 1 << 21; + static const uint8_t kMaxMutationFactor = 20; + static const size_t kSparseEnergyUpdates = 100; + + size_t NumExecutedMutations = 0; + + EntropicOptions Entropic; + +public: + InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic) + : Entropic(Entropic), OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + size_t MaxInputSize() const { + size_t Res = 0; + for (auto II : Inputs) + Res = std::max(Res, II->U.size()); + return Res; + } + void IncrementNumExecutedMutations() { NumExecutedMutations++; } + + size_t NumInputsThatTouchFocusFunction() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return II->HasFocusFunction; + }); + } + + size_t NumInputsWithDataFlowTrace() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return !II->DataFlowTraceForFocusFunction.empty(); + }); + } + + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, + bool HasFocusFunction, bool NeverReduce, + std::chrono::microseconds TimeOfUnit, + const Vector &FeatureSet, + const DataFlowTrace &DFT, const InputInfo *BaseII) { + assert(!U.empty()); + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.NeverReduce = NeverReduce; + II.TimeOfUnit = TimeOfUnit; + II.MayDeleteFile = MayDeleteFile; + II.UniqFeatureSet = FeatureSet; + II.HasFocusFunction = HasFocusFunction; + // Assign maximal energy to the new seed. + II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size()); + II.SumIncidence = RareFeatures.size(); + II.NeedsEnergyUpdate = false; + std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); + ComputeSHA1(U.data(), U.size(), II.Sha1); + auto Sha1Str = Sha1ToString(II.Sha1); + Hashes.insert(Sha1Str); + if (HasFocusFunction) + if (auto V = DFT.Get(Sha1Str)) + II.DataFlowTraceForFocusFunction = *V; + // This is a gross heuristic. + // Ideally, when we add an element to a corpus we need to know its DFT. + // But if we don't, we'll use the DFT of its base input. + if (II.DataFlowTraceForFocusFunction.empty() && BaseII) + II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction; + DistributionNeedsUpdate = true; + PrintCorpus(); + // ValidateFeatureSet(); + return &II; + } + + // Debug-only + void PrintUnit(const Unit &U) { + if (!FeatureDebug) return; + for (uint8_t C : U) { + if (C != 'F' && C != 'U' && C != 'Z') + C = '.'; + Printf("%c", C); + } + } + + // Debug-only + void PrintFeatureSet(const Vector &FeatureSet) { + if (!FeatureDebug) return; + Printf("{"); + for (uint32_t Feature: FeatureSet) + Printf("%u,", Feature); + Printf("}"); + } + + // Debug-only + void PrintCorpus() { + if (!FeatureDebug) return; + Printf("======= CORPUS:\n"); + int i = 0; + for (auto II : Inputs) { + if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) { + Printf("[%2d] ", i); + Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size()); + PrintUnit(II->U); + Printf(" "); + PrintFeatureSet(II->UniqFeatureSet); + Printf("\n"); + } + i++; + } + } + + void Replace(InputInfo *II, const Unit &U) { + assert(II->U.size() > U.size()); + Hashes.erase(Sha1ToString(II->Sha1)); + DeleteFile(*II); + ComputeSHA1(U.data(), U.size(), II->Sha1); + Hashes.insert(Sha1ToString(II->Sha1)); + II->U = U; + II->Reduced = true; + DistributionNeedsUpdate = true; + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + } + + InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) { + if (!UniformDist) { + return ChooseUnitToMutate(Rand); + } + InputInfo &II = *Inputs[Rand(Inputs.size())]; + assert(!II.U.empty()); + return II; + } + + // Returns an index of random unit from the corpus to mutate. + size_t ChooseUnitIdxToMutate(Random &Rand) { + UpdateCorpusDistribution(Rand); + size_t Idx = static_cast(CorpusDistribution(Rand)); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteFile(const InputInfo &II) { + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + DeleteFile(II); + Unit().swap(II.U); + II.Energy = 0.0; + II.NeedsEnergyUpdate = false; + DistributionNeedsUpdate = true; + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + void AddRareFeature(uint32_t Idx) { + // Maintain *at least* TopXRarestFeatures many rare features + // and all features with a frequency below ConsideredRare. + // Remove all other features. + while (RareFeatures.size() > Entropic.NumberOfRarestFeatures && + FreqOfMostAbundantRareFeature > Entropic.FeatureFrequencyThreshold) { + + // Find most and second most abbundant feature. + uint32_t MostAbundantRareFeatureIndices[2] = {RareFeatures[0], + RareFeatures[0]}; + size_t Delete = 0; + for (size_t i = 0; i < RareFeatures.size(); i++) { + uint32_t Idx2 = RareFeatures[i]; + if (GlobalFeatureFreqs[Idx2] >= + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[0]]) { + MostAbundantRareFeatureIndices[1] = MostAbundantRareFeatureIndices[0]; + MostAbundantRareFeatureIndices[0] = Idx2; + Delete = i; + } + } + + // Remove most abundant rare feature. + RareFeatures[Delete] = RareFeatures.back(); + RareFeatures.pop_back(); + + for (auto II : Inputs) { + if (II->DeleteFeatureFreq(MostAbundantRareFeatureIndices[0])) + II->NeedsEnergyUpdate = true; + } + + // Set 2nd most abundant as the new most abundant feature count. + FreqOfMostAbundantRareFeature = + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[1]]; + } + + // Add rare feature, handle collisions, and update energy. + RareFeatures.push_back(Idx); + GlobalFeatureFreqs[Idx] = 0; + for (auto II : Inputs) { + II->DeleteFeatureFreq(Idx); + + // Apply add-one smoothing to this locally undiscovered feature. + // Zero energy seeds will never be fuzzed and remain zero energy. + if (II->Energy > 0.0) { + II->SumIncidence += 1; + II->Energy += logl(II->SumIncidence) / II->SumIncidence; + } + } + + DistributionNeedsUpdate = true; + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } else { + NumAddedFeatures++; + if (Entropic.Enabled) + AddRareFeature((uint32_t)Idx); + } + NumUpdatedFeatures++; + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + return true; + } + return false; + } + + // Increment frequency of feature Idx globally and locally. + void UpdateFeatureFrequency(InputInfo *II, size_t Idx) { + uint32_t Idx32 = Idx % kFeatureSetSize; + + // Saturated increment. + if (GlobalFeatureFreqs[Idx32] == 0xFFFF) + return; + uint16_t Freq = GlobalFeatureFreqs[Idx32]++; + + // Skip if abundant. + if (Freq > FreqOfMostAbundantRareFeature || + std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) == + RareFeatures.end()) + return; + + // Update global frequencies. + if (Freq == FreqOfMostAbundantRareFeature) + FreqOfMostAbundantRareFeature++; + + // Update local frequencies. + if (II) + II->UpdateFeatureFrequency(Idx32); + } + + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + // + // Hypothesis: inputs that maximize information about globally rare features + // are interesting. + void UpdateCorpusDistribution(Random &Rand) { + // Skip update if no seeds or rare features were added/deleted. + // Sparse updates for local change of feature frequencies, + // i.e., randomly do not skip. + if (!DistributionNeedsUpdate && + (!Entropic.Enabled || Rand(kSparseEnergyUpdates))) + return; + + DistributionNeedsUpdate = false; + + size_t N = Inputs.size(); + assert(N); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + + std::chrono::microseconds AverageUnitExecutionTime(0); + for (auto II : Inputs) { + AverageUnitExecutionTime += II->TimeOfUnit; + } + AverageUnitExecutionTime /= N; + + bool VanillaSchedule = true; + if (Entropic.Enabled) { + for (auto II : Inputs) { + if (II->NeedsEnergyUpdate && II->Energy != 0.0) { + II->NeedsEnergyUpdate = false; + II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime, + AverageUnitExecutionTime); + } + } + + for (size_t i = 0; i < N; i++) { + + if (Inputs[i]->NumFeatures == 0) { + // If the seed doesn't represent any features, assign zero energy. + Weights[i] = 0.; + } else if (Inputs[i]->NumExecutedMutations / kMaxMutationFactor > + NumExecutedMutations / Inputs.size()) { + // If the seed was fuzzed a lot more than average, assign zero energy. + Weights[i] = 0.; + } else { + // Otherwise, simply assign the computed energy. + Weights[i] = Inputs[i]->Energy; + } + + // If energy for all seeds is zero, fall back to vanilla schedule. + if (Weights[i] > 0.0) + VanillaSchedule = false; + } + } + + if (VanillaSchedule) { + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures + ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1) + : 0.; + } + + if (FeatureDebug) { + for (size_t i = 0; i < N; i++) + Printf("%zd ", Inputs[i]->NumFeatures); + Printf("SCORE\n"); + for (size_t i = 0; i < N; i++) + Printf("%f ", Weights[i]); + Printf("Weights\n"); + } + CorpusDistribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution CorpusDistribution; + + Vector Intervals; + Vector Weights; + + std::unordered_set Hashes; + Vector Inputs; + + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + + bool DistributionNeedsUpdate = true; + uint16_t FreqOfMostAbundantRareFeature = 0; + uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {}; + Vector RareFeatures; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/custom_mutators/libfuzzer/FuzzerCrossOver.cpp b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000..3b3fd94a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,60 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t * InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + + } + + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + + } + + return OutPos; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp new file mode 100644 index 00000000..797a52a7 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp @@ -0,0 +1,344 @@ +//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace +//===----------------------------------------------------------------------===// + +#include "FuzzerDataFlowTrace.h" + +#include "FuzzerCommand.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static const char *kFunctionsTxt = "functions.txt"; + +bool BlockCoverage::AppendCoverage(const std::string &S) { + + std::stringstream SS(S); + return AppendCoverage(SS); + +} + +// Coverage lines have this form: +// CN X Y Z T +// where N is the number of the function, T is the total number of instrumented +// BBs, and X,Y,Z, if present, are the indecies of covered BB. +// BB #0, which is the entry block, is not explicitly listed. +bool BlockCoverage::AppendCoverage(std::istream &IN) { + + std::string L; + while (std::getline(IN, L, '\n')) { + + if (L.empty()) continue; + std::stringstream SS(L.c_str() + 1); + size_t FunctionId = 0; + SS >> FunctionId; + if (L[0] == 'F') { + + FunctionsWithDFT.insert(FunctionId); + continue; + + } + + if (L[0] != 'C') continue; + Vector CoveredBlocks; + while (true) { + + uint32_t BB = 0; + SS >> BB; + if (!SS) break; + CoveredBlocks.push_back(BB); + + } + + if (CoveredBlocks.empty()) return false; + uint32_t NumBlocks = CoveredBlocks.back(); + CoveredBlocks.pop_back(); + for (auto BB : CoveredBlocks) + if (BB >= NumBlocks) return false; + auto It = Functions.find(FunctionId); + auto &Counters = + It == Functions.end() + ? Functions.insert({FunctionId, Vector(NumBlocks)}) + .first->second + : It->second; + + if (Counters.size() != NumBlocks) return false; // wrong number of blocks. + + Counters[0]++; + for (auto BB : CoveredBlocks) + Counters[BB]++; + + } + + return true; + +} + +// Assign weights to each function. +// General principles: +// * any uncovered function gets weight 0. +// * a function with lots of uncovered blocks gets bigger weight. +// * a function with a less frequently executed code gets bigger weight. +Vector BlockCoverage::FunctionWeights(size_t NumFunctions) const { + + Vector Res(NumFunctions); + for (auto It : Functions) { + + auto FunctionID = It.first; + auto Counters = It.second; + assert(FunctionID < NumFunctions); + auto &Weight = Res[FunctionID]; + // Give higher weight if the function has a DFT. + Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1; + // Give higher weight to functions with less frequently seen basic blocks. + Weight /= SmallestNonZeroCounter(Counters); + // Give higher weight to functions with the most uncovered basic blocks. + Weight *= NumberOfUncoveredBlocks(Counters) + 1; + + } + + return Res; + +} + +void DataFlowTrace::ReadCoverage(const std::string &DirPath) { + + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; + std::ifstream IF(SF.File); + Coverage.AppendCoverage(IF); + + } + +} + +static void DFTStringAppendToVector(Vector * DFT, + const std::string &DFTString) { + + assert(DFT->size() == DFTString.size()); + for (size_t I = 0, Len = DFT->size(); I < Len; I++) + (*DFT)[I] = DFTString[I] == '1'; + +} + +// converts a string of '0' and '1' into a Vector +static Vector DFTStringToVector(const std::string &DFTString) { + + Vector DFT(DFTString.size()); + DFTStringAppendToVector(&DFT, DFTString); + return DFT; + +} + +static bool ParseError(const char *Err, const std::string &Line) { + + Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str()); + return false; + +} + +// TODO(metzman): replace std::string with std::string_view for +// better performance. Need to figure our how to use string_view on Windows. +static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, + std::string *DFTString) { + + if (!Line.empty() && Line[0] != 'F') return false; // Ignore coverage. + size_t SpacePos = Line.find(' '); + if (SpacePos == std::string::npos) + return ParseError("no space in the trace line", Line); + if (Line.empty() || Line[0] != 'F') + return ParseError("the trace line doesn't start with 'F'", Line); + *FunctionNum = std::atol(Line.c_str() + 1); + const char *Beg = Line.c_str() + SpacePos + 1; + const char *End = Line.c_str() + Line.size(); + assert(Beg < End); + size_t Len = End - Beg; + for (size_t I = 0; I < Len; I++) { + + if (Beg[I] != '0' && Beg[I] != '1') + return ParseError("the trace should contain only 0 or 1", Line); + + } + + *DFTString = Beg; + return true; + +} + +bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand) { + + if (DirPath.empty()) return false; + Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + std::string L; + size_t FocusFuncIdx = SIZE_MAX; + Vector FunctionNames; + + // Collect the hashes of the corpus files. + for (auto &SF : CorporaFiles) + CorporaHashes.insert(Hash(FileToVector(SF.File))); + + // Read functions.txt + std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt)); + size_t NumFunctions = 0; + while (std::getline(IF, L, '\n')) { + + FunctionNames.push_back(L); + NumFunctions++; + if (*FocusFunction == L) FocusFuncIdx = NumFunctions - 1; + + } + + if (!NumFunctions) return false; + + if (*FocusFunction == "auto") { + + // AUTOFOCUS works like this: + // * reads the coverage data from the DFT files. + // * assigns weights to functions based on coverage. + // * chooses a random function according to the weights. + ReadCoverage(DirPath); + auto Weights = Coverage.FunctionWeights(NumFunctions); + Vector Intervals(NumFunctions + 1); + std::iota(Intervals.begin(), Intervals.end(), 0); + auto Distribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + FocusFuncIdx = static_cast(Distribution(Rand)); + *FocusFunction = FunctionNames[FocusFuncIdx]; + assert(FocusFuncIdx < NumFunctions); + Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, + FunctionNames[FocusFuncIdx].c_str()); + for (size_t i = 0; i < NumFunctions; i++) { + + if (!Weights[i]) continue; + Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, + Weights[i], Coverage.GetNumberOfBlocks(i), + Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), + FunctionNames[i].c_str()); + + } + + } + + if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) + return false; + + // Read traces. + size_t NumTraceFiles = 0; + size_t NumTracesWithFocusFunction = 0; + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; // not in the corpus. + NumTraceFiles++; + // Printf("=== %s\n", Name.c_str()); + std::ifstream IF(SF.File); + while (std::getline(IF, L, '\n')) { + + size_t FunctionNum = 0; + std::string DFTString; + if (ParseDFTLine(L, &FunctionNum, &DFTString) && + FunctionNum == FocusFuncIdx) { + + NumTracesWithFocusFunction++; + + if (FunctionNum >= NumFunctions) + return ParseError("N is greater than the number of functions", L); + Traces[Name] = DFTStringToVector(DFTString); + // Print just a few small traces. + if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) + Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str()); + break; // No need to parse the following lines. + + } + + } + + } + + Printf( + "INFO: DataFlowTrace: %zd trace files, %zd functions, " + "%zd traces with focus function\n", + NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); + return NumTraceFiles > 0; + +} + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles) { + + Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n", + DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size()); + if (CorporaFiles.empty()) { + + Printf("ERROR: can't collect data flow without corpus provided."); + return 1; + + } + + static char DFSanEnv[] = "DFSAN_OPTIONS=warn_unimplemented=0"; + putenv(DFSanEnv); + MkDir(DirPath); + for (auto &F : CorporaFiles) { + + // For every input F we need to collect the data flow and the coverage. + // Data flow collection may fail if we request too many DFSan tags at once. + // So, we start from requesting all tags in range [0,Size) and if that fails + // we then request tags in [0,Size/2) and [Size/2, Size), and so on. + // Function number => DFT. + auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File))); + std::unordered_map> DFTMap; + std::unordered_set Cov; + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.addArgument(F.File); + Cmd.addArgument(OutPath); + Printf("CMD: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + + // Write functions.txt if it's currently empty or doesn't exist. + auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt); + if (FileToString(FunctionsTxtPath).empty()) { + + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.setOutputFile(FunctionsTxtPath); + ExecuteCommand(Cmd); + + } + + return 0; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h new file mode 100644 index 00000000..d6e3de30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h @@ -0,0 +1,135 @@ +//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace; reads and handles a data-flow trace. +// +// A data flow trace is generated by e.g. dataflow/DataFlow.cpp +// and is stored on disk in a separate directory. +// +// The trace dir contains a file 'functions.txt' which lists function names, +// oner per line, e.g. +// ==> functions.txt <== +// Func2 +// LLVMFuzzerTestOneInput +// Func1 +// +// All other files in the dir are the traces, see dataflow/DataFlow.cpp. +// The name of the file is sha1 of the input used to generate the trace. +// +// Current status: +// the data is parsed and the summary is printed, but the data is not yet +// used in any other way. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DATA_FLOW_TRACE +#define LLVM_FUZZER_DATA_FLOW_TRACE + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles); + +class BlockCoverage { + public: + bool AppendCoverage(std::istream &IN); + bool AppendCoverage(const std::string &S); + + size_t NumCoveredFunctions() const { return Functions.size(); } + + uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + if (BasicBlockId < Counters.size()) + return Counters[BasicBlockId]; + return 0; + } + + uint32_t GetNumberOfBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + return Counters.size(); + } + + uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + uint32_t Result = 0; + for (auto Cnt: Counters) + if (Cnt) + Result++; + return Result; + } + + Vector FunctionWeights(size_t NumFunctions) const; + void clear() { Functions.clear(); } + + private: + + typedef Vector CoverageVector; + + uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { + uint32_t Res = 0; + for (auto Cnt : Counters) + if (Cnt) + Res++; + return Res; + } + + uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { + return Counters.size() - NumberOfCoveredBlocks(Counters); + } + + uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { + assert(!Counters.empty()); + uint32_t Res = Counters[0]; + for (auto Cnt : Counters) + if (Cnt) + Res = Min(Res, Cnt); + assert(Res); + return Res; + } + + // Function ID => vector of counters. + // Each counter represents how many input files trigger the given basic block. + std::unordered_map Functions; + // Functions that have DFT entry. + std::unordered_set FunctionsWithDFT; +}; + +class DataFlowTrace { + public: + void ReadCoverage(const std::string &DirPath); + bool Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand); + void Clear() { Traces.clear(); } + const Vector *Get(const std::string &InputSha1) const { + auto It = Traces.find(InputSha1); + if (It != Traces.end()) + return &It->second; + return nullptr; + } + + private: + // Input's sha1 => DFT for the FocusFunction. + std::unordered_map > Traces; + BlockCoverage Coverage; + std::unordered_set CorporaHashes; +}; +} // namespace fuzzer + +#endif // LLVM_FUZZER_DATA_FLOW_TRACE diff --git a/custom_mutators/libfuzzer/FuzzerDefs.h b/custom_mutators/libfuzzer/FuzzerDefs.h new file mode 100644 index 00000000..1a2752af --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDefs.h @@ -0,0 +1,75 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace fuzzer { + +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +// We are using a custom allocator to give a different symbol name to STL +// containers in order to avoid ODR violations. +template + class fuzzer_allocator: public std::allocator { + public: + fuzzer_allocator() = default; + + template + fuzzer_allocator(const fuzzer_allocator&) {} + + template + struct rebind { typedef fuzzer_allocator other; }; + }; + +template +using Vector = std::vector>; + +template +using Set = std::set, fuzzer_allocator>; + +typedef Vector Unit; +typedef Vector UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); + +extern bool RunningUserCallback; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/custom_mutators/libfuzzer/FuzzerDictionary.h b/custom_mutators/libfuzzer/FuzzerDictionary.h new file mode 100644 index 00000000..301c5d9a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDictionary.h @@ -0,0 +1,118 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template class FixedWord { +public: + static const size_t kMaxSize = kMaxSizeT; + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord &w) const { + return Size == w.Size && 0 == memcmp(Data, w.Data, Size); + } + + static size_t GetMaxSize() { return kMaxSize; } + const uint8_t *data() const { return Data; } + uint8_t size() const { return Size; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<64> Word; + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successful, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed successfully. +bool ParseDictionaryFile(const std::string &Text, Vector *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/custom_mutators/libfuzzer/FuzzerDriver.cpp b/custom_mutators/libfuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000..9a0a32b0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDriver.cpp @@ -0,0 +1,1122 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerCorpus.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +#if LIBFUZZER_MSVC +extern "C" void __libfuzzer_is_present() { + +} + + #if defined(_M_IX86) || defined(__i386__) + #pragma comment(linker, "/include:___libfuzzer_is_present") + #else + #pragma comment(linker, "/include:__libfuzzer_is_present") + #endif +#else +extern "C" __attribute__((used)) void __libfuzzer_is_present() { + +} + +#endif // LIBFUZZER_MSVC + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + + const char * Name; + const char * Description; + int Default; + int * IntFlag; + const char ** StrFlag; + unsigned int *UIntFlag; + +}; + +struct { +\ +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +} Flags; + +static const FlagDescription FlagDescriptions[]{ +\ +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static Vector *Inputs; +static std::string * ProgName; + +static void PrintHelp() { + + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + + } + + Printf( + "\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); + +} + +static const char *FlagValue(const char *Param, const char *Name) { + + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; + +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + + long Res = 0; + long Sign = 1; + if (*Str == '-') { + + Str++; + Sign = -1; + + } + + for (size_t i = 0; Str[i]; i++) { + + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') return Res; + Res = Res * 10 + (Ch - '0'); + + } + + return Res * Sign; + +} + +static bool ParseOneFlag(const char *Param) { + + if (Param[0] != '-') return false; + if (Param[1] == '-') { + + static bool PrintedWarning = false; + if (!PrintedWarning) { + + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + + } + + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + + } + + for (size_t F = 0; F < kNumFlags; F++) { + + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + + if (FlagDescriptions[F].IntFlag) { + + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %d\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].UIntFlag) { + + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %u\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].StrFlag) { + + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) Printf("Flag: %s %s\n", Name, Str); + return true; + + } else { // Deprecated flag. + + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + + } + + } + + } + + Printf( + "\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", + Param); + return true; + +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const Vector &Args, + const ExternalFunctions * EF) { + + for (size_t F = 0; F < kNumFlags; F++) { + + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) *FlagDescriptions[F].StrFlag = nullptr; + + } + + // Disable len_control by default, if LLVMFuzzerCustomMutator is used. + if (EF->LLVMFuzzerCustomMutator) { + + Flags.len_control = 0; + Printf( + "INFO: found LLVMFuzzerCustomMutator (%p). " + "Disabling -len_control by default.\n", + EF->LLVMFuzzerCustomMutator); + + } + + Inputs = new Vector; + for (size_t A = 1; A < Args.size(); A++) { + + if (ParseOneFlag(Args[A].c_str())) { + + if (Flags.ignore_remaining_args) break; + continue; + + } + + Inputs->push_back(Args[A]); + + } + +} + +static std::mutex Mu; + +static void PulseThread() { + + while (true) { + + SleepSeconds(600); + std::lock_guard Lock(Mu); + Printf("pulse...\n"); + + } + +} + +static void WorkerThread(const Command &BaseCmd, std::atomic *Counter, + unsigned NumJobs, std::atomic *HasErrors) { + + while (true) { + + unsigned C = (*Counter)++; + if (C >= NumJobs) break; + std::string Log = "fuzz-" + std::to_string(C) + ".log"; + Command Cmd(BaseCmd); + Cmd.setOutputFile(Log); + Cmd.combineOutAndErr(); + if (Flags.verbosity) { + + std::string CommandLine = Cmd.toString(); + Printf("%s\n", CommandLine.c_str()); + + } + + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode != 0) *HasErrors = true; + std::lock_guard Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + + } + +} + +static void ValidateDirectoryExists(const std::string &Path, + bool CreateDirectory) { + + if (Path.empty()) { + + Printf("ERROR: Provided directory path is an empty string\n"); + exit(1); + + } + + if (IsDirectory(Path)) return; + + if (CreateDirectory) { + + if (!MkDirRecursive(Path)) { + + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + + } + + return; + + } + + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); + +} + +std::string CloneArgsWithoutX(const Vector &Args, const char *X1, + const char *X2) { + + std::string Cmd; + for (auto &S : Args) { + + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) continue; + Cmd += S + " "; + + } + + return Cmd; + +} + +static int RunInMultipleProcesses(const Vector &Args, + unsigned NumWorkers, unsigned NumJobs) { + + std::atomic Counter(0); + std::atomic HasErrors(false); + Command Cmd(Args); + Cmd.removeFlag("jobs"); + Cmd.removeFlag("workers"); + Vector V; + std::thread Pulse(PulseThread); + Pulse.detach(); + for (unsigned i = 0; i < NumWorkers; i++) + V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, + &HasErrors)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; + +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + + while (true) { + + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) F->RssLimitCallback(); + + } + +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); + +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) U.resize(MaxLen); + F->ExecuteCallback(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; + +} + +static bool AllInputsAreFiles() { + + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) return false; + return true; + +} + +static std::string GetDedupTokenFromCmdOutput(const std::string &S) { + + auto Beg = S.find("DEDUP_TOKEN:"); + if (Beg == std::string::npos) return ""; + auto End = S.find('\n', Beg); + if (End == std::string::npos) return ""; + return S.substr(Beg, End - Beg); + +} + +int CleanseCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1 || !Flags.exact_artifact_path) { + + Printf( + "ERROR: -cleanse_crash should be given one input file and" + " -exact_artifact_path\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + std::string OutputFilePath = Flags.exact_artifact_path; + Command Cmd(Args); + Cmd.removeFlag("cleanse_crash"); + + assert(Cmd.hasArgument(InputFilePath)); + Cmd.removeArgument(InputFilePath); + + auto TmpFilePath = TempPath("CleanseCrashInput", ".repro"); + Cmd.addArgument(TmpFilePath); + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + auto U = FileToVector(CurrentFilePath); + size_t Size = U.size(); + + const Vector ReplacementBytes = {' ', 0xff}; + for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { + + bool Changed = false; + for (size_t Idx = 0; Idx < Size; Idx++) { + + Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts, + Idx, Size); + uint8_t OriginalByte = U[Idx]; + if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(), + ReplacementBytes.end(), + OriginalByte)) + continue; + for (auto NewByte : ReplacementBytes) { + + U[Idx] = NewByte; + WriteToFile(U, TmpFilePath); + auto ExitCode = ExecuteCommand(Cmd); + RemoveFile(TmpFilePath); + if (!ExitCode) { + + U[Idx] = OriginalByte; + + } else { + + Changed = true; + Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte); + WriteToFile(U, OutputFilePath); + break; + + } + + } + + } + + if (!Changed) break; + + } + + return 0; + +} + +int MinimizeCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1) { + + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + Command BaseCmd(Args); + BaseCmd.removeFlag("minimize_crash"); + BaseCmd.removeFlag("exact_artifact_path"); + assert(BaseCmd.hasArgument(InputFilePath)); + BaseCmd.removeArgument(InputFilePath); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + + Printf( + "INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd.addFlag("max_total_time", "600"); + + } + + BaseCmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + while (true) { + + Unit U = FileToVector(CurrentFilePath); + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + Command Cmd(BaseCmd); + Cmd.addArgument(CurrentFilePath); + + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + std::string CmdOutput; + bool Success = ExecuteCommand(Cmd, &CmdOutput); + if (Success) { + + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + + } + + Printf( + "CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + auto DedupToken1 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken1.empty()) + Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); + + std::string ArtifactPath = + Flags.exact_artifact_path + ? Flags.exact_artifact_path + : Options.ArtifactPrefix + "minimized-from-" + Hash(U); + Cmd.addFlag("minimize_crash_internal_step", "1"); + Cmd.addFlag("exact_artifact_path", ArtifactPath); + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + CmdOutput.clear(); + Success = ExecuteCommand(Cmd, &CmdOutput); + Printf("%s", CmdOutput.c_str()); + if (Success) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + break; + + } + + auto DedupToken2 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken2.empty()) + Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); + + if (DedupToken1 != DedupToken2) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf( + "CRASH_MIN: mismatch in dedup tokens" + " (looks like a different bug). Won't minimize further\n"); + break; + + } + + CurrentFilePath = ArtifactPath; + Printf("*********************************\n"); + + } + + return 0; + +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + if (U.size() < 2) { + + Printf("INFO: The input is small enough, exiting\n"); + exit(0); + + } + + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; + +} + +void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector &Args, + const Vector &Corpora, const char *CFPathOrNull) { + + if (Corpora.size() < 2) { + + Printf("INFO: Merge requires two or more corpus dirs\n"); + exit(0); + + } + + Vector OldCorpus, NewCorpus; + GetSizedFilesFromDir(Corpora[0], &OldCorpus); + for (size_t i = 1; i < Corpora.size(); i++) + GetSizedFilesFromDir(Corpora[i], &NewCorpus); + std::sort(OldCorpus.begin(), OldCorpus.end()); + std::sort(NewCorpus.begin(), NewCorpus.end()); + + std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); + Vector NewFiles; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + {}, &NewCov, CFPath, true); + for (auto &Path : NewFiles) + F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); + // We are done, delete the control file if it was a temporary one. + if (!Flags.merge_control_file) RemoveFile(CFPath); + + exit(0); + +} + +int AnalyzeDictionary(Fuzzer *F, const Vector &Dict, UnitVector &Corpus) { + + Printf("Started dictionary minimization (up to %d tests)\n", + Dict.size() * Corpus.size() * 2); + + // Scores and usage count for each dictionary unit. + Vector Scores(Dict.size()); + Vector Usages(Dict.size()); + + Vector InitialFeatures; + Vector ModifiedFeatures; + for (auto &C : Corpus) { + + // Get coverage for the testcase without modifications. + F->ExecuteCallback(C.data(), C.size()); + InitialFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { InitialFeatures.push_back(Feature); }); + + for (size_t i = 0; i < Dict.size(); ++i) { + + Vector Data = C; + auto StartPos = + std::search(Data.begin(), Data.end(), Dict[i].begin(), Dict[i].end()); + // Skip dictionary unit, if the testcase does not contain it. + if (StartPos == Data.end()) continue; + + ++Usages[i]; + while (StartPos != Data.end()) { + + // Replace all occurrences of dictionary unit in the testcase. + auto EndPos = StartPos + Dict[i].size(); + for (auto It = StartPos; It != EndPos; ++It) + *It ^= 0xFF; + + StartPos = + std::search(EndPos, Data.end(), Dict[i].begin(), Dict[i].end()); + + } + + // Get coverage for testcase with masked occurrences of dictionary unit. + F->ExecuteCallback(Data.data(), Data.size()); + ModifiedFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { ModifiedFeatures.push_back(Feature); }); + + if (InitialFeatures == ModifiedFeatures) + --Scores[i]; + else + Scores[i] += 2; + + } + + } + + Printf("###### Useless dictionary elements. ######\n"); + for (size_t i = 0; i < Dict.size(); ++i) { + + // Dictionary units with positive score are treated as useful ones. + if (Scores[i] > 0) continue; + + Printf("\""); + PrintASCII(Dict[i].data(), Dict[i].size(), "\""); + Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); + + } + + Printf("###### End of useless dictionary elements. ######\n"); + return 0; + +} + +Vector ParseSeedInuts(const char *seed_inputs) { + + // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file + Vector Files; + if (!seed_inputs) return Files; + std::string SeedInputs; + if (Flags.seed_inputs[0] == '@') + SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. + else + SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. + if (SeedInputs.empty()) { + + Printf("seed_inputs is empty or @file does not exist.\n"); + exit(1); + + } + + // Parse SeedInputs. + size_t comma_pos = 0; + while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) { + + Files.push_back(SeedInputs.substr(comma_pos + 1)); + SeedInputs = SeedInputs.substr(0, comma_pos); + + } + + Files.push_back(SeedInputs); + return Files; + +} + +static Vector ReadCorpora( + const Vector &CorpusDirs, + const Vector &ExtraSeedFiles) { + + Vector SizedFiles; + size_t LastNumFiles = 0; + for (auto &Dir : CorpusDirs) { + + GetSizedFilesFromDir(Dir, &SizedFiles); + Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles, + Dir.c_str()); + LastNumFiles = SizedFiles.size(); + + } + + for (auto &File : ExtraSeedFiles) + if (auto Size = FileSize(File)) SizedFiles.push_back({File, Size}); + return SizedFiles; + +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + std::string Argv0((*argv)[0]); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + const Vector Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + if (Argv0 != *ProgName) { + + Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); + exit(1); + + } + + ParseFlags(Args, EF); + if (Flags.help) { + + PrintHelp(); + return 0; + + } + + if (Flags.close_fd_mask & 2) DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) Printf("Running %u workers\n", Flags.workers); + + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.LenControl = Flags.len_control; + Options.KeepSeed = Flags.keep_seed; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.IgnoreTimeouts = Flags.ignore_timeouts; + Options.IgnoreOOMs = Flags.ignore_ooms; + Options.IgnoreCrashes = Flags.ignore_crashes; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.CrossOverUniformDist = Flags.cross_over_uniform_dist; + Options.MutateDepth = Flags.mutate_depth; + Options.ReduceDepth = Flags.reduce_depth; + Options.UseCounters = Flags.use_counters; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ReduceInputs = Flags.reduce_inputs; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.DetectLeaks = Flags.detect_leaks; + Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + Options.MallocLimitMb = Flags.malloc_limit_mb; + if (!Options.MallocLimitMb) Options.MallocLimitMb = Options.RssLimitMb; + if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) { + + // Ensure output corpus assumed to be the first arbitrary argument input + // is not a path to an existing file. + std::string OutputCorpusDir = (*Inputs)[0]; + if (!IsFile(OutputCorpusDir)) { + + Options.OutputCorpus = OutputCorpusDir; + ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs); + + } + + } + + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) { + + Options.ArtifactPrefix = Flags.artifact_prefix; + + // Since the prefix could be a full path to a file name prefix, assume + // that if the path ends with the platform's separator that a directory + // is desired + std::string ArtifactPathDir = Options.ArtifactPrefix; + if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { + + ArtifactPathDir = DirName(ArtifactPathDir); + + } + + ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs); + + } + + if (Flags.exact_artifact_path) { + + Options.ExactArtifactPath = Flags.exact_artifact_path; + ValidateDirectoryExists(DirName(Options.ExactArtifactPath), + Flags.create_missing_dirs); + + } + + Vector Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool RunIndividualFiles = AllInputsAreFiles(); + Options.SaveArtifacts = + !RunIndividualFiles || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintNewCovFuncs = Flags.print_funcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) Options.ExitOnItem = Flags.exit_on_item; + if (Flags.focus_function) Options.FocusFunction = Flags.focus_function; + if (Flags.data_flow_trace) Options.DataFlowTrace = Flags.data_flow_trace; + if (Flags.features_dir) { + + Options.FeaturesDir = Flags.features_dir; + ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs); + + } + + if (Flags.mutation_graph_file) + Options.MutationGraphFile = Flags.mutation_graph_file; + if (Flags.collect_data_flow) + Options.CollectDataFlow = Flags.collect_data_flow; + if (Flags.stop_file) Options.StopFile = Flags.stop_file; + Options.Entropic = Flags.entropic; + Options.EntropicFeatureFrequencyThreshold = + (size_t)Flags.entropic_feature_frequency_threshold; + Options.EntropicNumberOfRarestFeatures = + (size_t)Flags.entropic_number_of_rarest_features; + Options.EntropicScalePerExecTime = Flags.entropic_scale_per_exec_time; + if (Options.Entropic) { + + if (!Options.FocusFunction.empty()) { + + Printf( + "ERROR: The parameters `--entropic` and `--focus_function` cannot " + "be used together.\n"); + exit(1); + + } + + Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", + Options.EntropicFeatureFrequencyThreshold, + Options.EntropicNumberOfRarestFeatures); + + } + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + Entropic.FeatureFrequencyThreshold = + Options.EntropicFeatureFrequencyThreshold; + Entropic.NumberOfRarestFeatures = Options.EntropicNumberOfRarestFeatures; + Entropic.ScalePerExecTime = Options.EntropicScalePerExecTime; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = + std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); + if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); + + if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { + + if (RunIndividualFiles) + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora({}, *Inputs)); + else + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora(*Inputs, {})); + + } + + Random Rand(Seed); + auto * MD = new MutationDispatcher(Rand, Options); + auto * Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto * F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U : Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + // Threads are only supported by Chrome. Don't use them with emscripten + // for now. +#if !LIBFUZZER_EMSCRIPTEN + StartRssThread(F, Flags.rss_limit_mb); +#endif // LIBFUZZER_EMSCRIPTEN + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleAlrm = !Flags.minimize_crash; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + Options.HandleXfsz = Flags.handle_xfsz; + Options.HandleUsr1 = Flags.handle_usr1; + Options.HandleUsr2 = Flags.handle_usr2; + SetSignalHandler(Options); + + std::atexit(Fuzzer::StaticExitCallback); + + if (Flags.minimize_crash) return MinimizeCrashInput(Args, Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (Flags.cleanse_crash) return CleanseCrashInput(Args, Options); + + if (RunIndividualFiles) { + + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + + } + + Printf( + "***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + + } + + if (Flags.fork) + FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); + + if (Flags.merge) Merge(F, Options, Args, *Inputs, Flags.merge_control_file); + + if (Flags.merge_inner) { + + const size_t kDefaultMaxMergeLen = 1 << 20; + if (Options.MaxLen == 0) F->SetMaxInputLen(kDefaultMaxMergeLen); + assert(Flags.merge_control_file); + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + exit(0); + + } + + if (Flags.analyze_dict) { + + size_t MaxLen = INT_MAX; // Large max length. + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, MaxLen, + /*ExitOnError=*/false); + + } + + if (Dictionary.empty() || Inputs->empty()) { + + Printf("ERROR: can't analyze dict without dict and corpus provided\n"); + return 1; + + } + + if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { + + Printf("Dictionary analysis failed\n"); + exit(1); + + } + + Printf("Dictionary analysis succeeded\n"); + exit(0); + + } + + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); + F->Loop(CorporaFiles); + + if (Flags.verbosity) + Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. + +} + +extern "C" ATTRIBUTE_INTERFACE int LLVMFuzzerRunDriver( + int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)) { + + return FuzzerDriver(argc, argv, UserCb); + +} + +#include "libfuzzer.inc" + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.def b/custom_mutators/libfuzzer/FuzzerExtFunctions.def new file mode 100644 index 00000000..51edf844 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.def @@ -0,0 +1,50 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(, , , ) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); +EXT_FUNC(__sanitizer_purge_allocator, void, (), false); +EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false); +EXT_FUNC(__msan_unpoison_param, void, (size_t n), false); diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.h b/custom_mutators/libfuzzer/FuzzerExtFunctions.h new file mode 100644 index 00000000..c88aac4e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.h @@ -0,0 +1,34 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include +#include + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 00000000..8009b237 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,60 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + +using namespace fuzzer; + +template +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + + if (WarnIfMissing) { + + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + + } + + } + + return reinterpret_cast(Fn); + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(#NAME, WARN) + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 00000000..c7a1d05e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,63 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +extern "C" { + + // Declare these symbols as weak to allow them to be optionally defined. + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + + if (FnPtr == nullptr && WarnIfMissing) { + + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + + } + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr(reinterpret_cast(reinterpret_cast(::NAME)), \ + #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp new file mode 100644 index 00000000..a727220a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp @@ -0,0 +1,95 @@ +//=== FuzzerExtWindows.cpp - Interface to external functions --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when +// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately +// the method each compiler supports is not supported by the other. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +using namespace fuzzer; + + // Intermediate macro to ensure the parameter is expanded before stringified. + #define STRINGIFY_(A) #A + #define STRINGIFY(A) STRINGIFY_(A) + + #if LIBFUZZER_MSVC + // Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h + #if defined(_M_IX86) || defined(__i386__) + #define WIN_SYM_PREFIX "_" + #else + #define WIN_SYM_PREFIX + #endif + + // Declare external functions as having alternativenames, so that we can + // determine if they are not defined. + #define EXTERNAL_FUNC(Name, Default) \ + __pragma( \ + comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ + Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) + #else + // Declare external functions as weak to allow them to default to a + // specified function if not defined explicitly. We must use weak symbols + // because clang's support for alternatename is not 100%, see + // https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. + #define EXTERNAL_FUNC(Name, Default) \ + __attribute__((weak, alias(STRINGIFY(Default)))) + #endif // LIBFUZZER_MSVC + +extern "C" { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + \ + } \ + EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +template +static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { + + if (Fun == FunDef) { + + if (WarnIfMissing) + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + return nullptr; + + } + + return Fun; + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(::NAME, ::NAME##Def, #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 00000000..3ff9b0d5 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,71 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#include + +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN +__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; +__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; + +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return &__start___libfuzzer_extra_counters; + +} + +uint8_t *ExtraCountersEnd() { + + return &__stop___libfuzzer_extra_counters; + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { // hand-written memset, don't asan-ify. + uintptr_t *Beg = reinterpret_cast(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast(ExtraCountersEnd()); + for (; Beg < End; Beg++) { + + *Beg = 0; + __asm__ __volatile__("" : : : "memory"); + + } + +} + +} // namespace fuzzer + +#else +// TODO: implement for other platforms. +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return nullptr; + +} + +uint8_t *ExtraCountersEnd() { + + return nullptr; + +} + +void ClearExtraCounters() { + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerFlags.def b/custom_mutators/libfuzzer/FuzzerFlags.def new file mode 100644 index 00000000..c9a787e0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFlags.def @@ -0,0 +1,197 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG_INT(runs, -1, + "Number of individual test runs (-1 for infinite runs).") +FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it. ") +FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, " + "then try larger inputs over time. Specifies the rate at which the length " + "limit is increased (smaller == faster). If 0, immediately try inputs with " + "size up to max_len. Default value is 0, if LLVMFuzzerCustomMutator is used.") +FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " + "to use as an additional seed corpus. Alternatively, an \"@\" followed by " + "the name of a file containing the comma-separated list.") +FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if " + "they do not produce new coverage. When used with |reduce_inputs==1|, the " + "seed inputs will never be reduced. This option can be useful when seeds are" + "not properly formed for the fuzz target but still have useful snippets.") +FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a " + "uniform probability distribution when choosing inputs to cross over with. " + "Some of the inputs in the corpus may never get chosen for mutation " + "depending on the input mutation scheduling policy. With this flag, all " + "inputs, regardless of the input mutation scheduling policy, can be chosen " + "as an input to cross over with. This can be particularly useful with " + "|keep_seed==1|; all the initial seed inputs, even though they do not " + "increase coverage because they are not properly formed, will still be " + "chosen as an input to cross over with.") + +FUZZER_FLAG_INT(mutate_depth, 5, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. " + "Reduce depth if mutations lose unique features") +FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup") +FUZZER_FLAG_INT(prefer_small, 1, + "If 1, always prefer smaller inputs during the corpus shuffle.") +FUZZER_FLAG_INT( + timeout, 1200, + "Timeout in seconds (if positive). " + "If one unit runs more than this number of seconds the process will abort.") +FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug " + "this exit code will be used.") +FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout " + "this exit code will be used.") +FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " + "time in seconds to run the fuzzer.") +FUZZER_FLAG_INT(help, 0, "Print help.") +FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens " + "in a subprocess") +FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode") +FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode") +FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode") +FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Only interesting units will be taken. " + "This flag can be used to minimize a corpus.") +FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists") +FUZZER_FLAG_STRING(merge_inner, "internal flag") +FUZZER_FLAG_STRING(merge_control_file, + "Specify a control file used for the merge process. " + "If a merge process gets killed it tries to leave this file " + "in a state suitable for resuming the merge. " + "By default a temporary file will be used." + "The same file can be used for multistep merge process.") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts." + " Use with -exact_artifact_path to specify the output." + " Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that" + " the minimized input triggers the same crash." + ) +FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided" + " crash input to make it contain fewer original bytes." + " Use with -exact_artifact_path to specify the output." + ) +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") +FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk." + "Every time a new input is added to the corpus, a corresponding file in the features_dir" + " is created containing the unique features of that input." + " Features are stored in binary format.") +FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to" + " mutation_graph_file. The graph contains a vertex for each input that has" + " unique coverage; directed edges are provided between parents and children" + " where the child has unique coverage, and are recorded with the type of" + " mutation that caused the child.") +FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") +FUZZER_FLAG_INT(use_memmem, 1, + "Use hints from intercepting memmem, strstr, etc") +FUZZER_FLAG_INT(use_value_profile, 0, + "Experimental. Use value profile to guide fuzzing.") +FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") +FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.") +FUZZER_FLAG_INT(reduce_inputs, 1, + "Try to reduce the size of inputs while preserving their full feature sets") +FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" + " this number of jobs in separate worker processes" + " with stdout/stderr redirected to fuzz-JOB.log.") +FUZZER_FLAG_UNSIGNED(workers, 0, + "Number of simultaneous worker processes to run the jobs." + " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.") +FUZZER_FLAG_INT(reload, 1, + "Reload the main corpus every seconds to get new units" + " discovered by other processes. If 0, disabled") +FUZZER_FLAG_INT(report_slow_units, 10, + "Report slowest units if they run for more than this number of seconds.") +FUZZER_FLAG_INT(only_ascii, 0, + "If 1, generate only ASCII (isprint+isspace) inputs.") +FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.") +FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " + "timeout, or slow inputs) as " + "$(artifact_prefix)file") +FUZZER_FLAG_STRING(exact_artifact_path, + "Write the single artifact on failure (crash, timeout) " + "as $(exact_artifact_path). This overrides -artifact_prefix " + "and will not use checksum in the file name. Do not " + "use the same path for several parallel processes.") +FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of " + "newly covered functions.") +FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(print_corpus_stats, 0, + "If 1, print statistics on corpus elements at exit.") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" + " at exit.") +FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") +FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.") +FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.") +FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.") +FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " + "if 2, close stderr; if 3, close both. " + "Be careful, this will also close e.g. stderr of asan.") +FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled " + "try to detect memory leaks during fuzzing (i.e. not only at shut down).") +FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and " + "quarantines every seconds. When rss_limit_mb is specified (>0), " + "purging starts when RSS exceeds 50% of rss_limit_mb. Pass " + "purge_allocator_interval=-1 to disable this functionality.") +FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " + "If >= 2 will also print stack traces.") +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" + "reaching this limit of RSS memory usage.") +FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit " + "if the target tries to allocate this number of Mb with one malloc call. " + "If zero (default) same limit as rss_limit_mb is applied.") +FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates" + " from the given source location. Example: -exit_on_src_pos=foo.cc:123. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" + " was added to the corpus. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed " + "after this one. Useful for fuzzers that need to do their own " + "argument parsing.") +FUZZER_FLAG_STRING(focus_function, "Experimental. " + "Fuzzing will focus on inputs that trigger calls to this function. " + "If -focus_function=auto and -data_flow_trace is used, libFuzzer " + "will choose the focus functions automatically.") +FUZZER_FLAG_INT(entropic, 0, "Experimental. Enables entropic power schedule.") +FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If " + "entropic is enabled, all features which are observed less often than " + "the specified value are considered as rare.") +FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If " + "entropic is enabled, we keep track of the frequencies only for the " + "Top-X least abundant features (union features that are considered as " + "rare).") +FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, " + "the Entropic power schedule gets scaled based on the input execution " + "time. Inputs with lower execution time get scheduled more (up to 30x). " + "Note that, if 1, fuzzer stops from being deterministic even if a " + "non-zero random seed is given.") + +FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") +FUZZER_DEPRECATED_FLAG(use_clang_coverage) +FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_STRING(collect_data_flow, + "Experimental: collect the data flow trace") + +FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create " + "directories for arguments that would normally expect them to already " + "exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)") diff --git a/custom_mutators/libfuzzer/FuzzerFork.cpp b/custom_mutators/libfuzzer/FuzzerFork.cpp new file mode 100644 index 00000000..d6ffed74 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.cpp @@ -0,0 +1,501 @@ +//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Spawn and orchestrate separate fuzzing processes. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct Stats { + + size_t number_of_executed_units = 0; + size_t peak_rss_mb = 0; + size_t average_exec_per_sec = 0; + +}; + +static Stats ParseFinalStatsFromLog(const std::string &LogPath) { + + std::ifstream In(LogPath); + std::string Line; + Stats Res; + struct { + + const char *Name; + size_t * Var; + + } NameVarPairs[] = { + + {"stat::number_of_executed_units:", &Res.number_of_executed_units}, + {"stat::peak_rss_mb:", &Res.peak_rss_mb}, + {"stat::average_exec_per_sec:", &Res.average_exec_per_sec}, + {nullptr, nullptr}, + + }; + + while (std::getline(In, Line, '\n')) { + + if (Line.find("stat::") != 0) continue; + std::istringstream ISS(Line); + std::string Name; + size_t Val; + ISS >> Name >> Val; + for (size_t i = 0; NameVarPairs[i].Name; i++) + if (Name == NameVarPairs[i].Name) *NameVarPairs[i].Var = Val; + + } + + return Res; + +} + +struct FuzzJob { + + // Inputs. + Command Cmd; + std::string CorpusDir; + std::string FeaturesDir; + std::string LogPath; + std::string SeedListPath; + std::string CFPath; + size_t JobId; + + int DftTimeInSeconds = 0; + + // Fuzzing Outputs. + int ExitCode; + + ~FuzzJob() { + + RemoveFile(CFPath); + RemoveFile(LogPath); + RemoveFile(SeedListPath); + RmDirRecursive(CorpusDir); + RmDirRecursive(FeaturesDir); + + } + +}; + +struct GlobalEnv { + + Vector Args; + Vector CorpusDirs; + std::string MainCorpusDir; + std::string TempDir; + std::string DFTDir; + std::string DataFlowBinary; + Set Features, Cov; + Set FilesWithDFT; + Vector Files; + Random * Rand; + std::chrono::system_clock::time_point ProcessStartTime; + int Verbosity = 0; + + size_t NumTimeouts = 0; + size_t NumOOMs = 0; + size_t NumCrashes = 0; + + size_t NumRuns = 0; + + std::string StopFile() { + + return DirPlusFile(TempDir, "STOP"); + + } + + size_t secondsSinceProcessStartUp() const { + + return std::chrono::duration_cast( + std::chrono::system_clock::now() - ProcessStartTime) + .count(); + + } + + FuzzJob *CreateNewJob(size_t JobId) { + + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.removeFlag("collect_data_flow"); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload. + Cmd.addFlag("print_final_stats", "1"); + Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing. + Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId))); + Cmd.addFlag("stop_file", StopFile()); + if (!DataFlowBinary.empty()) { + + Cmd.addFlag("data_flow_trace", DFTDir); + if (!Cmd.hasFlag("focus_function")) Cmd.addFlag("focus_function", "auto"); + + } + + auto Job = new FuzzJob; + std::string Seeds; + if (size_t CorpusSubsetSize = + std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) { + + auto Time1 = std::chrono::system_clock::now(); + for (size_t i = 0; i < CorpusSubsetSize; i++) { + + auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + + } + + auto Time2 = std::chrono::system_clock::now(); + Job->DftTimeInSeconds = duration_cast(Time2 - Time1).count(); + + } + + if (!Seeds.empty()) { + + Job->SeedListPath = + DirPlusFile(TempDir, std::to_string(JobId) + ".seeds"); + WriteToFile(Seeds, Job->SeedListPath); + Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath); + + } + + Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log"); + Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId)); + Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId)); + Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge"); + Job->JobId = JobId; + + Cmd.addArgument(Job->CorpusDir); + Cmd.addFlag("features_dir", Job->FeaturesDir); + + for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) { + + RmDirRecursive(D); + MkDir(D); + + } + + Cmd.setOutputFile(Job->LogPath); + Cmd.combineOutAndErr(); + + Job->Cmd = Cmd; + + if (Verbosity >= 2) + Printf("Job %zd/%p Created: %s\n", JobId, Job, + Job->Cmd.toString().c_str()); + // Start from very short runs and gradually increase them. + return Job; + + } + + void RunOneMergeJob(FuzzJob *Job) { + + auto Stats = ParseFinalStatsFromLog(Job->LogPath); + NumRuns += Stats.number_of_executed_units; + + Vector TempFiles, MergeCandidates; + // Read all newly created inputs and their feature sets. + // Choose only those inputs that have new features. + GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); + std::sort(TempFiles.begin(), TempFiles.end()); + for (auto &F : TempFiles) { + + auto FeatureFile = F.File; + FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir); + auto FeatureBytes = FileToVector(FeatureFile, 0, false); + assert((FeatureBytes.size() % sizeof(uint32_t)) == 0); + Vector NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); + memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size()); + for (auto Ft : NewFeatures) { + + if (!Features.count(Ft)) { + + MergeCandidates.push_back(F); + break; + + } + + } + + } + + // if (!FilesToAdd.empty() || Job->ExitCode != 0) + Printf( + "#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd " + "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n", + NumRuns, Cov.size(), Features.size(), Files.size(), + Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, + secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); + + if (MergeCandidates.empty()) return; + + Vector FilesToAdd; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, + &NewFeatures, Cov, &NewCov, Job->CFPath, false); + for (auto &Path : FilesToAdd) { + + auto U = FileToVector(Path); + auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); + WriteToFile(U, NewPath); + Files.push_back(NewPath); + + } + + Features.insert(NewFeatures.begin(), NewFeatures.end()); + Cov.insert(NewCov.begin(), NewCov.end()); + for (auto Idx : NewCov) + if (auto *TE = TPC.PCTableEntryByIdx(Idx)) + if (TPC.PcIsFuncEntry(TE)) + PrintPC(" NEW_FUNC: %p %F %L\n", "", + TPC.GetNextInstructionPc(TE->PC)); + + } + + void CollectDFT(const std::string &InputPath) { + + if (DataFlowBinary.empty()) return; + if (!FilesWithDFT.insert(InputPath).second) return; + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.addFlag("data_flow_trace", DFTDir); + Cmd.addArgument(InputPath); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log")); + Cmd.combineOutAndErr(); + // Printf("CollectDFT: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + +}; + +struct JobQueue { + + std::queue Qu; + std::mutex Mu; + std::condition_variable Cv; + + void Push(FuzzJob *Job) { + + { + + std::lock_guard Lock(Mu); + Qu.push(Job); + + } + + Cv.notify_one(); + + } + + FuzzJob *Pop() { + + std::unique_lock Lk(Mu); + // std::lock_guard Lock(Mu); + Cv.wait(Lk, [&] { return !Qu.empty(); }); + assert(!Qu.empty()); + auto Job = Qu.front(); + Qu.pop(); + return Job; + + } + +}; + +void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { + + while (auto Job = FuzzQ->Pop()) { + + // Printf("WorkerThread: job %p\n", Job); + Job->ExitCode = ExecuteCommand(Job->Cmd); + MergeQ->Push(Job); + + } + +} + +// This is just a skeleton of an experimental -fork=1 feature. +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs) { + + Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); + + GlobalEnv Env; + Env.Args = Args; + Env.CorpusDirs = CorpusDirs; + Env.Rand = &Rand; + Env.Verbosity = Options.Verbosity; + Env.ProcessStartTime = std::chrono::system_clock::now(); + Env.DataFlowBinary = Options.CollectDataFlow; + + Vector SeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &SeedFiles); + std::sort(SeedFiles.begin(), SeedFiles.end()); + Env.TempDir = TempPath("FuzzWithFork", ".dir"); + Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); + RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs. + MkDir(Env.TempDir); + MkDir(Env.DFTDir); + + if (CorpusDirs.empty()) + MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C")); + else + Env.MainCorpusDir = CorpusDirs[0]; + + if (Options.KeepSeed) { + + for (auto &File : SeedFiles) + Env.Files.push_back(File.File); + + } else { + + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, CFPath, false); + RemoveFile(CFPath); + + } + + Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, + Env.Files.size(), Env.TempDir.c_str()); + + int ExitCode = 0; + + JobQueue FuzzQ, MergeQ; + + auto StopJobs = [&]() { + + for (int i = 0; i < NumJobs; i++) + FuzzQ.Push(nullptr); + MergeQ.Push(nullptr); + WriteToFile(Unit({1}), Env.StopFile()); + + }; + + size_t JobId = 1; + Vector Threads; + for (int t = 0; t < NumJobs; t++) { + + Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ)); + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + while (true) { + + std::unique_ptr Job(MergeQ.Pop()); + if (!Job) break; + ExitCode = Job->ExitCode; + if (ExitCode == Options.InterruptExitCode) { + + Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid()); + StopJobs(); + break; + + } + + Fuzzer::MaybeExitGracefully(); + + Env.RunOneMergeJob(Job.get()); + + // Continue if our crash is one of the ignorred ones. + if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) + Env.NumTimeouts++; + else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode) + Env.NumOOMs++; + else if (ExitCode != 0) { + + Env.NumCrashes++; + if (Options.IgnoreCrashes) { + + std::ifstream In(Job->LogPath); + std::string Line; + while (std::getline(In, Line, '\n')) + if (Line.find("ERROR:") != Line.npos || + Line.find("runtime error:") != Line.npos) + Printf("%s\n", Line.c_str()); + + } else { + + // And exit if we don't ignore this crash. + Printf("INFO: log from the inner process:\n%s", + FileToString(Job->LogPath).c_str()); + StopJobs(); + break; + + } + + } + + // Stop if we are over the time budget. + // This is not precise, since other threads are still running + // and we will wait while joining them. + // We also don't stop instantly: other jobs need to finish. + if (Options.MaxTotalTimeSec > 0 && + Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) { + + Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n", + Env.secondsSinceProcessStartUp()); + StopJobs(); + break; + + } + + if (Env.NumRuns >= Options.MaxNumberOfRuns) { + + Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n", + Env.NumRuns); + StopJobs(); + break; + + } + + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + for (auto &T : Threads) + T.join(); + + // The workers have terminated. Don't try to remove the directory before they + // terminate to avoid a race condition preventing cleanup on Windows. + RmDirRecursive(Env.TempDir); + + // Use the exit code from the last child process. + Printf("INFO: exiting: %d time: %zds\n", ExitCode, + Env.secondsSinceProcessStartUp()); + exit(ExitCode); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerFork.h b/custom_mutators/libfuzzer/FuzzerFork.h new file mode 100644 index 00000000..b29a43e1 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.h @@ -0,0 +1,24 @@ +//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_FORK_H +#define LLVM_FUZZER_FORK_H + +#include "FuzzerDefs.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +#include + +namespace fuzzer { +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs); +} // namespace fuzzer + +#endif // LLVM_FUZZER_FORK_H diff --git a/custom_mutators/libfuzzer/FuzzerIO.cpp b/custom_mutators/libfuzzer/FuzzerIO.cpp new file mode 100644 index 00000000..e0c15db4 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.cpp @@ -0,0 +1,248 @@ +//===- FuzzerIO.cpp - IO utils. -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static FILE *OutputFile = stderr; + +long GetEpoch(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; // Can't stat, be conservative. + return St.st_mtime; + +} + +Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { + + std::ifstream T(Path, std::ios::binary); + if (ExitOnError && !T) { + + Printf("No such directory: %s; exiting\n", Path.c_str()); + exit(1); + + } + + T.seekg(0, T.end); + auto EndPos = T.tellg(); + if (EndPos < 0) return {}; + size_t FileLen = EndPos; + if (MaxSize) FileLen = std::min(FileLen, MaxSize); + + T.seekg(0, T.beg); + Unit Res(FileLen); + T.read(reinterpret_cast(Res.data()), FileLen); + return Res; + +} + +std::string FileToString(const std::string &Path) { + + std::ifstream T(Path, std::ios::binary); + return std::string((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); + +} + +void CopyFileToErr(const std::string &Path) { + + Printf("%s", FileToString(Path).c_str()); + +} + +void WriteToFile(const Unit &U, const std::string &Path) { + + WriteToFile(U.data(), U.size(), Path); + +} + +void WriteToFile(const std::string &Data, const std::string &Path) { + + WriteToFile(reinterpret_cast(Data.c_str()), Data.size(), + Path); + +} + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + // Use raw C interface because this function may be called from a sig handler. + FILE *Out = fopen(Path.c_str(), "wb"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void AppendToFile(const std::string &Data, const std::string &Path) { + + AppendToFile(reinterpret_cast(Data.data()), Data.size(), + Path); + +} + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + FILE *Out = fopen(Path.c_str(), "a"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, long *Epoch, + size_t MaxSize, bool ExitOnError) { + + long E = Epoch ? *Epoch : 0; + Vector Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/ true); + size_t NumLoaded = 0; + for (size_t i = 0; i < Files.size(); i++) { + + auto &X = Files[i]; + if (Epoch && GetEpoch(X) < E) continue; + NumLoaded++; + if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) + Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); + auto S = FileToVector(X, MaxSize, ExitOnError); + if (!S.empty()) V->push_back(S); + + } + +} + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V) { + + Vector Files; + ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/ true); + for (auto &File : Files) + if (size_t Size = FileSize(File)) V->push_back({File, Size}); + +} + +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName) { + + return DirPath + GetSeparator() + FileName; + +} + +void DupAndCloseStderr() { + + int OutputFd = DuplicateFile(2); + if (OutputFd >= 0) { + + FILE *NewOutputFile = OpenFile(OutputFd, "w"); + if (NewOutputFile) { + + OutputFile = NewOutputFile; + if (EF->__sanitizer_set_report_fd) + EF->__sanitizer_set_report_fd( + reinterpret_cast(GetHandleFromFd(OutputFd))); + DiscardOutput(2); + + } + + } + +} + +void CloseStdout() { + + DiscardOutput(1); + +} + +void Printf(const char *Fmt, ...) { + + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +void VPrintf(bool Verbose, const char *Fmt, ...) { + + if (!Verbose) return; + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +static bool MkDirRecursiveInner(const std::string &Leaf) { + + // Prevent chance of potential infinite recursion + if (Leaf == ".") return true; + + const std::string &Dir = DirName(Leaf); + + if (IsDirectory(Dir)) { + + MkDir(Leaf); + return IsDirectory(Leaf); + + } + + bool ret = MkDirRecursiveInner(Dir); + if (!ret) { + + // Give up early if a previous MkDir failed + return ret; + + } + + MkDir(Leaf); + return IsDirectory(Leaf); + +} + +bool MkDirRecursive(const std::string &Dir) { + + if (Dir.empty()) return false; + + if (IsDirectory(Dir)) return true; + + return MkDirRecursiveInner(Dir); + +} + +void RmDirRecursive(const std::string &Dir) { + + IterateDirRecursive( + Dir, [](const std::string &Path) {}, + [](const std::string &Path) { RmDir(Path); }, + [](const std::string &Path) { RemoveFile(Path); }); + +} + +std::string TempPath(const char *Prefix, const char *Extension) { + + return DirPlusFile(TmpDir(), std::string("libFuzzerTemp.") + Prefix + + std::to_string(GetPid()) + Extension); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerIO.h b/custom_mutators/libfuzzer/FuzzerIO.h new file mode 100644 index 00000000..abd25110 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.h @@ -0,0 +1,112 @@ +//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO interface. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IO_H +#define LLVM_FUZZER_IO_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +long GetEpoch(const std::string &Path); + +Unit FileToVector(const std::string &Path, size_t MaxSize = 0, + bool ExitOnError = true); + +std::string FileToString(const std::string &Path); + +void CopyFileToErr(const std::string &Path); + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path); +// Write Data.c_str() to the file without terminating null character. +void WriteToFile(const std::string &Data, const std::string &Path); +void WriteToFile(const Unit &U, const std::string &Path); + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path); +void AppendToFile(const std::string &Data, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError); + +// Returns "Dir/FileName" or equivalent for the current OS. +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName); + +// Returns the name of the dir, similar to the 'dirname' utility. +std::string DirName(const std::string &FileName); + +// Returns path to a TmpDir. +std::string TmpDir(); + +std::string TempPath(const char *Prefix, const char *Extension); + +bool IsInterestingCoverageFile(const std::string &FileName); + +void DupAndCloseStderr(); + +void CloseStdout(); + +void Printf(const char *Fmt, ...); +void VPrintf(bool Verbose, const char *Fmt, ...); + +// Print using raw syscalls, useful when printing at early init stages. +void RawPrint(const char *Str); + +// Platform specific functions: +bool IsFile(const std::string &Path); +bool IsDirectory(const std::string &Path); +size_t FileSize(const std::string &Path); + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir); + +bool MkDirRecursive(const std::string &Dir); +void RmDirRecursive(const std::string &Dir); + +// Iterate files and dirs inside Dir, recursively. +// Call DirPreCallback/DirPostCallback on dirs before/after +// calling FileCallback on files. +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)); + +struct SizedFile { + std::string File; + size_t Size; + bool operator<(const SizedFile &B) const { return Size < B.Size; } +}; + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V); + +char GetSeparator(); +bool IsSeparator(char C); +// Similar to the basename utility: returns the file name w/o the dir prefix. +std::string Basename(const std::string &Path); + +FILE* OpenFile(int Fd, const char *Mode); + +int CloseFile(int Fd); + +int DuplicateFile(int Fd); + +void RemoveFile(const std::string &Path); +void RenameFile(const std::string &OldPath, const std::string &NewPath); + +intptr_t GetHandleFromFd(int fd); + +void MkDir(const std::string &Path); +void RmDir(const std::string &Path); + +const std::string &getDevNull(); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_IO_H diff --git a/custom_mutators/libfuzzer/FuzzerIOPosix.cpp b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp new file mode 100644 index 00000000..36ec5a9c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp @@ -0,0 +1,223 @@ +//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +bool IsFile(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISREG(St.st_mode); + +} + +bool IsDirectory(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISDIR(St.st_mode); + +} + +size_t FileSize(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; + return St.st_size; + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.rfind(GetSeparator()); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + DIR *D = opendir(Dir.c_str()); + if (!D) { + + Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str()); + exit(1); + + } + + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + V->push_back(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); + + } + + closedir(D); + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + DirPreCallback(Dir); + DIR *D = opendir(Dir.c_str()); + if (!D) return; + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + FileCallback(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } + + closedir(D); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '/'; + +} + +bool IsSeparator(char C) { + + return C == '/'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return fdopen(Fd, Mode); + +} + +int CloseFile(int fd) { + + return close(fd); + +} + +int DuplicateFile(int Fd) { + + return dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return static_cast(fd); + +} + +std::string DirName(const std::string &FileName) { + + char *Tmp = new char[FileName.size() + 1]; + memcpy(Tmp, FileName.c_str(), FileName.size() + 1); + std::string Res = dirname(Tmp); + delete[] Tmp; + return Res; + +} + +std::string TmpDir() { + + if (auto Env = getenv("TMPDIR")) return Env; + return "/tmp"; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (FileName.find("/usr/lib/") != std::string::npos) return false; + if (FileName.find("/usr/include/") != std::string::npos) return false; + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + mkdir(Path.c_str(), 0700); + +} + +void RmDir(const std::string &Path) { + + rmdir(Path.c_str()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "/dev/null"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerIOWindows.cpp b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp new file mode 100644 index 00000000..9352984a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp @@ -0,0 +1,513 @@ +//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { + + if (FileAttributes & FILE_ATTRIBUTE_NORMAL) return true; + + if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false; + + HANDLE FileHandle(CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + + if (FileHandle == INVALID_HANDLE_VALUE) { + + Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + return false; + + } + + DWORD FileType = GetFileType(FileHandle); + + if (FileType == FILE_TYPE_UNKNOWN) { + + Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + CloseHandle(FileHandle); + return false; + + } + + if (FileType != FILE_TYPE_DISK) { + + CloseHandle(FileHandle); + return false; + + } + + CloseHandle(FileHandle); + return true; + +} + +bool IsFile(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsFile(Path, Att); + +} + +static bool IsDir(DWORD FileAttrs) { + + if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false; + return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; + +} + +bool IsDirectory(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsDir(Att); + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.find_last_of("/\\"); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +size_t FileSize(const std::string &Path) { + + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) + Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), LastError); + return 0; + + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + return size.QuadPart; + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + std::string Path(Dir); + assert(!Path.empty()); + if (Path.back() != '\\') Path.push_back('\\'); + Path.push_back('*'); + + // Get the first directory entry. + WIN32_FIND_DATAA FindInfo; + HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); + if (FindHandle == INVALID_HANDLE_VALUE) { + + if (GetLastError() == ERROR_FILE_NOT_FOUND) return; + Printf("No such file or directory: %s; exiting\n", Dir.c_str()); + exit(1); + + } + + do { + + std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); + + if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + + size_t FilenameLen = strlen(FindInfo.cFileName); + if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || + (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && + FindInfo.cFileName[1] == '.')) + continue; + + ListFilesInDirRecursive(FileName, Epoch, V, false); + + } else if (IsFile(FileName, FindInfo.dwFileAttributes)) + + V->push_back(FileName); + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed (Error code: %lu).\n", LastError); + + FindClose(FindHandle); + + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + // TODO(metzman): Implement ListFilesInDirRecursive via this function. + DirPreCallback(Dir); + + DWORD DirAttrs = GetFileAttributesA(Dir.c_str()); + if (!IsDir(DirAttrs)) return; + + std::string TargetDir(Dir); + assert(!TargetDir.empty()); + if (TargetDir.back() != '\\') TargetDir.push_back('\\'); + TargetDir.push_back('*'); + + WIN32_FIND_DATAA FindInfo; + // Find the directory's first file. + HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo); + if (FindHandle == INVALID_HANDLE_VALUE) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) { + + // If the directory isn't empty, then something abnormal is going on. + Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + } + + return; + + } + + do { + + std::string Path = DirPlusFile(Dir, FindInfo.cFileName); + DWORD PathAttrs = FindInfo.dwFileAttributes; + if (IsDir(PathAttrs)) { + + // Is Path the current directory (".") or the parent ("..")? + if (strcmp(FindInfo.cFileName, ".") == 0 || + strcmp(FindInfo.cFileName, "..") == 0) + continue; + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) { + + FileCallback(Path); + + } + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + FindClose(FindHandle); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '\\'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return _fdopen(Fd, Mode); + +} + +int CloseFile(int Fd) { + + return _close(Fd); + +} + +int DuplicateFile(int Fd) { + + return _dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + _unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return _get_osfhandle(fd); + +} + +bool IsSeparator(char C) { + + return C == '\\' || C == '/'; + +} + +// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". +// Returns number of characters considered if successful. +static size_t ParseDrive(const std::string &FileName, const size_t Offset, + bool Relative = true) { + + if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') return 0; + if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { + + if (!Relative) // Accept relative path? + return 0; + else + return 2; + + } + + return 3; + +} + +// Parse a file name, like: SomeFile.txt +// Returns number of characters considered if successful. +static size_t ParseFileName(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + return Pos - Offset; + +} + +// Parse a directory ending in separator, like: `SomeDir\` +// Returns number of characters considered if successful. +static size_t ParseDir(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + if (Pos >= End || IsSeparator(FileName[Pos])) return 0; + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + if (Pos >= End) return 0; + ++Pos; // Include separator. + return Pos - Offset; + +} + +// Parse a servername and share, like: `SomeServer\SomeShare\` +// Returns number of characters considered if successful. +static size_t ParseServerAndShare(const std::string &FileName, + const size_t Offset) { + + size_t Pos = Offset, Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + return Pos - Offset; + +} + +// Parse the given Ref string from the position Offset, to exactly match the +// given string Patt. Returns number of characters considered if successful. +static size_t ParseCustomString(const std::string &Ref, size_t Offset, + const char *Patt) { + + size_t Len = strlen(Patt); + if (Offset + Len > Ref.size()) return 0; + return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; + +} + +// Parse a location, like: +// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: +// Returns number of characters considered if successful. +static size_t ParseLocation(const std::string &FileName) { + + size_t Pos = 0, Res; + + if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { + + Pos += Res; + if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { + + Pos += Res; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + if ((Res = ParseDrive(FileName, Pos, false))) return Pos + Res; + return 0; + + } + + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + return Pos; + + } + + if ((Res = ParseDrive(FileName, Pos))) return Pos + Res; + + return Pos; + +} + +std::string DirName(const std::string &FileName) { + + size_t LocationLen = ParseLocation(FileName); + size_t DirLen = 0, Res; + while ((Res = ParseDir(FileName, LocationLen + DirLen))) + DirLen += Res; + size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); + + if (LocationLen + DirLen + FileLen != FileName.size()) { + + Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); + exit(1); + + } + + if (DirLen) { + + --DirLen; // Remove trailing separator. + if (!FileLen) { // Path ended in separator. + assert(DirLen); + // Remove file name from Dir. + while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) + --DirLen; + if (DirLen) // Remove trailing separator. + --DirLen; + + } + + } + + if (!LocationLen) { // Relative path. + if (!DirLen) return "."; + return std::string(".\\").append(FileName, 0, DirLen); + + } + + return FileName.substr(0, LocationLen + DirLen); + +} + +std::string TmpDir() { + + std::string Tmp; + Tmp.resize(MAX_PATH + 1); + DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]); + if (Size == 0) { + + Printf("Couldn't get Tmp path.\n"); + exit(1); + + } + + Tmp.resize(Size); + return Tmp; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("Program Files") != std::string::npos) return false; + if (FileName.find("compiler-rt\\lib\\") != std::string::npos) + return false; // sanitizer internal. + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + _write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + if (CreateDirectoryA(Path.c_str(), nullptr)) return; + Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +void RmDir(const std::string &Path) { + + if (RemoveDirectoryA(Path.c_str())) return; + Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "NUL"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerInterface.h b/custom_mutators/libfuzzer/FuzzerInterface.h new file mode 100644 index 00000000..4f62822e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInterface.h @@ -0,0 +1,79 @@ +//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the interface between libFuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +// NOTE: the libFuzzer interface is thin and in the majority of cases +// you should not include this file into your target. In 95% of cases +// all you need is to define the following function in your file: +// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// WARNING: keep the interface in C. + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that +// doesn't break MSVC. +#if defined(_WIN32) +#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport) +#else +#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default"))) +#endif + +// Mandatory user-provided target function. +// Executes the code under test with [Data, Data+Size) as the input. +// libFuzzer will invoke this function *many* times with different inputs. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// Optional user-provided initialization function. +// If provided, this function will be called by libFuzzer once at startup. +// It may read and modify argc/argv. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Optional user-provided custom mutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +// Given the same Seed produces the same mutation. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + +// Optional user-provided custom cross-over function. +// Combines pieces of Data1 & Data2 together into Out. +// Returns the new size, which is not greater than MaxOutSize. +// Should produce the same mutation given the same Seed. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, uint8_t *Out, + size_t MaxOutSize, unsigned int Seed); + +// Experimental, may go away in future. +// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#undef FUZZER_INTERFACE_VISIBILITY + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/custom_mutators/libfuzzer/FuzzerInternal.h b/custom_mutators/libfuzzer/FuzzerInternal.h new file mode 100644 index 00000000..2b172d91 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInternal.h @@ -0,0 +1,173 @@ +//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the main class fuzzer::Fuzzer and most functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERNAL_H +#define LLVM_FUZZER_INTERNAL_H + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerInterface.h" +#include "FuzzerOptions.h" +#include "FuzzerSHA1.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +using namespace std::chrono; + +class Fuzzer { +public: + + Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options); + ~Fuzzer(); + void Loop(Vector &CorporaFiles); + void ReadAndExecuteSeedCorpora(Vector &CorporaFiles); + void MinimizeCrashLoop(const Unit &U); + void RereadOutputCorpus(size_t MaxSize); + + size_t secondsSinceProcessStartUp() { + return duration_cast(system_clock::now() - ProcessStartTime) + .count(); + } + + bool TimedOut() { + return Options.MaxTotalTimeSec > 0 && + secondsSinceProcessStartUp() > + static_cast(Options.MaxTotalTimeSec); + } + + size_t execPerSec() { + size_t Seconds = secondsSinceProcessStartUp(); + return Seconds ? TotalNumberOfRuns / Seconds : 0; + } + + size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } + + static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticExitCallback(); + static void StaticInterruptCallback(); + static void StaticFileSizeExceedCallback(); + static void StaticGracefulExitCallback(); + + void ExecuteCallback(const uint8_t *Data, size_t Size); + bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, + InputInfo *II = nullptr, bool ForceAddToCorpus = false, + bool *FoundUniqFeatures = nullptr); + + // Merge Corpora[1:] into Corpora[0]. + void Merge(const Vector &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath); + MutationDispatcher &GetMD() { return MD; } + void PrintFinalStats(); + void SetMaxInputLen(size_t MaxInputLen); + void SetMaxMutationLen(size_t MaxMutationLen); + void RssLimitCallback(); + + bool InFuzzingThread() const { return IsMyThread; } + size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); + + void HandleMalloc(size_t Size); + static void MaybeExitGracefully(); + std::string WriteToOutputCorpus(const Unit &U); + +private: + void AlarmCallback(); + void CrashCallback(); + void ExitCallback(); + void CrashOnOverwrittenData(); + void InterruptCallback(); + void MutateAndTestOne(); + void PurgeAllocator(); + void ReportNewCoverage(InputInfo *II, const Unit &U); + void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); + void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, + size_t Features = 0); + void PrintStatusForNewUnit(const Unit &U, const char *Text); + void CheckExitOnSrcPosOrItem(); + + static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); + void DeathCallback(); + + void AllocateCurrentUnitData(); + uint8_t *CurrentUnitData = nullptr; + std::atomic CurrentUnitSize; + uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. + + bool GracefulExitRequested = false; + + size_t TotalNumberOfRuns = 0; + size_t NumberOfNewUnitsAdded = 0; + + size_t LastCorpusUpdateRun = 0; + + bool HasMoreMallocsThanFrees = false; + size_t NumberOfLeakDetectionAttempts = 0; + + system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now(); + + UserCallback CB; + InputCorpus &Corpus; + MutationDispatcher &MD; + FuzzingOptions Options; + DataFlowTrace DFT; + + system_clock::time_point ProcessStartTime = system_clock::now(); + system_clock::time_point UnitStartTime, UnitStopTime; + long TimeOfLongestUnitInSeconds = 0; + long EpochOfLastReadOfOutputCorpus = 0; + + size_t MaxInputLen = 0; + size_t MaxMutationLen = 0; + size_t TmpMaxMutationLen = 0; + + Vector UniqFeatureSetTmp; + + // Need to know our own thread. + static thread_local bool IsMyThread; +}; + +struct ScopedEnableMsanInterceptorChecks { + ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } + ~ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } +}; + +struct ScopedDisableMsanInterceptorChecks { + ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } + ~ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_INTERNAL_H diff --git a/custom_mutators/libfuzzer/FuzzerLoop.cpp b/custom_mutators/libfuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000..49187b30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerLoop.cpp @@ -0,0 +1,1087 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +#if defined(__has_include) + #if __has_include() + #include + #endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) + #if __has_feature(memory_sanitizer) + #undef NO_SANITIZE_MEMORY + #define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) + #endif +#endif + +namespace fuzzer { + +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +bool RunningUserCallback = false; + +// Only one Fuzzer per process. +static Fuzzer *F; + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + + void Start(int TraceLevel) { + + this->TraceLevel = TraceLevel; + if (TraceLevel) Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + + } + + // Returns true if there were more mallocs than frees. + bool Stop() { + + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + + } + + std::atomic Mallocs; + std::atomic Frees; + int TraceLevel = 0; + + std::recursive_mutex TraceMutex; + bool TraceDisabled = false; + +}; + +static MallocFreeTracer AllocTracer; + +// Locks printing and avoids nested hooks triggered from mallocs/frees in +// sanitizer. +class TraceLock { + + public: + TraceLock() : Lock(AllocTracer.TraceMutex) { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + ~TraceLock() { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + bool IsDisabled() const { + + // This is already inverted value. + return !AllocTracer.TraceDisabled; + + } + + private: + std::lock_guard Lock; + +}; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + + if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintStackTrace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + + if (EF->__sanitizer_set_death_callback) + EF->__sanitizer_set_death_callback(StaticDeathCallback); + assert(!F); + F = this; + TPC.ResetMaps(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfileMask(Options.UseValueProfile); + + if (Options.Verbosity) TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + TmpMaxMutationLen = 0; // Will be set once we load the corpus. + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); + +} + +Fuzzer::~Fuzzer() { + +} + +void Fuzzer::AllocateCurrentUnitData() { + + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; + +} + +void Fuzzer::StaticDeathCallback() { + + assert(F); + F->DeathCallback(); + +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + + if (!CurrentUnitData) return; // Happens when running individual inputs. + ScopedDisableMsanInterceptorChecks S; + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + + } + + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + + DumpCurrentUnit("crash-"); + PrintFinalStats(); + +} + +void Fuzzer::StaticAlarmCallback() { + + assert(F); + F->AlarmCallback(); + +} + +void Fuzzer::StaticCrashSignalCallback() { + + assert(F); + F->CrashCallback(); + +} + +void Fuzzer::StaticExitCallback() { + + assert(F); + F->ExitCallback(); + +} + +void Fuzzer::StaticInterruptCallback() { + + assert(F); + F->InterruptCallback(); + +} + +void Fuzzer::StaticGracefulExitCallback() { + + assert(F); + F->GracefulExitRequested = true; + Printf("INFO: signal received, trying to exit gracefully\n"); + +} + +void Fuzzer::StaticFileSizeExceedCallback() { + + Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid()); + exit(1); + +} + +void Fuzzer::CrashCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + PrintStackTrace(); + Printf( + "NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +void Fuzzer::ExitCallback() { + + if (!RunningUserCallback) + return; // This exit did not come from the user callback + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: fuzz target exited\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); + +} + +void Fuzzer::MaybeExitGracefully() { + + if (!F->GracefulExitRequested) return; + Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + F->PrintFinalStats(); + _Exit(0); + +} + +void Fuzzer::InterruptCallback() { + + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir(). + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + // Stop right now, don't perform any at-exit actions. + _Exit(Options.InterruptExitCode); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + + assert(Options.UnitTimeoutSec > 0); + // In Windows and Fuchsia, Alarm callback is executed by a different thread. + // NetBSD's current behavior needs this change too. +#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA + if (!InFuzzingThread()) return; +#endif + if (!RunningUserCallback) return; // We have not started running units yet. + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) return; + if (Options.Verbosity >= 2) Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + + } + +} + +void Fuzzer::RssLimitCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintMemoryProfile(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, + size_t Features) { + + size_t ExecPerSec = execPerSec(); + if (!Options.Verbosity) return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (size_t N = TPC.GetTotalPCCoverage()) Printf(" cov: %zd", N); + if (size_t N = Features ? Features : Corpus.NumFeatures()) + Printf(" ft: %zd", N); + if (!Corpus.empty()) { + + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + + if (N < (1 << 14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + + } + + if (size_t FF = Corpus.NumInputsThatTouchFocusFunction()) + Printf(" focus: %zd", FF); + + } + + if (TmpMaxMutationLen) Printf(" lim: %zd", TmpMaxMutationLen); + if (Units) Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); + +} + +void Fuzzer::PrintFinalStats() { + + if (Options.PrintCoverage) TPC.PrintCoverage(); + if (Options.PrintCorpusStats) Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); + +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + + assert(this->MaxInputLen == + 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf( + "INFO: -max_len is not provided; " + "libFuzzer will not generate inputs larger than %zd bytes\n", + MaxInputLen); + +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; + +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + + if (!Options.ExitOnSrcPos.empty()) { + + static auto *PCsSet = new Set; + auto HandlePC = [&](const TracePC::PCTableEntry *TE) { + + if (!PCsSet->insert(TE->PC).second) return; + std::string Descr = DescribePC("%F %L", TE->PC + 1); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + + } + + }; + + TPC.ForEachObservedPC(HandlePC); + + } + + if (!Options.ExitOnItem.empty()) { + + if (Corpus.HasUnit(Options.ExitOnItem)) { + + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + + } + + } + +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + Vector AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + bool Reloaded = false; + for (auto &U : AdditionalCorpus) { + + if (U.size() > MaxSize) U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + + if (RunOne(U.data(), U.size())) { + + CheckExitOnSrcPosOrItem(); + Reloaded = true; + + } + + } + + } + + if (Reloaded) PrintStats("RELOAD"); + +} + +void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { + + auto TimeOfUnit = + duration_cast(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + + } + +} + +static void WriteFeatureSetToFile(const std::string & FeaturesDir, + const std::string & FileName, + const Vector &FeatureSet) { + + if (FeaturesDir.empty() || FeatureSet.empty()) return; + WriteToFile(reinterpret_cast(FeatureSet.data()), + FeatureSet.size() * sizeof(FeatureSet[0]), + DirPlusFile(FeaturesDir, FileName)); + +} + +static void RenameFeatureSetFile(const std::string &FeaturesDir, + const std::string &OldFile, + const std::string &NewFile) { + + if (FeaturesDir.empty()) return; + RenameFile(DirPlusFile(FeaturesDir, OldFile), + DirPlusFile(FeaturesDir, NewFile)); + +} + +static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile, + const InputInfo * II, + const InputInfo * BaseII, + const std::string &MS) { + + if (MutationGraphFile.empty()) return; + + std::string Sha1 = Sha1ToString(II->Sha1); + + std::string OutputString; + + // Add a new vertex. + OutputString.append("\""); + OutputString.append(Sha1); + OutputString.append("\"\n"); + + // Add a new edge if there is base input. + if (BaseII) { + + std::string BaseSha1 = Sha1ToString(BaseII->Sha1); + OutputString.append("\""); + OutputString.append(BaseSha1); + OutputString.append("\" -> \""); + OutputString.append(Sha1); + OutputString.append("\" [label=\""); + OutputString.append(MS); + OutputString.append("\"];\n"); + + } + + AppendToFile(OutputString, MutationGraphFile); + +} + +bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, + InputInfo *II, bool ForceAddToCorpus, + bool *FoundUniqFeatures) { + + if (!Size) return false; + + ExecuteCallback(Data, Size); + auto TimeOfUnit = duration_cast(UnitStopTime - UnitStartTime); + + UniqFeatureSetTmp.clear(); + size_t FoundUniqFeaturesOfII = 0; + size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + TPC.CollectFeatures([&](size_t Feature) { + + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + UniqFeatureSetTmp.push_back(Feature); + if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); + if (Options.ReduceInputs && II && !II->NeverReduce) + if (std::binary_search(II->UniqFeatureSet.begin(), + II->UniqFeatureSet.end(), Feature)) + FoundUniqFeaturesOfII++; + + }); + + if (FoundUniqFeatures) *FoundUniqFeatures = FoundUniqFeaturesOfII; + PrintPulseAndReportSlowInput(Data, Size); + size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + if (NumNewFeatures || ForceAddToCorpus) { + + TPC.UpdateObservedPCs(); + auto NewII = + Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, + TPC.ObservedFocusFunction(), ForceAddToCorpus, + TimeOfUnit, UniqFeatureSetTmp, DFT, II); + WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), + NewII->UniqFeatureSet); + WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II, + MD.MutationSequence()); + return true; + + } + + if (II && FoundUniqFeaturesOfII && + II->DataFlowTraceForFocusFunction.empty() && + FoundUniqFeaturesOfII == II->UniqFeatureSet.size() && + II->U.size() > Size) { + + auto OldFeaturesFile = Sha1ToString(II->Sha1); + Corpus.Replace(II, {Data, Data + Size}); + RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile, + Sha1ToString(II->Sha1)); + return true; + + } + + return false; + +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; + +} + +void Fuzzer::CrashOnOverwrittenData() { + + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", + GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +// Compare two arrays, but not all bytes if the arrays are large. +static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { + + const size_t Limit = 64; + if (Size <= 64) return !memcmp(A, B, Size); + // Compare first and last Limit/2 bytes. + return !memcmp(A, B, Limit / 2) && + !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); + +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + + TPC.RecordInitialStack(); + TotalNumberOfRuns++; + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (EF->__msan_unpoison) EF->__msan_unpoison(DataCopy, Size); + if (EF->__msan_unpoison_param) EF->__msan_unpoison_param(2); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + { + + ScopedEnableMsanInterceptorChecks S; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + TPC.ResetMaps(); + RunningUserCallback = true; + int Res = CB(DataCopy, Size); + RunningUserCallback = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + + } + + if (!LooseMemeq(DataCopy, Data, Size)) CrashOnOverwrittenData(); + CurrentUnitSize = 0; + delete[] DataCopy; + +} + +std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { + + if (Options.OnlyASCII) assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) return ""; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written %zd bytes to %s\n", U.size(), Path.c_str()); + return Path; + +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + + if (!Options.SaveArtifacts) return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); + +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { + + if (!Options.PrintNEW) return; + PrintStats(Text, ""); + if (Options.Verbosity) { + + Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); + MD.PrintMutationSequence(); + Printf("\n"); + + } + +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW "); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus. + LastCorpusUpdateRun = TotalNumberOfRuns; + +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!DuringInitialCorpusExecution && + TotalNumberOfRuns >= Options.MaxNumberOfRuns) + return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + + Options.DetectLeaks = false; + Printf( + "INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + + } + + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + + } + +} + +void Fuzzer::MutateAndTestOne() { + + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + if (Options.DoCrossOver) { + + auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith( + MD.GetRand(), Options.CrossOverUniformDist); + MD.SetCrossOverWith(&CrossOverII.U); + + } + + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + size_t CurrentMaxMutationLen = + Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen)); + assert(CurrentMaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + MaybeExitGracefully(); + size_t NewSize = 0; + if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && + Size <= CurrentMaxMutationLen) + NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size, + II.DataFlowTraceForFocusFunction); + + // If MutateWithMask either failed or wasn't called, call default Mutate. + if (!NewSize) + NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); + Size = NewSize; + II.NumExecutedMutations++; + Corpus.IncrementNumExecutedMutations(); + + bool FoundUniqFeatures = false; + bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II, + /*ForceAddToCorpus*/ false, &FoundUniqFeatures); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + if (NewCov) { + + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + break; // We will mutate this input more in the next rounds. + + } + + if (Options.ReduceDepth && !FoundUniqFeatures) break; + + } + + II.NeedsEnergyUpdate = true; + +} + +void Fuzzer::PurgeAllocator() { + + if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator) + return; + if (duration_cast(system_clock::now() - + LastAllocatorPurgeAttemptTime) + .count() < Options.PurgeAllocatorIntervalSec) + return; + + if (Options.RssLimitMb <= 0 || + GetPeakRSSMb() > static_cast(Options.RssLimitMb) / 2) + EF->__sanitizer_purge_allocator(); + + LastAllocatorPurgeAttemptTime = system_clock::now(); + +} + +void Fuzzer::ReadAndExecuteSeedCorpora(Vector &CorporaFiles) { + + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 4096; + size_t MaxSize = 0; + size_t MinSize = -1; + size_t TotalSize = 0; + for (auto &File : CorporaFiles) { + + MaxSize = Max(File.Size, MaxSize); + MinSize = Min(File.Size, MinSize); + TotalSize += File.Size; + + } + + if (Options.MaxLen == 0) + SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen)); + assert(MaxInputLen > 0); + + // Test the callback with empty input and never try it again. + uint8_t dummy = 0; + ExecuteCallback(&dummy, 0); + + if (CorporaFiles.empty()) { + + Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); + Unit U({'\n'}); // Valid ASCII input. + RunOne(U.data(), U.size()); + + } else { + + Printf( + "INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb" + " rss: %zdMb\n", + CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb()); + if (Options.ShuffleAtStartUp) + std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand()); + + if (Options.PreferSmall) { + + std::stable_sort(CorporaFiles.begin(), CorporaFiles.end()); + assert(CorporaFiles.front().Size <= CorporaFiles.back().Size); + + } + + // Load and execute inputs one by one. + for (auto &SF : CorporaFiles) { + + auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false); + assert(U.size() <= MaxInputLen); + RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr, + /*ForceAddToCorpus*/ Options.KeepSeed, + /*FoundUniqFeatures*/ nullptr); + CheckExitOnSrcPosOrItem(); + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + + } + + } + + PrintStats("INITED"); + if (!Options.FocusFunction.empty()) { + + Printf("INFO: %zd/%zd inputs touch the focus function\n", + Corpus.NumInputsThatTouchFocusFunction(), Corpus.size()); + if (!Options.DataFlowTrace.empty()) + Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n", + Corpus.NumInputsWithDataFlowTrace(), + Corpus.NumInputsThatTouchFocusFunction()); + + } + + if (Corpus.empty() && Options.MaxNumberOfRuns) { + + Printf( + "ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + + } + +} + +void Fuzzer::Loop(Vector &CorporaFiles) { + + auto FocusFunctionOrAuto = Options.FocusFunction; + DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + MD.GetRand()); + TPC.SetFocusFunction(FocusFunctionOrAuto); + ReadAndExecuteSeedCorpora(CorporaFiles); + DFT.Clear(); // No need for DFT any more. + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); + system_clock::time_point LastCorpusReload = system_clock::now(); + + TmpMaxMutationLen = + Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize())); + + while (true) { + + auto Now = system_clock::now(); + if (!Options.StopFile.empty() && + !FileToVector(Options.StopFile, 1, false).empty()) + break; + if (duration_cast(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + + } + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + if (TimedOut()) break; + + // Update TmpMaxMutationLen + if (Options.LenControl) { + + if (TmpMaxMutationLen < MaxMutationLen && + TotalNumberOfRuns - LastCorpusUpdateRun > + Options.LenControl * Log(TmpMaxMutationLen)) { + + TmpMaxMutationLen = + Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen)); + LastCorpusUpdateRun = TotalNumberOfRuns; + + } + + } else { + + TmpMaxMutationLen = MaxMutationLen; + + } + + // Perform several mutations and runs. + MutateAndTestOne(); + + PurgeAllocator(); + + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); + +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + + if (U.size() <= 1) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + ExecuteCallback(CurrentUnitData, NewSize); + PrintPulseAndReportSlowInput(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + + } + + } + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.cpp b/custom_mutators/libfuzzer/FuzzerMerge.cpp new file mode 100644 index 00000000..b341f5b3 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.cpp @@ -0,0 +1,485 @@ +//===- FuzzerMerge.cpp - merging corpora ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging corpora. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerMerge.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include + +namespace fuzzer { + +bool Merger::Parse(const std::string &Str, bool ParseCoverage) { + + std::istringstream SS(Str); + return Parse(SS, ParseCoverage); + +} + +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { + + if (!Parse(IS, ParseCoverage)) { + + Printf("MERGE: failed to parse the control file (unexpected error)\n"); + exit(1); + + } + +} + +// The control file example: +// +// 3 # The number of inputs +// 1 # The number of inputs in the first corpus, <= the previous number +// file0 +// file1 +// file2 # One file name per line. +// STARTED 0 123 # FileID, file size +// FT 0 1 4 6 8 # FileID COV1 COV2 ... +// COV 0 7 8 9 # FileID COV1 COV1 +// STARTED 1 456 # If FT is missing, the input crashed while processing. +// STARTED 2 567 +// FT 2 8 9 +// COV 2 11 12 +bool Merger::Parse(std::istream &IS, bool ParseCoverage) { + + LastFailure.clear(); + std::string Line; + + // Parse NumFiles. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L1(Line); + size_t NumFiles = 0; + L1 >> NumFiles; + if (NumFiles == 0 || NumFiles > 10000000) return false; + + // Parse NumFilesInFirstCorpus. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L2(Line); + NumFilesInFirstCorpus = NumFiles + 1; + L2 >> NumFilesInFirstCorpus; + if (NumFilesInFirstCorpus > NumFiles) return false; + + // Parse file names. + Files.resize(NumFiles); + for (size_t i = 0; i < NumFiles; i++) + if (!std::getline(IS, Files[i].Name, '\n')) return false; + + // Parse STARTED, FT, and COV lines. + size_t ExpectedStartMarker = 0; + const size_t kInvalidStartMarker = -1; + size_t LastSeenStartMarker = kInvalidStartMarker; + Vector TmpFeatures; + Set PCs; + while (std::getline(IS, Line, '\n')) { + + std::istringstream ISS1(Line); + std::string Marker; + size_t N; + ISS1 >> Marker; + ISS1 >> N; + if (Marker == "STARTED") { + + // STARTED FILE_ID FILE_SIZE + if (ExpectedStartMarker != N) return false; + ISS1 >> Files[ExpectedStartMarker].Size; + LastSeenStartMarker = ExpectedStartMarker; + assert(ExpectedStartMarker < Files.size()); + ExpectedStartMarker++; + + } else if (Marker == "FT") { + + // FT FILE_ID COV1 COV2 COV3 ... + size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) return false; + LastSeenStartMarker = kInvalidStartMarker; + if (ParseCoverage) { + + TmpFeatures.clear(); // use a vector from outer scope to avoid resizes. + while (ISS1 >> N) + TmpFeatures.push_back(N); + std::sort(TmpFeatures.begin(), TmpFeatures.end()); + Files[CurrentFileIdx].Features = TmpFeatures; + + } + + } else if (Marker == "COV") { + + size_t CurrentFileIdx = N; + if (ParseCoverage) + while (ISS1 >> N) + if (PCs.insert(N).second) Files[CurrentFileIdx].Cov.push_back(N); + + } else { + + return false; + + } + + } + + if (LastSeenStartMarker != kInvalidStartMarker) + LastFailure = Files[LastSeenStartMarker].Name; + + FirstNotProcessedFile = ExpectedStartMarker; + return true; + +} + +size_t Merger::ApproximateMemoryConsumption() const { + + size_t Res = 0; + for (const auto &F : Files) + Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]); + return Res; + +} + +// Decides which files need to be merged (add those to NewFiles). +// Returns the number of new features added. +size_t Merger::Merge(const Set &InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles) { + + NewFiles->clear(); + assert(NumFilesInFirstCorpus <= Files.size()); + Set AllFeatures = InitialFeatures; + + // What features are in the initial corpus? + for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { + + auto &Cur = Files[i].Features; + AllFeatures.insert(Cur.begin(), Cur.end()); + + } + + // Remove all features that we already know from all other inputs. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto & Cur = Files[i].Features; + Vector Tmp; + std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), + AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); + Cur.swap(Tmp); + + } + + // Sort. Give preference to + // * smaller files + // * files with more features. + std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(), + [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool { + + if (a.Size != b.Size) return a.Size < b.Size; + return a.Features.size() > b.Features.size(); + + }); + + // One greedy pass: add the file's features to AllFeatures. + // If new features were added, add this file to NewFiles. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto &Cur = Files[i].Features; + // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(), + // Files[i].Size, Cur.size()); + bool FoundNewFeatures = false; + for (auto Fe : Cur) { + + if (AllFeatures.insert(Fe).second) { + + FoundNewFeatures = true; + NewFeatures->insert(Fe); + + } + + } + + if (FoundNewFeatures) NewFiles->push_back(Files[i].Name); + for (auto Cov : Files[i].Cov) + if (InitialCov.find(Cov) == InitialCov.end()) NewCov->insert(Cov); + + } + + return NewFeatures->size(); + +} + +Set Merger::AllFeatures() const { + + Set S; + for (auto &File : Files) + S.insert(File.Features.begin(), File.Features.end()); + return S; + +} + +// Inner process. May crash if the target crashes. +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { + + Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, false); + IF.close(); + if (!M.LastFailure.empty()) + Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", + M.LastFailure.c_str()); + + Printf( + "MERGE-INNER: %zd total files;" + " %zd processed earlier; will process %zd files now\n", + M.Files.size(), M.FirstNotProcessedFile, + M.Files.size() - M.FirstNotProcessedFile); + + std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + Set AllFeatures; + auto PrintStatsWrapper = [this, &AllFeatures](const char *Where) { + + this->PrintStats(Where, "\n", 0, AllFeatures.size()); + + }; + + Set AllPCs; + for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + + Fuzzer::MaybeExitGracefully(); + auto U = FileToVector(M.Files[i].Name); + if (U.size() > MaxInputLen) { + + U.resize(MaxInputLen); + U.shrink_to_fit(); + + } + + // Write the pre-run marker. + OF << "STARTED " << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since Command::Execute may crash. + // Run. + TPC.ResetMaps(); + ExecuteCallback(U.data(), U.size()); + // Collect coverage. We are iterating over the files in this order: + // * First, files in the initial corpus ordered by size, smallest first. + // * Then, all other files, smallest first. + // So it makes no sense to record all features for all files, instead we + // only record features that were not seen before. + Set UniqFeatures; + TPC.CollectFeatures([&](size_t Feature) { + + if (AllFeatures.insert(Feature).second) UniqFeatures.insert(Feature); + + }); + + TPC.UpdateObservedPCs(); + // Show stats. + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) + PrintStatsWrapper("pulse "); + if (TotalNumberOfRuns == M.NumFilesInFirstCorpus) + PrintStatsWrapper("LOADED"); + // Write the post-run marker and the coverage. + OF << "FT " << i; + for (size_t F : UniqFeatures) + OF << " " << F; + OF << "\n"; + OF << "COV " << i; + TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) { + + if (AllPCs.insert(TE).second) OF << " " << TPC.PCTableEntryIdx(TE); + + }); + + OF << "\n"; + OF.flush(); + + } + + PrintStatsWrapper("DONE "); + +} + +static size_t WriteNewControlFile(const std::string & CFPath, + const Vector & OldCorpus, + const Vector & NewCorpus, + const Vector &KnownFiles) { + + std::unordered_set FilesToSkip; + for (auto &SF : KnownFiles) + FilesToSkip.insert(SF.Name); + + Vector FilesToUse; + auto MaybeUseFile = [=, &FilesToUse](std::string Name) { + + if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name); + + }; + + for (auto &SF : OldCorpus) + MaybeUseFile(SF.File); + auto FilesToUseFromOldCorpus = FilesToUse.size(); + for (auto &SF : NewCorpus) + MaybeUseFile(SF.File); + + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << FilesToUse.size() << "\n"; + ControlFile << FilesToUseFromOldCorpus << "\n"; + for (auto &FN : FilesToUse) + ControlFile << FN << "\n"; + + if (!ControlFile) { + + Printf("MERGE-OUTER: failed to write to the control file: %s\n", + CFPath.c_str()); + exit(1); + + } + + return FilesToUse.size(); + +} + +// Outer process. Does not call the target code and thus should not fail. +void CrashResistantMerge(const Vector &Args, + const Vector & OldCorpus, + const Vector & NewCorpus, + Vector * NewFiles, + const Set & InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + const std::string &CFPath, bool V /*Verbose*/) { + + if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. + size_t NumAttempts = 0; + Vector KnownFiles; + if (FileSize(CFPath)) { + + VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", + CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + if (M.Parse(IF, /*ParseCoverage=*/true)) { + + VPrintf(V, + "MERGE-OUTER: control file ok, %zd files total," + " first not processed file %zd\n", + M.Files.size(), M.FirstNotProcessedFile); + if (!M.LastFailure.empty()) + VPrintf(V, + "MERGE-OUTER: '%s' will be skipped as unlucky " + "(merge has stumbled on it the last time)\n", + M.LastFailure.c_str()); + if (M.FirstNotProcessedFile >= M.Files.size()) { + + // Merge has already been completed with the given merge control file. + if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) { + + VPrintf( + V, + "MERGE-OUTER: nothing to do, merge has been completed before\n"); + exit(0); + + } + + // Number of input files likely changed, start merge from scratch, but + // reuse coverage information from the given merge control file. + VPrintf( + V, + "MERGE-OUTER: starting merge from scratch, but reusing coverage " + "information from the given control file\n"); + KnownFiles = M.Files; + + } else { + + // There is a merge in progress, continue. + NumAttempts = M.Files.size() - M.FirstNotProcessedFile; + + } + + } else { + + VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n"); + + } + + } + + if (!NumAttempts) { + + // The supplied control file is empty or bad, create a fresh one. + VPrintf(V, + "MERGE-OUTER: " + "%zd files, %zd in the initial corpus, %zd processed earlier\n", + OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), + KnownFiles.size()); + NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); + + } + + // Execute the inner process until it passes. + // Every inner process should execute at least one input. + Command BaseCmd(Args); + BaseCmd.removeFlag("merge"); + BaseCmd.removeFlag("fork"); + BaseCmd.removeFlag("collect_data_flow"); + for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { + + Fuzzer::MaybeExitGracefully(); + VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); + Command Cmd(BaseCmd); + Cmd.addFlag("merge_control_file", CFPath); + Cmd.addFlag("merge_inner", "1"); + if (!V) { + + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + } + + auto ExitCode = ExecuteCommand(Cmd); + if (!ExitCode) { + + VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt); + break; + + } + + } + + // Read the control file and do the merge. + Merger M; + std::ifstream IF(CFPath); + IF.seekg(0, IF.end); + VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", + (size_t)IF.tellg()); + IF.seekg(0, IF.beg); + M.ParseOrExit(IF, true); + IF.close(); + VPrintf(V, + "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", + M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + + M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end()); + M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); + VPrintf(V, + "MERGE-OUTER: %zd new files with %zd new features added; " + "%zd new coverage edges\n", + NewFiles->size(), NewFeatures->size(), NewCov->size()); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.h b/custom_mutators/libfuzzer/FuzzerMerge.h new file mode 100644 index 00000000..e0c6bc53 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.h @@ -0,0 +1,87 @@ +//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging Corpora. +// +// The task: +// Take the existing corpus (possibly empty) and merge new inputs into +// it so that only inputs with new coverage ('features') are added. +// The process should tolerate the crashes, OOMs, leaks, etc. +// +// Algorithm: +// The outer process collects the set of files and writes their names +// into a temporary "control" file, then repeatedly launches the inner +// process until all inputs are processed. +// The outer process does not actually execute the target code. +// +// The inner process reads the control file and sees a) list of all the inputs +// and b) the last processed input. Then it starts processing the inputs one +// by one. Before processing every input it writes one line to control file: +// STARTED INPUT_ID INPUT_SIZE +// After processing an input it writes the following lines: +// FT INPUT_ID Feature1 Feature2 Feature3 ... +// COV INPUT_ID Coverage1 Coverage2 Coverage3 ... +// If a crash happens while processing an input the last line in the control +// file will be "STARTED INPUT_ID" and so the next process will know +// where to resume. +// +// Once all inputs are processed by the inner process(es) the outer process +// reads the control files and does the merge based entirely on the contents +// of control file. +// It uses a single pass greedy algorithm choosing first the smallest inputs +// within the same size the inputs that have more new features. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MERGE_H +#define LLVM_FUZZER_MERGE_H + +#include "FuzzerDefs.h" + +#include +#include +#include +#include + +namespace fuzzer { + +struct MergeFileInfo { + std::string Name; + size_t Size = 0; + Vector Features, Cov; +}; + +struct Merger { + Vector Files; + size_t NumFilesInFirstCorpus = 0; + size_t FirstNotProcessedFile = 0; + std::string LastFailure; + + bool Parse(std::istream &IS, bool ParseCoverage); + bool Parse(const std::string &Str, bool ParseCoverage); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(const Set &InitialFeatures, Set *NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles); + size_t ApproximateMemoryConsumption() const; + Set AllFeatures() const; +}; + +void CrashResistantMerge(const Vector &Args, + const Vector &OldCorpus, + const Vector &NewCorpus, + Vector *NewFiles, + const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, + Set *NewCov, + const std::string &CFPath, + bool Verbose); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MERGE_H diff --git a/custom_mutators/libfuzzer/FuzzerMutate.cpp b/custom_mutators/libfuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000..8faf6918 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.cpp @@ -0,0 +1,720 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" +#include "FuzzerTracePC.h" + +namespace fuzzer { + +const size_t Dictionary::kMaxDictSize; + +static void PrintASCII(const Word &W, const char *PrintAfter) { + + PrintASCII(W.data(), W.size(), PrintAfter); + +} + +MutationDispatcher::MutationDispatcher(Random & Rand, + const FuzzingOptions &Options) + : Rand(Rand), Options(Options) { + + DefaultMutators.insert( + DefaultMutators.begin(), + { + + {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, + {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&MutationDispatcher::Mutate_InsertRepeatedBytes, + "InsertRepeatedBytes"}, + {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"}, + {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"}, + {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"}, + {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"}, + {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"}, + {&MutationDispatcher::Mutate_CopyPart, "CopyPart"}, + {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, + {&MutationDispatcher::Mutate_AddWordFromManualDictionary, + "ManualDict"}, + {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, + "PersAutoDict"}, + + }); + + if (Options.UseCmp) + DefaultMutators.push_back( + {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + + if (EF->LLVMFuzzerCustomMutator) + Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + else + Mutators = DefaultMutators; + + if (EF->LLVMFuzzerCustomCrossOver) + Mutators.push_back( + {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); + +} + +static char RandCh(Random &Rand) { + + if (Rand.RandBool()) return Rand(256); + const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; + return Special[Rand(sizeof(Special) - 1)]; + +} + +size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); + +} + +size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &Other = *CrossOverWith; + if (Other.empty()) return 0; + CustomCrossOverInPlaceHere.resize(MaxSize); + auto & U = CustomCrossOverInPlaceHere; + size_t NewSize = EF->LLVMFuzzerCustomCrossOver( + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + if (!NewSize) return 0; + assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; + +} + +size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + size_t ShuffleAmount = + Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. + size_t ShuffleStart = Rand(Size - ShuffleAmount); + assert(ShuffleStart + ShuffleAmount <= Size); + std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size <= 1) return 0; + size_t N = Rand(Size / 2) + 1; + assert(N < Size); + size_t Idx = Rand(Size - N + 1); + // Erase Data[Idx:Idx+N]. + memmove(Data + Idx, Data + Idx + N, Size - Idx - N); + // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx); + return Size - N; + +} + +size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size >= MaxSize) return 0; + size_t Idx = Rand(Size + 1); + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(Rand); + return Size + 1; + +} + +size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + const size_t kMinBytesToInsert = 3; + if (Size + kMinBytesToInsert >= MaxSize) return 0; + size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128); + size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert; + assert(Size + N <= MaxSize && N); + size_t Idx = Rand(Size + 1); + // Insert new values at Data[Idx]. + memmove(Data + Idx + N, Data + Idx, Size - Idx); + // Give preference to 0x00 and 0xff. + uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); + for (size_t i = 0; i < N; i++) + Data[Idx + i] = Byte; + return Size + N; + +} + +size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] = RandCh(Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] ^= 1 << Rand(8); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, + size_t MaxSize, + DictionaryEntry &DE) { + + const Word &W = DE.GetW(); + bool UsePositionHint = DE.HasPositionHint() && + DE.GetPositionHint() + W.size() < Size && + Rand.RandBool(); + if (Rand.RandBool()) { // Insert W. + if (Size + W.size() > MaxSize) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1); + memmove(Data + Idx + W.size(), Data + Idx, Size - Idx); + memcpy(Data + Idx, W.data(), W.size()); + Size += W.size(); + + } else { // Overwrite some bytes with W. + + if (W.size() > Size) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + memcpy(Data + Idx, W.data(), W.size()); + + } + + return Size; + +} + +// Somewhere in the past we have observed a comparison instructions +// with arguments Arg1 Arg2. This function tries to guess a dictionary +// entry that will satisfy that comparison. +// It first tries to find one of the arguments (possibly swapped) in the +// input and if it succeeds it creates a DE with a position hint. +// Otherwise it creates a DE with one of the arguments w/o a position hint. +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const void *Arg1, const void *Arg2, const void *Arg1Mutation, + const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data, + size_t Size) { + + bool HandleFirst = Rand.RandBool(); + const void * ExistingBytes, *DesiredBytes; + Word W; + const uint8_t *End = Data + Size; + for (int Arg = 0; Arg < 2; Arg++) { + + ExistingBytes = HandleFirst ? Arg1 : Arg2; + DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation; + HandleFirst = !HandleFirst; + W.Set(reinterpret_cast(DesiredBytes), ArgSize); + const size_t kMaxNumPositions = 8; + size_t Positions[kMaxNumPositions]; + size_t NumPositions = 0; + for (const uint8_t *Cur = Data; + Cur < End && NumPositions < kMaxNumPositions; Cur++) { + + Cur = + (const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); + if (!Cur) break; + Positions[NumPositions++] = Cur - Data; + + } + + if (!NumPositions) continue; + return DictionaryEntry(W, Positions[Rand(NumPositions)]); + + } + + DictionaryEntry DE(W); + return DE; + +} + +template +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + + if (Rand.RandBool()) Arg1 = Bswap(Arg1); + if (Rand.RandBool()) Arg2 = Bswap(Arg2); + T Arg1Mutation = Arg1 + Rand(-1, 1); + T Arg2Mutation = Arg2 + Rand(-1, 1); + return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, + sizeof(Arg1), Data, Size); + +} + +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) { + + return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(), + Arg2.data(), Arg1.size(), Data, Size); + +} + +size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, + size_t MaxSize) { + + Word W; + DictionaryEntry DE; + switch (Rand(4)) { + + case 0: { + + auto X = TPC.TORC8.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 1: { + + auto X = TPC.TORC4.Get(Rand.Rand()); + if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) + DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, + Size); + else + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 2: { + + auto X = TPC.TORCW.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 3: + if (Options.UseMemmem) { + + auto X = TPC.MMT.Get(Rand.Rand()); + DE = DictionaryEntry(X); + + } + + break; + default: + assert(0); + + } + + if (!DE.GetW().size()) return 0; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DictionaryEntry &DERef = + CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ % + kCmpDictionaryEntriesDequeSize]; + DERef = DE; + CurrentDictionaryEntrySequence.push_back(&DERef); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + + return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data, + size_t Size, size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (D.empty()) return 0; + DictionaryEntry &DE = D[Rand(D.size())]; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DE.IncUseCount(); + CurrentDictionaryEntrySequence.push_back(&DE); + return Size; + +} + +// Overwrites part of To[0,ToSize) with a part of From[0,FromSize). +// Returns ToSize. +size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize) { + + // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize). + size_t ToBeg = Rand(ToSize); + size_t CopySize = Rand(ToSize - ToBeg) + 1; + assert(ToBeg + CopySize <= ToSize); + CopySize = std::min(CopySize, FromSize); + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + memmove(To + ToBeg, From + FromBeg, CopySize); + return ToSize; + +} + +// Inserts part of From[0,ToSize) into To. +// Returns new size of To on success or 0 on failure. +size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize, + size_t MaxToSize) { + + if (ToSize >= MaxToSize) return 0; + size_t AvailableSpace = MaxToSize - ToSize; + size_t MaxCopySize = std::min(AvailableSpace, FromSize); + size_t CopySize = Rand(MaxCopySize) + 1; + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + size_t ToInsertPos = Rand(ToSize + 1); + assert(ToInsertPos + CopySize <= MaxToSize); + size_t TailSize = ToSize - ToInsertPos; + if (To == From) { + + MutateInPlaceHere.resize(MaxToSize); + memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize); + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize); + + } else { + + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, From + FromBeg, CopySize); + + } + + return ToSize + CopySize; + +} + +size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + // If Size == MaxSize, `InsertPartOf(...)` will + // fail so there's no point using it in this case. + if (Size == MaxSize || Rand.RandBool()) + return CopyPartOf(Data, Size, Data, Size); + else + return InsertPartOf(Data, Size, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t B = Rand(Size); + while (B < Size && !isdigit(Data[B])) + B++; + if (B == Size) return 0; + size_t E = B; + while (E < Size && isdigit(Data[E])) + E++; + assert(B < E); + // now we have digits in [B, E). + // strtol and friends don't accept non-zero-teminated data, parse it manually. + uint64_t Val = Data[B] - '0'; + for (size_t i = B + 1; i < E; i++) + Val = Val * 10 + Data[i] - '0'; + + // Mutate the integer value. + switch (Rand(5)) { + + case 0: + Val++; + break; + case 1: + Val--; + break; + case 2: + Val /= 2; + break; + case 3: + Val *= 2; + break; + case 4: + Val = Rand(Val * Val); + break; + default: + assert(0); + + } + + // Just replace the bytes with the new ones, don't bother moving bytes. + for (size_t i = B; i < E; i++) { + + size_t Idx = E + B - i - 1; + assert(Idx >= B && Idx < E); + Data[Idx] = (Val % 10) + '0'; + Val /= 10; + + } + + return Size; + +} + +template +size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { + + if (Size < sizeof(T)) return 0; + size_t Off = Rand(Size - sizeof(T) + 1); + assert(Off + sizeof(T) <= Size); + T Val; + if (Off < 64 && !Rand(4)) { + + Val = Size; + if (Rand.RandBool()) Val = Bswap(Val); + + } else { + + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = Rand(21); + Add -= 10; + if (Rand.RandBool()) + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + else + Val = Val + Add; // Add assuming current endiannes. + if (Add == 0 || Rand.RandBool()) // Maybe negate. + Val = -Val; + + } + + memcpy(Data + Off, &Val, sizeof(Val)); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + switch (Rand(4)) { + + case 3: + return ChangeBinaryInteger(Data, Size, Rand); + case 2: + return ChangeBinaryInteger(Data, Size, Rand); + case 1: + return ChangeBinaryInteger(Data, Size, Rand); + case 0: + return ChangeBinaryInteger(Data, Size, Rand); + default: + assert(0); + + } + + return 0; + +} + +size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &O = *CrossOverWith; + if (O.empty()) return 0; + size_t NewSize = 0; + switch (Rand(3)) { + + case 0: + MutateInPlaceHere.resize(MaxSize); + NewSize = CrossOver(Data, Size, O.data(), O.size(), + MutateInPlaceHere.data(), MaxSize); + memcpy(Data, MutateInPlaceHere.data(), NewSize); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize); + if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + default: + assert(0); + + } + + assert(NewSize > 0 && "CrossOver returned empty unit"); + assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); + return NewSize; + +} + +void MutationDispatcher::StartMutationSequence() { + + CurrentMutatorSequence.clear(); + CurrentDictionaryEntrySequence.clear(); + +} + +// Copy successful dictionary entries to PersistentAutoDictionary. +void MutationDispatcher::RecordSuccessfulMutationSequence() { + + for (auto DE : CurrentDictionaryEntrySequence) { + + // PersistentAutoDictionary.AddWithSuccessCountOne(DE); + DE->IncSuccessCount(); + assert(DE->GetW().size()); + // Linear search is fine here as this happens seldom. + if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) + PersistentAutoDictionary.push_back({DE->GetW(), 1}); + + } + +} + +void MutationDispatcher::PrintRecommendedDictionary() { + + Vector V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE); + if (V.empty()) return; + Printf("###### Recommended dictionary. ######\n"); + for (auto &DE : V) { + + assert(DE.GetW().size()); + Printf("\""); + PrintASCII(DE.GetW(), "\""); + Printf(" # Uses: %zd\n", DE.GetUseCount()); + + } + + Printf("###### End of recommended dictionary. ######\n"); + +} + +void MutationDispatcher::PrintMutationSequence() { + + Printf("MS: %zd ", CurrentMutatorSequence.size()); + for (auto M : CurrentMutatorSequence) + Printf("%s-", M.Name); + if (!CurrentDictionaryEntrySequence.empty()) { + + Printf(" DE: "); + for (auto DE : CurrentDictionaryEntrySequence) { + + Printf("\""); + PrintASCII(DE->GetW(), "\"-"); + + } + + } + +} + +std::string MutationDispatcher::MutationSequence() { + + std::string MS; + for (auto M : CurrentMutatorSequence) { + + MS += M.Name; + MS += "-"; + + } + + return MS; + +} + +size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, Mutators); + +} + +size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, DefaultMutators); + +} + +// Mutates Data in place, returns new size. +size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, + size_t MaxSize, + Vector &Mutators) { + + assert(MaxSize > 0); + // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), + // in which case they will return 0. + // Try several times before returning un-mutated data. + for (int Iter = 0; Iter < 100; Iter++) { + + auto M = Mutators[Rand(Mutators.size())]; + size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); + if (NewSize && NewSize <= MaxSize) { + + if (Options.OnlyASCII) ToASCII(Data, NewSize); + CurrentMutatorSequence.push_back(M); + return NewSize; + + } + + } + + *Data = ' '; + return 1; // Fallback, should not happen frequently. + +} + +// Mask represents the set of Data bytes that are worth mutating. +size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size, + size_t MaxSize, + const Vector &Mask) { + + size_t MaskedSize = std::min(Size, Mask.size()); + // * Copy the worthy bytes into a temporary array T + // * Mutate T + // * Copy T back. + // This is totally unoptimized. + auto &T = MutateWithMaskTemp; + if (T.size() < Size) T.resize(Size); + size_t OneBits = 0; + for (size_t I = 0; I < MaskedSize; I++) + if (Mask[I]) T[OneBits++] = Data[I]; + + if (!OneBits) return 0; + assert(!T.empty()); + size_t NewSize = Mutate(T.data(), OneBits, OneBits); + assert(NewSize <= OneBits); + (void)NewSize; + // Even if NewSize < OneBits we still use all OneBits bytes. + for (size_t I = 0, J = 0; I < MaskedSize; I++) + if (Mask[I]) Data[I] = T[J++]; + return Size; + +} + +void MutationDispatcher::AddWordToManualDictionary(const Word &W) { + + ManualDictionary.push_back({W, std::numeric_limits::max()}); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMutate.h b/custom_mutators/libfuzzer/FuzzerMutate.h new file mode 100644 index 00000000..3ce3159f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.h @@ -0,0 +1,158 @@ +//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationDispatcher +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MUTATE_H +#define LLVM_FUZZER_MUTATE_H + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +namespace fuzzer { + +class MutationDispatcher { +public: + MutationDispatcher(Random &Rand, const FuzzingOptions &Options); + ~MutationDispatcher() {} + /// Indicate that we are about to start a new sequence of mutations. + void StartMutationSequence(); + /// Print the current sequence of mutations. + void PrintMutationSequence(); + /// Return the current sequence of mutations. + std::string MutationSequence(); + /// Indicate that the current sequence of mutations was successful. + void RecordSuccessfulMutationSequence(); + /// Mutates data by invoking user-provided mutator. + size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by invoking user-provided crossover. + size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by shuffling bytes. + size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by erasing bytes. + size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting a byte. + size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting several repeated bytes. + size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one byte. + size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one bit. + size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by copying/inserting a part of data into a different place. + size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the persistent automatic dictionary. + size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Tries to find an ASCII integer in Data, changes it to another ASCII int. + size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize); + /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways. + size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize); + + /// CrossOver Data with CrossOverWith. + size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations. + /// Returns the new size of data which could be up to MaxSize. + size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations to the bytes of Data + /// that have '1' in Mask. + /// Mask.size() should be >= Size. + size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize, + const Vector &Mask); + + /// Applies one of the default mutations. Provided as a service + /// to mutation authors. + size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Creates a cross-over of two pieces of Data, returns its size. + size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); + + void AddWordToManualDictionary(const Word &W); + + void PrintRecommendedDictionary(); + + void SetCrossOverWith(const Unit *U) { CrossOverWith = U; } + + Random &GetRand() { return Rand; } + + private: + struct Mutator { + size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); + const char *Name; + }; + + size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, + size_t MaxSize); + size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, + Vector &Mutators); + + size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize, size_t MaxToSize); + size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize); + size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, + DictionaryEntry &DE); + + template + DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2, + const void *Arg1Mutation, + const void *Arg2Mutation, + size_t ArgSize, + const uint8_t *Data, size_t Size); + + Random &Rand; + const FuzzingOptions Options; + + // Dictionary provided by the user via -dict=DICT_FILE. + Dictionary ManualDictionary; + // Temporary dictionary modified by the fuzzer itself, + // recreated periodically. + Dictionary TempAutoDictionary; + // Persistent dictionary modified by the fuzzer, consists of + // entries that led to successful discoveries in the past mutations. + Dictionary PersistentAutoDictionary; + + Vector CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const Unit *CrossOverWith = nullptr; + Vector MutateInPlaceHere; + Vector MutateWithMaskTemp; + // CustomCrossOver needs its own buffer as a custom implementation may call + // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. + Vector CustomCrossOverInPlaceHere; + + Vector Mutators; + Vector DefaultMutators; + Vector CurrentMutatorSequence; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MUTATE_H diff --git a/custom_mutators/libfuzzer/FuzzerOptions.h b/custom_mutators/libfuzzer/FuzzerOptions.h new file mode 100644 index 00000000..706e1c64 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerOptions.h @@ -0,0 +1,90 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::FuzzingOptions +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_OPTIONS_H +#define LLVM_FUZZER_OPTIONS_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +struct FuzzingOptions { + int Verbosity = 1; + size_t MaxLen = 0; + size_t LenControl = 1000; + bool KeepSeed = false; + int UnitTimeoutSec = 300; + int TimeoutExitCode = 70; + int OOMExitCode = 71; + int InterruptExitCode = 72; + int ErrorExitCode = 77; + bool IgnoreTimeouts = true; + bool IgnoreOOMs = true; + bool IgnoreCrashes = false; + int MaxTotalTimeSec = 0; + int RssLimitMb = 0; + int MallocLimitMb = 0; + bool DoCrossOver = true; + bool CrossOverUniformDist = false; + int MutateDepth = 5; + bool ReduceDepth = false; + bool UseCounters = false; + bool UseMemmem = true; + bool UseCmp = false; + int UseValueProfile = false; + bool Shrink = false; + bool ReduceInputs = false; + int ReloadIntervalSec = 1; + bool ShuffleAtStartUp = true; + bool PreferSmall = true; + size_t MaxNumberOfRuns = -1L; + int ReportSlowUnits = 10; + bool OnlyASCII = false; + bool Entropic = false; + size_t EntropicFeatureFrequencyThreshold = 0xFF; + size_t EntropicNumberOfRarestFeatures = 100; + bool EntropicScalePerExecTime = false; + std::string OutputCorpus; + std::string ArtifactPrefix = "./"; + std::string ExactArtifactPath; + std::string ExitOnSrcPos; + std::string ExitOnItem; + std::string FocusFunction; + std::string DataFlowTrace; + std::string CollectDataFlow; + std::string FeaturesDir; + std::string MutationGraphFile; + std::string StopFile; + bool SaveArtifacts = true; + bool PrintNEW = true; // Print a status line when new units are found; + bool PrintNewCovPcs = false; + int PrintNewCovFuncs = 0; + bool PrintFinalStats = false; + bool PrintCorpusStats = false; + bool PrintCoverage = false; + bool DumpCoverage = false; + bool DetectLeaks = true; + int PurgeAllocatorIntervalSec = 1; + int TraceMalloc = 0; + bool HandleAbrt = false; + bool HandleAlrm = false; + bool HandleBus = false; + bool HandleFpe = false; + bool HandleIll = false; + bool HandleInt = false; + bool HandleSegv = false; + bool HandleTerm = false; + bool HandleXfsz = false; + bool HandleUsr1 = false; + bool HandleUsr2 = false; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_OPTIONS_H diff --git a/custom_mutators/libfuzzer/FuzzerPlatform.h b/custom_mutators/libfuzzer/FuzzerPlatform.h new file mode 100644 index 00000000..8befdb88 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerPlatform.h @@ -0,0 +1,163 @@ +//===-- FuzzerPlatform.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Common platform macros. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_PLATFORM_H +#define LLVM_FUZZER_PLATFORM_H + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __NetBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 1 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __FreeBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 1 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __OpenBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 1 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 1 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __Fuchsia__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __EMSCRIPTEN__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 1 +#else +#error "Support for your platform has not been implemented" +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +// MSVC compiler is being used. +#define LIBFUZZER_MSVC 1 +#else +#define LIBFUZZER_MSVC 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#define LIBFUZZER_POSIX \ + (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) + +#ifdef __x86_64 +#if __has_attribute(target) +#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif + +#ifdef __clang__ // avoid gcc warning. +#if __has_attribute(no_sanitize) +#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#endif +#define ALWAYS_INLINE __attribute__((always_inline)) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#define ALWAYS_INLINE +#endif // __clang__ + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#else +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#endif + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_ALIGNED(X) __declspec(align(X)) +#define ATTRIBUTE_INTERFACE __declspec(dllexport) +// This is used for __sancov_lowest_stack which is needed for +// -fsanitize-coverage=stack-depth. That feature is not yet available on +// Windows, so make the symbol static to avoid linking errors. +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X))) +#define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local + +#define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#endif + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS +#elif __has_feature(memory_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif + +#endif // LLVM_FUZZER_PLATFORM_H diff --git a/custom_mutators/libfuzzer/FuzzerRandom.h b/custom_mutators/libfuzzer/FuzzerRandom.h new file mode 100644 index 00000000..659283ee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerRandom.h @@ -0,0 +1,38 @@ +//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Random +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_RANDOM_H +#define LLVM_FUZZER_RANDOM_H + +#include + +namespace fuzzer { +class Random : public std::minstd_rand { + public: + Random(unsigned int seed) : std::minstd_rand(seed) {} + result_type operator()() { return this->std::minstd_rand::operator()(); } + size_t Rand() { return this->operator()(); } + size_t RandBool() { return Rand() % 2; } + size_t SkewTowardsLast(size_t n) { + size_t T = this->operator()(n * n); + size_t Res = sqrt(T); + return Res; + } + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { + assert(From < To); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_RANDOM_H diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.cpp b/custom_mutators/libfuzzer/FuzzerSHA1.cpp new file mode 100644 index 00000000..0a58b661 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.cpp @@ -0,0 +1,269 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerSHA1.h" +#include "FuzzerDefs.h" +#include "FuzzerPlatform.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ + #define SHA_BIG_ENDIAN +// Windows is always little endian and MSVC doesn't have +#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS +/* override */ +#elif defined __BYTE_ORDER + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#else // ! defined __LITTLE_ENDIAN__ + #include // machine/endian.h + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#endif + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + + uint32_t buffer[BLOCK_LENGTH / 4]; + uint32_t state[HASH_LENGTH / 4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; + +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t *sha1_result(sha1nfo *s); + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; + +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + + return ((number << bits) | (number >> (32 - bits))); + +} + +void sha1_hashBlock(sha1nfo *s) { + + uint8_t i; + uint32_t a, b, c, d, e, t; + + a = s->state[0]; + b = s->state[1]; + c = s->state[2]; + d = s->state[3]; + e = s->state[4]; + for (i = 0; i < 80; i++) { + + if (i >= 16) { + + t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^ + s->buffer[(i + 2) & 15] ^ s->buffer[i & 15]; + s->buffer[i & 15] = sha1_rol32(t, 1); + + } + + if (i < 20) { + + t = (d ^ (b & (c ^ d))) + SHA1_K0; + + } else if (i < 40) { + + t = (b ^ c ^ d) + SHA1_K20; + + } else if (i < 60) { + + t = ((b & c) | (d & (b | c))) + SHA1_K40; + + } else { + + t = (b ^ c ^ d) + SHA1_K60; + + } + + t += sha1_rol32(a, 5) + e + s->buffer[i & 15]; + e = d; + d = c; + c = sha1_rol32(b, 30); + b = a; + a = t; + + } + + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; + +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + + uint8_t *const b = (uint8_t *)s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + + sha1_hashBlock(s); + s->bufferOffset = 0; + + } + +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + + ++s->byteCount; + sha1_addUncounted(s, data); + +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + + for (; len--;) + sha1_writebyte(s, (uint8_t)*data++); + +} + +void sha1_pad(sha1nfo *s) { + + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) + sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted( + s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); + +} + +uint8_t *sha1_result(sha1nfo *s) { + + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i = 0; i < 5; i++) { + + s->state[i] = (((s->state[i]) << 24) & 0xff000000) | + (((s->state[i]) << 8) & 0x00ff0000) | + (((s->state[i]) >> 8) & 0x0000ff00) | + (((s->state[i]) >> 24) & 0x000000ff); + + } + +#endif + + // Return pointer to hash (20 characters) + return (uint8_t *)s->state; + +} + +} // namespace + +namespace fuzzer { + +// The rest is added for LibFuzzer +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char *)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); + +} + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) { + + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i]; + return SS.str(); + +} + +std::string Hash(const Unit &U) { + + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + return Sha1ToString(Hash); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.h b/custom_mutators/libfuzzer/FuzzerSHA1.h new file mode 100644 index 00000000..05cbacda --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.h @@ -0,0 +1,32 @@ +//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// SHA1 utils. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHA1_H +#define LLVM_FUZZER_SHA1_H + +#include "FuzzerDefs.h" +#include +#include + +namespace fuzzer { + +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; + +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]); + +std::string Hash(const Unit &U); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHA1_H diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.cpp b/custom_mutators/libfuzzer/FuzzerTracePC.cpp new file mode 100644 index 00000000..1177325e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.cpp @@ -0,0 +1,819 @@ +//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Trace PCs. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. +// +//===----------------------------------------------------------------------===// + +#include "FuzzerTracePC.h" +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerPlatform.h" +#include "FuzzerUtil.h" +#include "FuzzerValueBitMap.h" +#include + +// Used by -fsanitize-coverage=stack-depth to track stack depth +ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack; + +namespace fuzzer { + +TracePC TPC; + +size_t TracePC::GetTotalPCCoverage() { + + return ObservedPCs.size(); + +} + +void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { + + if (Start == Stop) return; + if (NumModules && Modules[NumModules - 1].Start() == Start) return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + auto & M = Modules[NumModules++]; + uint8_t *AlignedStart = RoundUpByPage(Start); + uint8_t *AlignedStop = RoundDownByPage(Stop); + size_t NumFullPages = AlignedStop > AlignedStart + ? (AlignedStop - AlignedStart) / PageSize() + : 0; + bool NeedFirst = Start < AlignedStart || !NumFullPages; + bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart; + M.NumRegions = NumFullPages + NeedFirst + NeedLast; + ; + assert(M.NumRegions > 0); + M.Regions = new Module::Region[M.NumRegions]; + assert(M.Regions); + size_t R = 0; + if (NeedFirst) + M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false}; + for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize()) + M.Regions[R++] = {P, P + PageSize(), true, true}; + if (NeedLast) M.Regions[R++] = {AlignedStop, Stop, true, false}; + assert(R == M.NumRegions); + assert(M.Size() == (size_t)(Stop - Start)); + assert(M.Stop() == Stop); + assert(M.Start() == Start); + NumInline8bitCounters += M.Size(); + +} + +void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + + const PCTableEntry *B = reinterpret_cast(Start); + const PCTableEntry *E = reinterpret_cast(Stop); + if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return; + assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0])); + ModulePCTable[NumPCTables++] = {B, E}; + NumPCsInPCTables += E - B; + +} + +void TracePC::PrintModuleInfo() { + + if (NumModules) { + + Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ", + NumModules, NumInline8bitCounters); + for (size_t i = 0; i < NumModules; i++) + Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(), + Modules[i].Stop()); + Printf("\n"); + + } + + if (NumPCTables) { + + Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables, + NumPCsInPCTables); + for (size_t i = 0; i < NumPCTables; i++) { + + Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start, + ModulePCTable[i].Start, ModulePCTable[i].Stop); + + } + + Printf("\n"); + + if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) { + + Printf( + "ERROR: The size of coverage PC tables does not match the\n" + "number of instrumented PCs. This might be a compiler bug,\n" + "please contact the libFuzzer developers.\n" + "Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n" + "for possible workarounds (tl;dr: don't use the old GNU ld)\n"); + _Exit(1); + + } + + } + + if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) + Printf("INFO: %zd Extra Counters\n", NumExtraCounters); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { + + const uintptr_t kBits = 12; + const uintptr_t kMask = (1 << kBits) - 1; + uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); + ValueProfileMap.AddValueModPrime(Idx); + +} + +/// \return the address of the previous instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h` +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + +#if defined(__arm__) + // T32 (Thumb) branch instructions might be 16 or 32 bit long, + // so we return (pc-2) in that case in order to be safe. + // For A32 mode we return (pc-4) because all instructions are 32 bit long. + return (PC - 3) & (~1); +#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__) + // PCs are always 4 byte aligned. + return PC - 4; +#elif defined(__sparc__) || defined(__mips__) + return PC - 8; +#else + return PC - 1; +#endif + +} + +/// \return the address of the next instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp` +ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { + +#if defined(__mips__) + return PC + 8; +#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ + defined(__aarch64__) + return PC + 4; +#else + return PC + 1; +#endif + +} + +void TracePC::UpdateObservedPCs() { + + Vector CoveredFuncs; + auto ObservePC = [&](const PCTableEntry *TE) { + + if (ObservedPCs.insert(TE).second && DoPrintNewPCs) { + + PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", + GetNextInstructionPc(TE->PC)); + Printf("\n"); + + } + + }; + + auto Observe = [&](const PCTableEntry *TE) { + + if (PcIsFuncEntry(TE)) + if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs) + CoveredFuncs.push_back(TE->PC); + ObservePC(TE); + + }; + + if (NumPCsInPCTables) { + + if (NumInline8bitCounters == NumPCsInPCTables) { + + for (size_t i = 0; i < NumModules; i++) { + + auto &M = Modules[i]; + assert(M.Size() == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t r = 0; r < M.NumRegions; r++) { + + auto &R = M.Regions[r]; + if (!R.Enabled) continue; + for (uint8_t *P = R.Start; P < R.Stop; P++) + if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]); + + } + + } + + } + + } + + for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N; + i++) { + + Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size()); + PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i])); + Printf("\n"); + + } + +} + +uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) { + + size_t TotalTEs = 0; + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start; + TotalTEs += M.Stop - M.Start; + + } + + assert(0); + return 0; + +} + +const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto & M = ModulePCTable[i]; + size_t Size = M.Stop - M.Start; + if (Idx < Size) return &M.Start[Idx]; + Idx -= Size; + + } + + return nullptr; + +} + +static std::string GetModuleName(uintptr_t PC) { + + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PC), ModulePathRaw, sizeof(ModulePathRaw), + &OffsetRaw)) + return ""; + return ModulePathRaw; + +} + +template +void TracePC::IterateCoveredFunctions(CallBack CB) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + assert(M.Start < M.Stop); + auto ModuleName = GetModuleName(M.Start->PC); + for (auto NextFE = M.Start; NextFE < M.Stop;) { + + auto FE = NextFE; + assert(PcIsFuncEntry(FE) && "Not a function entry point"); + do { + + NextFE++; + + } while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE))); + + CB(FE, NextFE, ObservedFuncs[FE->PC]); + + } + + } + +} + +void TracePC::SetFocusFunction(const std::string &FuncName) { + + // This function should be called once. + assert(!FocusFunctionCounterPtr); + // "auto" is not a valid function name. If this function is called with "auto" + // that means the auto focus functionality failed. + if (FuncName.empty() || FuncName == "auto") return; + for (size_t M = 0; M < NumModules; M++) { + + auto & PCTE = ModulePCTable[M]; + size_t N = PCTE.Stop - PCTE.Start; + for (size_t I = 0; I < N; I++) { + + if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry. + auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC)); + if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ') + Name = Name.substr(3, std::string::npos); + if (FuncName != Name) continue; + Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); + FocusFunctionCounterPtr = Modules[M].Start() + I; + return; + + } + + } + + Printf( + "ERROR: Failed to set focus function. Make sure the function name is " + "valid (%s) and symbolization is enabled.\n", + FuncName.c_str()); + exit(1); + +} + +bool TracePC::ObservedFocusFunction() { + + return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; + +} + +void TracePC::PrintCoverage() { + + if (!EF->__sanitizer_symbolize_pc || + !EF->__sanitizer_get_module_and_offset_for_pc) { + + Printf( + "INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " not printing coverage\n"); + return; + + } + + Printf("COVERAGE:\n"); + auto CoveredFunctionCallback = [&](const PCTableEntry *First, + const PCTableEntry *Last, + uintptr_t Counter) { + + assert(First < Last); + auto VisualizePC = GetNextInstructionPc(First->PC); + std::string FileStr = DescribePC("%s", VisualizePC); + if (!IsInterestingCoverageFile(FileStr)) return; + std::string FunctionStr = DescribePC("%F", VisualizePC); + if (FunctionStr.find("in ") == 0) FunctionStr = FunctionStr.substr(3); + std::string LineStr = DescribePC("%l", VisualizePC); + size_t NumEdges = Last - First; + Vector UncoveredPCs; + for (auto TE = First; TE < Last; TE++) + if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC); + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + + }; + + IterateCoveredFunctions(CoveredFunctionCallback); + +} + +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// For cmp instructions the interesting value is a XOR of the parameters. +// The interesting value is mixed up with the PC and is then added to the map. + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero) { + + if (!n) return; + size_t Len = std::min(n, Word::GetMaxSize()); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + uint8_t B1[Word::kMaxSize]; + uint8_t B2[Word::kMaxSize]; + // Copy the data into locals in this non-msan-instrumented function + // to avoid msan complaining further. + size_t Hash = 0; // Compute some simple hash of both strings. + for (size_t i = 0; i < Len; i++) { + + B1[i] = A1[i]; + B2[i] = A2[i]; + size_t T = B1[i]; + Hash ^= (T << 8) | B2[i]; + + } + + size_t I = 0; + uint8_t HammingDistance = 0; + for (; I < Len; I++) { + + if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { + + HammingDistance = Popcountll(B1[I] ^ B2[I]); + break; + + } + + } + + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = (PC & 4095) | (I << 12); + Idx += HammingDistance; + ValueProfileMap.AddValue(Idx); + TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); + +} + +template +ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE ATTRIBUTE_NO_SANITIZE_ALL void +TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { + + uint64_t ArgXor = Arg1 ^ Arg2; + if (sizeof(T) == 4) + TORC4.Insert(ArgXor, Arg1, Arg2); + else if (sizeof(T) == 8) + TORC8.Insert(ArgXor, Arg1, Arg2); + uint64_t HammingDistance = Popcountll(ArgXor); // [0,64] + uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1); + ValueProfileMap.AddValue(PC * 128 + HammingDistance); + ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance); + +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; + +} + +// Finds min of (strlen(S1), strlen(S2)). +// Needed bacause one of these strings may actually be non-zero terminated. +static size_t InternalStrnlen2(const char *S1, const char *S2) { + + size_t Len = 0; + for (; S1[Len] && S2[Len]; Len++) {} + return Len; + +} + +void TracePC::ClearInlineCounters() { + + IterateCounterRegions([](const Module::Region &R) { + + if (R.Enabled) memset(R.Start, 0, R.Stop - R.Start); + + }); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::RecordInitialStack() { + + int stack; + __sancov_lowest_stack = InitialStack = reinterpret_cast(&stack); + +} + +uintptr_t TracePC::GetMaxStackOffset() const { + + return InitialStack - __sancov_lowest_stack; // Stack grows down + +} + +void WarnAboutDeprecatedInstrumentation(const char *flag) { + + // Use RawPrint because Printf cannot be used on Windows before OutputFile is + // initialized. + RawPrint(flag); + RawPrint( + " is no longer supported by libFuzzer.\n" + "Please either migrate to a compiler that supports -fsanitize=fuzzer\n" + "or use an older version of libFuzzer\n"); + exit(1); + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +// Best-effort support for -fsanitize-coverage=trace-pc, which is available +// in both Clang and GCC. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc() { + + fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) { + + fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, + const uintptr_t *pcs_end) { + + fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCallerCallee(PC, Callee); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic +// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however, +// should be changed later to make full use of instrumentation. +void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + + uint64_t N = Cases[0]; + uint64_t ValSizeInBits = Cases[1]; + uint64_t *Vals = Cases + 2; + // Skip the most common and the most boring case: all switch values are small. + // We may want to skip this at compile-time, but it will make the + // instrumentation less general. + if (Vals[N - 1] < 256) return; + // Also skip small inputs values, they won't give good signal. + if (Val < 256) return; + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + size_t i; + uint64_t Smaller = 0; + uint64_t Larger = ~(uint64_t)0; + // Find two switch values such that Smaller < Val < Larger. + // Use 0 and 0xfff..f as the defaults. + for (i = 0; i < N; i++) { + + if (Val < Vals[i]) { + + Larger = Vals[i]; + break; + + } + + if (Val > Vals[i]) Smaller = Vals[i]; + + } + + // Apply HandleCmp to {Val,Smaller} and {Val, Larger}, + // use i as the PC modifier for HandleCmp. + if (ValSizeInBits == 16) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint16_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint16_t)(Larger)); + + } else if (ValSizeInBits == 32) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint32_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint32_t)(Larger)); + + } else { + + fuzzer::TPC.HandleCmp(PC + 2 * i, Val, Smaller); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, Val, Larger); + + } + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div4(uint32_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div8(uint64_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ false); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = fuzzer::InternalStrnlen(s1, n); + size_t Len2 = fuzzer::InternalStrnlen(s2, n); + n = std::min(n, Len1); + n = std::min(n, Len2); + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, + int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t N = fuzzer::InternalStrnlen2(s1, s2); + if (N <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strstr(void *called_pc, const char *s1, const char *s2, + char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), len2); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.h b/custom_mutators/libfuzzer/FuzzerTracePC.h new file mode 100644 index 00000000..4601300c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.h @@ -0,0 +1,291 @@ +//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::TracePC +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_TRACE_PC +#define LLVM_FUZZER_TRACE_PC + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerValueBitMap.h" + +#include +#include + +namespace fuzzer { + +// TableOfRecentCompares (TORC) remembers the most recently performed +// comparisons of type T. +// We record the arguments of CMP instructions in this table unconditionally +// because it seems cheaper this way than to compute some expensive +// conditions inside __sanitizer_cov_trace_cmp*. +// After the unit has been executed we may decide to use the contents of +// this table to populate a Dictionary. +template +struct TableOfRecentCompares { + static const size_t kSize = kSizeT; + struct Pair { + T A, B; + }; + ATTRIBUTE_NO_SANITIZE_ALL + void Insert(size_t Idx, const T &Arg1, const T &Arg2) { + Idx = Idx % kSize; + Table[Idx].A = Arg1; + Table[Idx].B = Arg2; + } + + Pair Get(size_t I) { return Table[I % kSize]; } + + Pair Table[kSize]; +}; + +template +struct MemMemTable { + static const size_t kSize = kSizeT; + Word MemMemWords[kSize]; + Word EmptyWord; + + void Add(const uint8_t *Data, size_t Size) { + if (Size <= 2) return; + Size = std::min(Size, Word::GetMaxSize()); + size_t Idx = SimpleFastHash(Data, Size) % kSize; + MemMemWords[Idx].Set(Data, Size); + } + const Word &Get(size_t Idx) { + for (size_t i = 0; i < kSize; i++) { + const Word &W = MemMemWords[(Idx + i) % kSize]; + if (W.size()) return W; + } + EmptyWord.Set(nullptr, 0); + return EmptyWord; + } +}; + +class TracePC { + public: + void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop); + void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); + template void HandleCmp(uintptr_t PC, T Arg1, T Arg2); + size_t GetTotalPCCoverage(); + void SetUseCounters(bool UC) { UseCounters = UC; } + void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; } + void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } + void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } + void UpdateObservedPCs(); + template void CollectFeatures(Callback CB) const; + + void ResetMaps() { + ValueProfileMap.Reset(); + ClearExtraCounters(); + ClearInlineCounters(); + } + + void ClearInlineCounters(); + + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); + void PrintFeatureSet(); + + void PrintModuleInfo(); + + void PrintCoverage(); + + template + void IterateCoveredFunctions(CallBack CB); + + void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero); + + TableOfRecentCompares TORC4; + TableOfRecentCompares TORC8; + TableOfRecentCompares TORCW; + MemMemTable<1024> MMT; + + void RecordInitialStack(); + uintptr_t GetMaxStackOffset() const; + + template + void ForEachObservedPC(CallBack CB) { + for (auto PC : ObservedPCs) + CB(PC); + } + + void SetFocusFunction(const std::string &FuncName); + bool ObservedFocusFunction(); + + struct PCTableEntry { + uintptr_t PC, PCFlags; + }; + + uintptr_t PCTableEntryIdx(const PCTableEntry *TE); + const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx); + static uintptr_t GetNextInstructionPc(uintptr_t PC); + bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + +private: + bool UseCounters = false; + uint32_t UseValueProfileMask = false; + bool DoPrintNewPCs = false; + size_t NumPrintNewFuncs = 0; + + // Module represents the array of 8-bit counters split into regions + // such that every region, except maybe the first and the last one, is one + // full page. + struct Module { + struct Region { + uint8_t *Start, *Stop; + bool Enabled; + bool OneFullPage; + }; + Region *Regions; + size_t NumRegions; + uint8_t *Start() { return Regions[0].Start; } + uint8_t *Stop() { return Regions[NumRegions - 1].Stop; } + size_t Size() { return Stop() - Start(); } + size_t Idx(uint8_t *P) { + assert(P >= Start() && P < Stop()); + return P - Start(); + } + }; + + Module Modules[4096]; + size_t NumModules; // linker-initialized. + size_t NumInline8bitCounters; + + template + void IterateCounterRegions(Callback CB) { + for (size_t m = 0; m < NumModules; m++) + for (size_t r = 0; r < Modules[m].NumRegions; r++) + CB(Modules[m].Regions[r]); + } + + struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096]; + size_t NumPCTables; + size_t NumPCsInPCTables; + + Set ObservedPCs; + std::unordered_map ObservedFuncs; // PC => Counter. + + uint8_t *FocusFunctionCounterPtr = nullptr; + + ValueBitMap ValueProfileMap; + uintptr_t InitialStack; +}; + +template +// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value); +ATTRIBUTE_NO_SANITIZE_ALL +size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, + size_t FirstFeature, Callback Handle8bitCounter) { + typedef uintptr_t LargeType; + const size_t Step = sizeof(LargeType) / sizeof(uint8_t); + const size_t StepMask = Step - 1; + auto P = Begin; + // Iterate by 1 byte until either the alignment boundary or the end. + for (; reinterpret_cast(P) & StepMask && P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + + // Iterate by Step bytes at a time. + for (; P < End; P += Step) + if (LargeType Bundle = *reinterpret_cast(P)) { + Bundle = HostToLE(Bundle); + for (size_t I = 0; I < Step; I++, Bundle >>= 8) + if (uint8_t V = Bundle & 0xff) + Handle8bitCounter(FirstFeature, P - Begin + I, V); + } + + // Iterate by 1 byte until the end. + for (; P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + return End - Begin; +} + +// Given a non-zero Counter returns a number in the range [0,7]. +template +unsigned CounterToFeature(T Counter) { + // Returns a feature number by placing Counters into buckets as illustrated + // below. + // + // Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+] + // Feature number: 0 1 2 3 4 5 6 7 + // + // This is a heuristic taken from AFL (see + // http://lcamtuf.coredump.cx/afl/technical_details.txt). + // + // This implementation may change in the future so clients should + // not rely on it. + assert(Counter); + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + return Bit; +} + +template // void Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +ATTRIBUTE_NOINLINE +void TracePC::CollectFeatures(Callback HandleFeature) const { + auto Handle8bitCounter = [&](size_t FirstFeature, + size_t Idx, uint8_t Counter) { + if (UseCounters) + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); + else + HandleFeature(FirstFeature + Idx); + }; + + size_t FirstFeature = 0; + + for (size_t i = 0; i < NumModules; i++) { + for (size_t r = 0; r < Modules[i].NumRegions; r++) { + if (!Modules[i].Regions[r].Enabled) continue; + FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start, + Modules[i].Regions[r].Stop, + FirstFeature, Handle8bitCounter); + } + } + + FirstFeature += + 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), + FirstFeature, Handle8bitCounter); + + if (UseValueProfileMask) { + ValueProfileMap.ForEach([&](size_t Idx) { + HandleFeature(FirstFeature + Idx); + }); + FirstFeature += ValueProfileMap.SizeInBits(); + } + + // Step function, grows similar to 8 * Log_2(A). + auto StackDepthStepFunction = [](uint32_t A) -> uint32_t { + if (!A) return A; + uint32_t Log2 = Log(A); + if (Log2 < 3) return A; + Log2 -= 3; + return (Log2 + 1) * 8 + ((A >> Log2) & 7); + }; + assert(StackDepthStepFunction(1024) == 64); + assert(StackDepthStepFunction(1024 * 4) == 80); + assert(StackDepthStepFunction(1024 * 1024) == 144); + + if (auto MaxStackOffset = GetMaxStackOffset()) + HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); +} + +extern TracePC TPC; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_TRACE_PC diff --git a/custom_mutators/libfuzzer/FuzzerUtil.cpp b/custom_mutators/libfuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000..7c395f7d --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.cpp @@ -0,0 +1,314 @@ +//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils. +//===----------------------------------------------------------------------===// + +#include "FuzzerUtil.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +void PrintHexArray(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + Printf("0x%x,", (unsigned)Data[i]); + Printf("%s", PrintAfter); + +} + +void Print(const Unit &v, const char *PrintAfter) { + + PrintHexArray(v.data(), v.size(), PrintAfter); + +} + +void PrintASCIIByte(uint8_t Byte) { + + if (Byte == '\\') + Printf("\\\\"); + else if (Byte == '"') + Printf("\\\""); + else if (Byte >= 32 && Byte < 127) + Printf("%c", Byte); + else + Printf("\\x%02x", Byte); + +} + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + PrintASCIIByte(Data[i]); + Printf("%s", PrintAfter); + +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + + PrintASCII(U.data(), U.size(), PrintAfter); + +} + +bool ToASCII(uint8_t *Data, size_t Size) { + + bool Changed = false; + for (size_t i = 0; i < Size; i++) { + + uint8_t &X = Data[i]; + auto NewX = X; + NewX &= 127; + if (!isspace(NewX) && !isprint(NewX)) NewX = ' '; + Changed |= NewX != X; + X = NewX; + + } + + return Changed; + +} + +bool IsASCII(const Unit &U) { + + return IsASCII(U.data(), U.size()); + +} + +bool IsASCII(const uint8_t *Data, size_t Size) { + + for (size_t i = 0; i < Size; i++) + if (!(isprint(Data[i]) || isspace(Data[i]))) return false; + return true; + +} + +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { + + U->clear(); + if (Str.empty()) return false; + size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. + // Skip spaces from both sides. + while (L < R && isspace(Str[L])) + L++; + while (R > L && isspace(Str[R])) + R--; + if (R - L < 2) return false; + // Check the closing " + if (Str[R] != '"') return false; + R--; + // Find the opening " + while (L < R && Str[L] != '"') + L++; + if (L >= R) return false; + assert(Str[L] == '\"'); + L++; + assert(L <= R); + for (size_t Pos = L; Pos <= R; Pos++) { + + uint8_t V = (uint8_t)Str[Pos]; + if (!isprint(V) && !isspace(V)) return false; + if (V == '\\') { + + // Handle '\\' + if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { + + U->push_back(Str[Pos + 1]); + Pos++; + continue; + + } + + // Handle '\xAB' + if (Pos + 3 <= R && Str[Pos + 1] == 'x' && isxdigit(Str[Pos + 2]) && + isxdigit(Str[Pos + 3])) { + + char Hex[] = "0xAA"; + Hex[2] = Str[Pos + 2]; + Hex[3] = Str[Pos + 3]; + U->push_back(strtol(Hex, nullptr, 16)); + Pos += 3; + continue; + + } + + return false; // Invalid escape. + + } else { + + // Any other character. + U->push_back(V); + + } + + } + + return true; + +} + +bool ParseDictionaryFile(const std::string &Text, Vector *Units) { + + if (Text.empty()) { + + Printf("ParseDictionaryFile: file does not exist or is empty\n"); + return false; + + } + + std::istringstream ISS(Text); + Units->clear(); + Unit U; + int LineNo = 0; + std::string S; + while (std::getline(ISS, S, '\n')) { + + LineNo++; + size_t Pos = 0; + while (Pos < S.size() && isspace(S[Pos])) + Pos++; // Skip spaces. + if (Pos == S.size()) continue; // Empty line. + if (S[Pos] == '#') continue; // Comment line. + if (ParseOneDictionaryEntry(S, &U)) { + + Units->push_back(U); + + } else { + + Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo, + S.c_str()); + return false; + + } + + } + + return true; + +} + +// Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h +std::string Base64(const Unit &U) { + + static const char Table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Buffer; + Buffer.resize(((U.size() + 2) / 3) * 4); + + size_t i = 0, j = 0; + for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) | + (unsigned char)U[i + 2]; + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = Table[x & 63]; + + } + + if (i + 1 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = '='; + Buffer[j + 3] = '='; + + } else if (i + 2 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = '='; + + } + + return Buffer; + +} + +static std::mutex SymbolizeMutex; + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) + return ""; + char PcDescr[1024] = {}; + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, + PcDescr, sizeof(PcDescr)); + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; + +} + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { + + if (EF->__sanitizer_symbolize_pc) + Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); + else + Printf(FallbackFMT, PC); + +} + +void PrintStackTrace() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_stack_trace && l.owns_lock()) + EF->__sanitizer_print_stack_trace(); + +} + +void PrintMemoryProfile() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_memory_profile && l.owns_lock()) + EF->__sanitizer_print_memory_profile(95, 8); + +} + +unsigned NumberOfCpuCores() { + + unsigned N = std::thread::hardware_concurrency(); + if (!N) { + + Printf( + "WARNING: std::thread::hardware_concurrency not well defined for " + "your platform. Assuming CPU count of 1.\n"); + N = 1; + + } + + return N; + +} + +size_t SimpleFastHash(const uint8_t *Data, size_t Size) { + + size_t Res = 0; + for (size_t i = 0; i < Size; i++) + Res = Res * 11 + Data[i]; + return Res; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerUtil.h b/custom_mutators/libfuzzer/FuzzerUtil.h new file mode 100644 index 00000000..e90be085 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.h @@ -0,0 +1,117 @@ +//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Util functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_UTIL_H +#define LLVM_FUZZER_UTIL_H + +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCommand.h" +#include "FuzzerDefs.h" + +namespace fuzzer { + +void PrintHexArray(const Unit &U, const char *PrintAfter = ""); + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter = ""); + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = ""); + +void PrintASCII(const Unit &U, const char *PrintAfter = ""); + +// Changes U to contain only ASCII (isprint+isspace) characters. +// Returns true iff U has been changed. +bool ToASCII(uint8_t *Data, size_t Size); + +bool IsASCII(const Unit &U); + +bool IsASCII(const uint8_t *Data, size_t Size); + +std::string Base64(const Unit &U); + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); + +void PrintStackTrace(); + +void PrintMemoryProfile(); + +unsigned NumberOfCpuCores(); + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions& Options); + +void SleepSeconds(int Seconds); + +unsigned long GetPid(); + +size_t GetPeakRSSMb(); + +int ExecuteCommand(const Command &Cmd); +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput); + +// Fuchsia does not have popen/pclose. +FILE *OpenProcessPipe(const char *Command, const char *Mode); +int CloseProcessPipe(FILE *F); + +const void *SearchMemory(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +std::string CloneArgsWithoutX(const Vector &Args, + const char *X1, const char *X2); + +inline std::string CloneArgsWithoutX(const Vector &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +inline std::pair SplitBefore(std::string X, + std::string S) { + auto Pos = S.find(X); + if (Pos == std::string::npos) + return std::make_pair(S, ""); + return std::make_pair(S.substr(0, Pos), S.substr(Pos)); +} + +void DiscardOutput(int Fd); + +std::string DisassembleCmd(const std::string &FileName); + +std::string SearchRegexCmd(const std::string &Regex); + +size_t SimpleFastHash(const uint8_t *Data, size_t Size); + +inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; } + +inline size_t PageSize() { return 4096; } +inline uint8_t *RoundUpByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = (X + Mask) & ~Mask; + return reinterpret_cast(X); +} +inline uint8_t *RoundDownByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = X & ~Mask; + return reinterpret_cast(X); +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +template T HostToLE(T X) { return X; } +#else +template T HostToLE(T X) { return Bswap(X); } +#endif + +} // namespace fuzzer + +#endif // LLVM_FUZZER_UTIL_H diff --git a/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp new file mode 100644 index 00000000..420d8c23 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp @@ -0,0 +1,205 @@ +//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Darwin. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + +// There is no header for this on macOS so declare here +extern "C" char **environ; + +namespace fuzzer { + +static std::mutex SignalMutex; +// Global variables used to keep track of how signal handling should be +// restored. They should **not** be accessed without holding `SignalMutex`. +static int ActiveThreadCount = 0; +static struct sigaction OldSigIntAction; +static struct sigaction OldSigQuitAction; +static sigset_t OldBlockedSignalsSet; + +// This is a reimplementation of Libc's `system()`. On Darwin the Libc +// implementation contains a mutex which prevents it from being used +// concurrently. This implementation **can** be used concurrently. It sets the +// signal handlers when the first thread enters and restores them when the last +// thread finishes execution of the function and ensures this is not racey by +// using a mutex. +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + posix_spawnattr_t SpawnAttributes; + if (posix_spawnattr_init(&SpawnAttributes)) return -1; + // Block and ignore signals of the current process when the first thread + // enters. + { + + std::lock_guard Lock(SignalMutex); + if (ActiveThreadCount == 0) { + + static struct sigaction IgnoreSignalAction; + sigset_t BlockedSignalsSet; + memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); + IgnoreSignalAction.sa_handler = SIG_IGN; + + if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { + + Printf("Failed to ignore SIGINT\n"); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { + + Printf("Failed to ignore SIGQUIT\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + (void)sigemptyset(&BlockedSignalsSet); + (void)sigaddset(&BlockedSignalsSet, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == + -1) { + + Printf("Failed to block SIGCHLD\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + } + + ++ActiveThreadCount; + + } + + // NOTE: Do not introduce any new `return` statements past this + // point. It is important that `ActiveThreadCount` always be decremented + // when leaving this function. + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigset_t DefaultSigSet; + (void)sigemptyset(&DefaultSigSet); + (void)sigaddset(&DefaultSigSet, SIGQUIT); + (void)sigaddset(&DefaultSigSet, SIGINT); + (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + // Make sure the child process doesn't block SIGCHLD + (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); + short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + pid_t Pid; + char ** Environ = environ; // Read from global + const char *CommandCStr = CmdLine.c_str(); + char *const Argv[] = {strdup("sh"), strdup("-c"), strdup(CommandCStr), NULL}; + int ErrorCode = 0, ProcessStatus = 0; + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = + posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, Argv, Environ); + (void)posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + + pid_t SavedPid = Pid; + do { + + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + + } while (Pid == -1 && errno == EINTR); + + if (Pid == -1) { + + // Fail for some other reason. + ProcessStatus = -1; + + } + + } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + + // Fork failure. + ProcessStatus = -1; + + } else { + + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + + } + + for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i) + free(Argv[i]); + + // Restore the signal handlers of the current process when the last thread + // using this function finishes. + { + + std::lock_guard Lock(SignalMutex); + --ActiveThreadCount; + if (ActiveThreadCount == 0) { + + bool FailedRestore = false; + if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { + + Printf("Failed to restore SIGINT handling\n"); + FailedRestore = true; + + } + + if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { + + Printf("Failed to restore SIGQUIT handling\n"); + FailedRestore = true; + + } + + if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { + + Printf("Failed to unblock SIGCHLD\n"); + FailedRestore = true; + + } + + if (FailedRestore) ProcessStatus = -1; + + } + + } + + return ProcessStatus; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp new file mode 100644 index 00000000..45ecbca8 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp @@ -0,0 +1,658 @@ +//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Fuchsia/Zircon APIs. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" + +#if LIBFUZZER_FUCHSIA + + #include "FuzzerInternal.h" + #include "FuzzerUtil.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + +namespace fuzzer { + +// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written +// around, the general approach is to spin up dedicated threads to watch for +// each requested condition (alarm, interrupt, crash). Of these, the crash +// handler is the most involved, as it requires resuming the crashed thread in +// order to invoke the sanitizers to get the needed state. + +// Forward declaration of assembly trampoline needed to resume crashed threads. +// This appears to have external linkage to C++, which is why it's not in the +// anonymous namespace. The assembly definition inside MakeTrampoline() +// actually defines the symbol with internal linkage only. +void CrashTrampolineAsm() __asm__("CrashTrampolineAsm"); + +namespace { + +// Helper function to handle Zircon syscall failures. +void ExitOnErr(zx_status_t Status, const char *Syscall) { + + if (Status != ZX_OK) { + + Printf("libFuzzer: %s failed: %s\n", Syscall, + _zx_status_get_string(Status)); + exit(1); + + } + +} + +void AlarmHandler(int Seconds) { + + while (true) { + + SleepSeconds(Seconds); + Fuzzer::StaticAlarmCallback(); + + } + +} + +void InterruptHandler() { + + fd_set readfds; + // Ctrl-C sends ETX in Zircon. + do { + + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr); + + } while (!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03); + + Fuzzer::StaticInterruptCallback(); + +} + + // CFAOffset is used to reference the stack pointer before entering the + // trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping + // to the trampoline we copy all the registers onto the stack. We need to make + // sure that the new stack has enough space to store all the registers. + // + // The trampoline holds CFI information regarding the registers stored in the + // stack, which is then used by the unwinder to restore them. + #if defined(__x86_64__) +// In x86_64 the crashing function might also be using the red zone (128 bytes +// on top of their rsp). +constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t); + #elif defined(__aarch64__) +// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so +// we make sure that we are keeping that same alignment. +constexpr size_t CFAOffset = + (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; + #endif + + // For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback + // without POSIX signal handlers. To achieve this, we use an assembly + // function to add the necessary CFI unwinding information and a C function to + // bridge from that back into C++. + + // FIXME: This works as a short-term solution, but this code really shouldn't + // be architecture dependent. A better long term solution is to implement + // remote unwinding and expose the necessary APIs through sanitizer_common + // and/or ASAN to allow the exception handling thread to gather the crash + // state directly. + // + // Alternatively, Fuchsia may in future actually implement basic signal + // handling for the machine trap signals. + #if defined(__x86_64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_REG(rax) \ + OP_REG(rbx) \ + OP_REG(rcx) \ + OP_REG(rdx) \ + OP_REG(rsi) \ + OP_REG(rdi) \ + OP_REG(rbp) \ + OP_REG(rsp) \ + OP_REG(r8) \ + OP_REG(r9) \ + OP_REG(r10) \ + OP_REG(r11) \ + OP_REG(r12) \ + OP_REG(r13) \ + OP_REG(r14) \ + OP_REG(r15) \ + OP_REG(rip) + + #elif defined(__aarch64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_NUM(0) \ + OP_NUM(1) \ + OP_NUM(2) \ + OP_NUM(3) \ + OP_NUM(4) \ + OP_NUM(5) \ + OP_NUM(6) \ + OP_NUM(7) \ + OP_NUM(8) \ + OP_NUM(9) \ + OP_NUM(10) \ + OP_NUM(11) \ + OP_NUM(12) \ + OP_NUM(13) \ + OP_NUM(14) \ + OP_NUM(15) \ + OP_NUM(16) \ + OP_NUM(17) \ + OP_NUM(18) \ + OP_NUM(19) \ + OP_NUM(20) \ + OP_NUM(21) \ + OP_NUM(22) \ + OP_NUM(23) \ + OP_NUM(24) \ + OP_NUM(25) \ + OP_NUM(26) \ + OP_NUM(27) \ + OP_NUM(28) \ + OP_NUM(29) \ + OP_REG(sp) + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Produces a CFI directive for the named or numbered register. + // The value used refers to an assembler immediate operand with the same name + // as the register (see ASM_OPERAND_REG). + #define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n" + #define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num) + + // Produces an assembler immediate operand for the named or numbered register. + // This operand contains the offset of the register relative to the CFA. + #define ASM_OPERAND_REG(reg) \ + [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), + #define ASM_OPERAND_NUM(num) \ + [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset), + +// Trampoline to bridge from the assembly below to the static C++ crash +// callback. +__attribute__((noreturn)) static void StaticCrashHandler() { + + Fuzzer::StaticCrashSignalCallback(); + for (;;) { + + _Exit(1); + + } + +} + +// Creates the trampoline with the necessary CFI information to unwind through +// to the crashing call stack: +// * Defining the CFA so that it points to the stack pointer at the point +// of crash. +// * Storing all registers at the point of crash in the stack and refer to them +// via CFI information (relative to the CFA). +// * Setting the return column so the unwinder knows how to continue unwinding. +// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler. +// * Calling StaticCrashHandler that will trigger the unwinder. +// +// The __attribute__((used)) is necessary because the function +// is never called; it's just a container around the assembly to allow it to +// use operands for compile-time computed constants. +__attribute__((used)) void MakeTrampoline() { + + __asm__(".cfi_endproc\n" + ".pushsection .text.CrashTrampolineAsm\n" + ".type CrashTrampolineAsm,STT_FUNC\n" +"CrashTrampolineAsm:\n" + ".cfi_startproc simple\n" + ".cfi_signal_frame\n" + #if defined(__x86_64__) + ".cfi_return_column rip\n" + ".cfi_def_cfa rsp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + "mov %%rsp, %%rbp\n" + ".cfi_def_cfa_register rbp\n" + "andq $-16, %%rsp\n" + "call %c[StaticCrashHandler]\n" + "ud2\n" + #elif defined(__aarch64__) + ".cfi_return_column 33\n" + ".cfi_def_cfa sp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + ".cfi_offset 33, %c[pc]\n" + ".cfi_offset 30, %c[lr]\n" + "bl %c[StaticCrashHandler]\n" + "brk 1\n" + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + ".cfi_endproc\n" + ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n" + ".popsection\n" + ".cfi_startproc\n" + : // No outputs + : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) + #if defined(__aarch64__) + ASM_OPERAND_REG(pc) + ASM_OPERAND_REG(lr) + #endif + [StaticCrashHandler] "i" (StaticCrashHandler), + [CFAOffset] "i" (CFAOffset)); + +} + +void CrashHandler(zx_handle_t *Event) { + + // This structure is used to ensure we close handles to objects we create in + // this handler. + struct ScopedHandle { + + ~ScopedHandle() { + + _zx_handle_close(Handle); + + } + + zx_handle_t Handle = ZX_HANDLE_INVALID; + + }; + + // Create the exception channel. We need to claim to be a "debugger" so the + // kernel will allow us to modify and resume dying threads (see below). Once + // the channel is set, we can signal the main thread to continue and wait + // for the exception to arrive. + ScopedHandle Channel; + zx_handle_t Self = _zx_process_self(); + ExitOnErr(_zx_task_create_exception_channel( + Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle), + "_zx_task_create_exception_channel"); + + ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal"); + + // This thread lives as long as the process in order to keep handling + // crashes. In practice, the first crashed thread to reach the end of the + // StaticCrashHandler will end the process. + while (true) { + + ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one"); + + zx_exception_info_t ExceptionInfo; + ScopedHandle Exception; + ExitOnErr( + _zx_channel_read(Channel.Handle, 0, &ExceptionInfo, &Exception.Handle, + sizeof(ExceptionInfo), 1, nullptr, nullptr), + "_zx_channel_read"); + + // Ignore informational synthetic exceptions. + if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type || + ZX_EXCP_THREAD_EXITING == ExceptionInfo.type || + ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) { + + continue; + + } + + // At this point, we want to get the state of the crashing thread, but + // libFuzzer and the sanitizers assume this will happen from that same + // thread via a POSIX signal handler. "Resurrecting" the thread in the + // middle of the appropriate callback is as simple as forcibly setting the + // instruction pointer/program counter, provided we NEVER EVER return from + // that function (since otherwise our stack will not be valid). + ScopedHandle Thread; + ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle), + "_zx_exception_get_thread"); + + zx_thread_state_general_regs_t GeneralRegisters; + ExitOnErr( + _zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_read_state"); + + // To unwind properly, we need to push the crashing thread's register state + // onto the stack and jump into a trampoline with CFI instructions on how + // to restore it. + #if defined(__x86_64__) + uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.rsp = StackPtr; + GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); + + #elif defined(__aarch64__) + uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.sp = StackPtr; + GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Now force the crashing thread's state. + ExitOnErr( + _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_write_state"); + + // Set the exception to HANDLED so it resumes the thread on close. + uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED; + ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE, + &ExceptionState, sizeof(ExceptionState)), + "zx_object_set_property"); + + } + +} + +} // namespace + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions &Options) { + + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + + // Set up alarm handler if needed. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) { + + std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); + T.detach(); + + } + + // Set up interrupt handler if needed. + if (Options.HandleInt || Options.HandleTerm) { + + std::thread T(InterruptHandler); + T.detach(); + + } + + // Early exit if no crash handler needed. + if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll && + !Options.HandleFpe && !Options.HandleAbrt) + return; + + // Set up the crash handler and wait until it is ready before proceeding. + zx_handle_t Event; + ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create"); + + std::thread T(CrashHandler, &Event); + zx_status_t Status = + _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr); + _zx_handle_close(Event); + ExitOnErr(Status, "_zx_object_wait_one"); + + T.detach(); + +} + +void SleepSeconds(int Seconds) { + + _zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds))); + +} + +unsigned long GetPid() { + + zx_status_t rc; + zx_info_handle_basic_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return Info.koid; + +} + +size_t GetPeakRSSMb() { + + zx_status_t rc; + zx_info_task_stats_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20; + +} + +template +class RunOnDestruction { + + public: + explicit RunOnDestruction(Fn fn) : fn_(fn) { + + } + + ~RunOnDestruction() { + + fn_(); + + } + + private: + Fn fn_; + +}; + +template +RunOnDestruction at_scope_exit(Fn fn) { + + return RunOnDestruction(fn); + +} + +static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) { + + return { + + .action = FDIO_SPAWN_ACTION_CLONE_FD, + .fd = + { + + .local_fd = localFd, + .target_fd = targetFd, + + }, + + }; + +} + +int ExecuteCommand(const Command &Cmd) { + + zx_status_t rc; + + // Convert arguments to C array + auto Args = Cmd.getArguments(); + size_t Argc = Args.size(); + assert(Argc != 0); + std::unique_ptr Argv(new const char *[Argc + 1]); + for (size_t i = 0; i < Argc; ++i) + Argv[i] = Args[i].c_str(); + Argv[Argc] = nullptr; + + // Determine output. On Fuchsia, the fuzzer is typically run as a component + // that lacks a mutable working directory. Fortunately, when this is the case + // a mutable output directory must be specified using "-artifact_prefix=...", + // so write the log file(s) there. + // However, we don't want to apply this logic for absolute paths. + int FdOut = STDOUT_FILENO; + bool discardStdout = false; + bool discardStderr = false; + + if (Cmd.hasOutputFile()) { + + std::string Path = Cmd.getOutputFile(); + if (Path == getDevNull()) { + + // On Fuchsia, there's no "/dev/null" like-file, so we + // just don't copy the FDs into the spawned process. + discardStdout = true; + + } else { + + bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/'; + if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix")) + Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path; + + FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0); + if (FdOut == -1) { + + Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(), + strerror(errno)); + return ZX_ERR_IO; + + } + + } + + } + + auto CloseFdOut = at_scope_exit([FdOut]() { + + if (FdOut != STDOUT_FILENO) close(FdOut); + + }); + + // Determine stderr + int FdErr = STDERR_FILENO; + if (Cmd.isOutAndErrCombined()) { + + FdErr = FdOut; + if (discardStdout) discardStderr = true; + + } + + // Clone the file descriptors into the new process + std::vector SpawnActions; + SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO)); + + if (!discardStdout) + SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO)); + if (!discardStderr) + SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO)); + + // Start the process. + char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; + zx_handle_t ProcessHandle = ZX_HANDLE_INVALID; + rc = fdio_spawn_etc(ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0], + Argv.get(), nullptr, SpawnActions.size(), + SpawnActions.data(), &ProcessHandle, ErrorMsg); + + if (rc != ZX_OK) { + + Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg, + _zx_status_get_string(rc)); + return rc; + + } + + auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); }); + + // Now join the process and return the exit status. + if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED, + ZX_TIME_INFINITE, nullptr)) != ZX_OK) { + + Printf("libFuzzer: failed to join '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + zx_info_process_t Info; + if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info, + sizeof(Info), nullptr, nullptr)) != ZX_OK) { + + Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + return Info.return_code; + +} + +bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) { + + auto LogFilePath = TempPath("SimPopenOut", ".txt"); + Command Cmd(BaseCmd); + Cmd.setOutputFile(LogFilePath); + int Ret = ExecuteCommand(Cmd); + *CmdOutput = FileToString(LogFilePath); + RemoveFile(LogFilePath); + return Ret == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +// In fuchsia, accessing /dev/null is not supported. There's nothing +// similar to a file that discards everything that is written to it. +// The way of doing something similar in fuchsia is by using +// fdio_null_create and binding that to a file descriptor. +void DiscardOutput(int Fd) { + + fdio_t *fdio_null = fdio_null_create(); + if (fdio_null == nullptr) return; + int nullfd = fdio_bind_to_fd(fdio_null, -1, 0); + if (nullfd < 0) return; + dup2(nullfd, Fd); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_FUCHSIA + diff --git a/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp new file mode 100644 index 00000000..f2531bee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp @@ -0,0 +1,43 @@ +//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Linux. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + #include "FuzzerCommand.h" + + #include + #include + #include + #include + +namespace fuzzer { + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + int exit_code = system(CmdLine.c_str()); + if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code); + return exit_code; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp new file mode 100644 index 00000000..372bfa5e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp @@ -0,0 +1,239 @@ +//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include "FuzzerTracePC.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static void AlarmHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticAlarmCallback(); + +} + +static void (*upstream_segv_handler)(int, siginfo_t *, void *); + +static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { + + assert(si->si_signo == SIGSEGV); + if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); + Fuzzer::StaticCrashSignalCallback(); + +} + +static void CrashHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +static void InterruptHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticInterruptCallback(); + +} + +static void GracefulExitHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticGracefulExitCallback(); + +} + +static void FileSizeExceedHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticFileSizeExceedCallback(); + +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + + struct sigaction sigact = {}; + if (sigaction(signum, nullptr, &sigact)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + + if (sigact.sa_flags & SA_SIGINFO) { + + if (sigact.sa_sigaction) { + + if (signum != SIGSEGV) return; + upstream_segv_handler = sigact.sa_sigaction; + + } + + } else { + + if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN && + sigact.sa_handler != SIG_ERR) + return; + + } + + sigact = {}; + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + +} + +// Return true on success, false otherwise. +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return pclose(Pipe) == 0; + +} + +void SetTimer(int Seconds) { + + struct itimerval T { + + {Seconds, 0}, { + + Seconds, 0 + + } + + }; + + if (setitimer(ITIMER_REAL, &T, nullptr)) { + + Printf("libFuzzer: setitimer failed with %d\n", errno); + exit(1); + + } + + SetSigaction(SIGALRM, AlarmHandler); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + // setitimer is not implemented in emscripten. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN) + SetTimer(Options.UnitTimeoutSec / 2 + 1); + if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler); + if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler); + if (Options.HandleSegv) SetSigaction(SIGSEGV, SegvHandler); + if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler); + if (Options.HandleAbrt) SetSigaction(SIGABRT, CrashHandler); + if (Options.HandleIll) SetSigaction(SIGILL, CrashHandler); + if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler); + if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler); + if (Options.HandleUsr1) SetSigaction(SIGUSR1, GracefulExitHandler); + if (Options.HandleUsr2) SetSigaction(SIGUSR2, GracefulExitHandler); + +} + +void SleepSeconds(int Seconds) { + + sleep(Seconds); // Use C API to avoid coverage from instrumented libc++. + +} + +unsigned long GetPid() { + + return (unsigned long)getpid(); + +} + +size_t GetPeakRSSMb() { + + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) return 0; + if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD || + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) { + + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + + } else if (LIBFUZZER_APPLE) { + + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + + } + + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return pclose(F); + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +std::string DisassembleCmd(const std::string &FileName) { + + return "objdump -d " + FileName; + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "grep '" + Regex + "'"; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp new file mode 100644 index 00000000..dca5630f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp @@ -0,0 +1,279 @@ +//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // This must be included after windows.h. + #include + +namespace fuzzer { + +static const FuzzingOptions *HandlerOpt = nullptr; + +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { + + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { + + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_STACK_OVERFLOW: + if (HandlerOpt->HandleSegv) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_IN_PAGE_ERROR: + if (HandlerOpt->HandleBus) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if (HandlerOpt->HandleIll) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback(); + break; + // TODO: handle (Options.HandleXfsz) + + } + + return EXCEPTION_CONTINUE_SEARCH; + +} + +BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { + + switch (dwCtrlType) { + + case CTRL_C_EVENT: + if (HandlerOpt->HandleInt) Fuzzer::StaticInterruptCallback(); + return TRUE; + case CTRL_BREAK_EVENT: + if (HandlerOpt->HandleTerm) Fuzzer::StaticInterruptCallback(); + return TRUE; + + } + + return FALSE; + +} + +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { + + Fuzzer::StaticAlarmCallback(); + +} + +class TimerQ { + + HANDLE TimerQueue; + + public: + TimerQ() : TimerQueue(NULL) { + + } + + ~TimerQ() { + + if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL); + + } + + void SetTimer(int Seconds) { + + if (!TimerQueue) { + + TimerQueue = CreateTimerQueue(); + if (!TimerQueue) { + + Printf("libFuzzer: CreateTimerQueue failed.\n"); + exit(1); + + } + + } + + HANDLE Timer; + if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, + Seconds * 1000, Seconds * 1000, 0)) { + + Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); + exit(1); + + } + + } + +}; + +static TimerQ Timer; + +static void CrashHandler(int) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + HandlerOpt = &Options; + + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) + Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); + + if (Options.HandleInt || Options.HandleTerm) + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + + DWORD LastError = GetLastError(); + Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", + LastError); + exit(1); + + } + + if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || + Options.HandleFpe) + SetUnhandledExceptionFilter(ExceptionHandler); + + if (Options.HandleAbrt) + if (SIG_ERR == signal(SIGABRT, CrashHandler)) { + + Printf("libFuzzer: signal failed with %d\n", errno); + exit(1); + + } + +} + +void SleepSeconds(int Seconds) { + + Sleep(Seconds * 1000); + +} + +unsigned long GetPid() { + + return GetCurrentProcessId(); + +} + +size_t GetPeakRSSMb() { + + PROCESS_MEMORY_COUNTERS info; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) return 0; + return info.PeakWorkingSetSize >> 20; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return _popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return _pclose(F); + +} + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + return system(CmdLine.c_str()); + +} + +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = _popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return _pclose(Pipe) == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + // TODO: make this implementation more efficient. + const char *Cdata = (const char *)Data; + const char *Cpatt = (const char *)Patt; + + if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) + return NULL; + + if (PattLen == 1) return memchr(Data, *Cpatt, DataLen); + + const char *End = Cdata + DataLen - PattLen + 1; + + for (const char *It = Cdata; It < End; ++It) + if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) return It; + + return NULL; + +} + +std::string DisassembleCmd(const std::string &FileName) { + + Vector command_vector; + command_vector.push_back("dumpbin /summary > nul"); + if (ExecuteCommand(Command(command_vector)) == 0) + return "dumpbin /disasm " + FileName; + Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); + exit(1); + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "findstr /r \"" + Regex + "\""; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("nul", "w"); + if (!Temp) return; + _dup2(_fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerValueBitMap.h b/custom_mutators/libfuzzer/FuzzerValueBitMap.h new file mode 100644 index 00000000..ddbfe200 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerValueBitMap.h @@ -0,0 +1,73 @@ +//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// ValueBitMap. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H +#define LLVM_FUZZER_VALUE_BIT_MAP_H + +#include "FuzzerPlatform.h" +#include + +namespace fuzzer { + +// A bit map containing kMapSizeInWords bits. +struct ValueBitMap { + static const size_t kMapSizeInBits = 1 << 16; + static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits; + static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); + static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord; + public: + + // Clears all bits. + void Reset() { memset(Map, 0, sizeof(Map)); } + + // Computes a hash function of Value and sets the corresponding bit. + // Returns true if the bit was changed from 0 to 1. + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValue(uintptr_t Value) { + uintptr_t Idx = Value % kMapSizeInBits; + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + uintptr_t Old = Map[WordIdx]; + uintptr_t New = Old | (1ULL << BitIdx); + Map[WordIdx] = New; + return New != Old; + } + + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValueModPrime(uintptr_t Value) { + return AddValue(Value % kMapPrimeMod); + } + + inline bool Get(uintptr_t Idx) { + assert(Idx < kMapSizeInBits); + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + return Map[WordIdx] & (1ULL << BitIdx); + } + + size_t SizeInBits() const { return kMapSizeInBits; } + + template + ATTRIBUTE_NO_SANITIZE_ALL + void ForEach(Callback CB) const { + for (size_t i = 0; i < kMapSizeInWords; i++) + if (uintptr_t M = Map[i]) + for (size_t j = 0; j < sizeof(M) * 8; j++) + if (M & ((uintptr_t)1 << j)) + CB(i * sizeof(M) * 8 + j); + } + + private: + ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords]; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_VALUE_BIT_MAP_H diff --git a/custom_mutators/libfuzzer/Makefile b/custom_mutators/libfuzzer/Makefile new file mode 100644 index 00000000..f0c80392 --- /dev/null +++ b/custom_mutators/libfuzzer/Makefile @@ -0,0 +1,81 @@ + +#CFLAGS = -O3 -funroll-loops -fPIC -fpermissive -std=c++11 +CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11 +CC := clang++ + +all: libfuzzer-mutator.so + +FuzzerCrossOver.o: FuzzerCrossOver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDataFlowTrace.o: FuzzerDataFlowTrace.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDriver.o: FuzzerDriver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsDlsym.o: FuzzerExtFunctionsDlsym.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWeak.o: FuzzerExtFunctionsWeak.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWindows.o: FuzzerExtFunctionsWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtraCounters.o: FuzzerExtraCounters.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerFork.o: FuzzerFork.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIO.o: FuzzerIO.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOPosix.o: FuzzerIOPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOWindows.o: FuzzerIOWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerLoop.o: FuzzerLoop.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMerge.o: FuzzerMerge.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMutate.o: FuzzerMutate.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerSHA1.o: FuzzerSHA1.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerTracePC.o: FuzzerTracePC.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtil.o: FuzzerUtil.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilDarwin.o: FuzzerUtilDarwin.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilFuchsia.o: FuzzerUtilFuchsia.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilLinux.o: FuzzerUtilLinux.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilPosix.o: FuzzerUtilPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilWindows.o: FuzzerUtilWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer.o: libfuzzer.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer-mutator.so: FuzzerCrossOver.o FuzzerDataFlowTrace.o FuzzerDriver.o FuzzerExtFunctionsDlsym.o FuzzerExtFunctionsWeak.o FuzzerExtFunctionsWindows.o FuzzerExtraCounters.o FuzzerFork.o FuzzerIO.o FuzzerIOPosix.o FuzzerIOWindows.o FuzzerLoop.o FuzzerMerge.o FuzzerMutate.o FuzzerSHA1.o FuzzerTracePC.o FuzzerUtil.o FuzzerUtilDarwin.o FuzzerUtilFuchsia.o FuzzerUtilLinux.o FuzzerUtilPosix.o FuzzerUtilWindows.o libfuzzer.o + $(CC) $(CFLAGS) -I../../include -I. -shared -o libfuzzer-mutator.so *.o + +clean: + rm -f *.o *~ *.so core diff --git a/custom_mutators/libfuzzer/README.md b/custom_mutators/libfuzzer/README.md new file mode 100644 index 00000000..a773da02 --- /dev/null +++ b/custom_mutators/libfuzzer/README.md @@ -0,0 +1,24 @@ +# custum mutator: libfuzzer LLVMFuzzerMutate() + +This uses the libfuzzer LLVMFuzzerMutate() function in llvm 12. + +just type `make` to build + +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz ...``` + +Note that is is currently simple and is missing two features: + * Splicing ("Crossover") + * Dictionary support + +To update the source, all that is needed is that FuzzerDriver.cpp has to receive +``` +#include "libfuzzer.inc" +``` +before the closing namespace bracket. + +It is also libfuzzer.inc where the configuration of the libfuzzer mutations +are done. + +> Original repository: https://github.com/llvm/llvm-project +> Path: compiler-rt/lib/fuzzer/*.{h|cpp} +> Source commit: d4b88ac1658d681e143482336cac27c6a74b8b24 diff --git a/custom_mutators/libfuzzer/libfuzzer.cpp b/custom_mutators/libfuzzer/libfuzzer.cpp new file mode 100644 index 00000000..cf41af2d --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +//#include "config.h" +//#include "debug.h" +#include "afl-fuzz.h" + +afl_state_t *afl_struct; + +extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv, + int (*UserCb)(const uint8_t *Data, + size_t Size)); +extern "C" void LLVMFuzzerMyInit(int (*UserCb)(const uint8_t *Data, + size_t Size), + unsigned int Seed); + +typedef struct my_mutator { + + afl_state_t *afl; + u8 * mutator_buf; + unsigned int seed; + unsigned int extras_cnt, a_extras_cnt; + +} my_mutator_t; + +extern "C" int dummy(const uint8_t *Data, size_t Size) { + + (void)(Data); + (void)(Size); + fprintf(stderr, "dummy() called\n"); + return 0; + +} + +extern "C" my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + if ((data->mutator_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("mutator_buf alloc"); + return NULL; + + } + + data->afl = afl; + data->seed = seed; + afl_struct = afl; + + /* + char **argv; + argv = (char**)malloc(sizeof(size_t) * 2); + argv[0] = (char*)"foo"; + argv[1] = NULL; + int eins = 1; + LLVMFuzzerRunDriver(&eins, &argv, dummy); + */ + + LLVMFuzzerMyInit(dummy, seed); + + return data; + +} + +/* When a new queue entry is added we check if there are new dictionary + entries to add to honggfuzz structure */ +#if ß +extern "C" void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + while (data->extras_cnt < afl_struct->extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->extras[data->extras_cnt].data, + afl_struct->extras[data->extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->extras[data->extras_cnt].len; + run.global->mutate.dictionaryCnt++; + */ + data->extras_cnt++; + + } + + while (data->a_extras_cnt < afl_struct->a_extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->a_extras[data->a_extras_cnt].data, + afl_struct->a_extras[data->a_extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->a_extras[data->a_extras_cnt].len; + run.global->mutate.dictionaryCnt++; + data->a_extras_cnt++; + */ + + } + +} + +#endif +/* we could set only_printable if is_ascii is set ... let's see +uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) { + + //run.global->cfg.only_printable = ... + +} + +*/ + +/* here we run the honggfuzz mutator, which is really good */ + +extern "C" size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, + size_t buf_size, u8 **out_buf, + uint8_t *add_buf, size_t add_buf_size, + size_t max_size) { + + memcpy(data->mutator_buf, buf, buf_size); + size_t ret = LLVMFuzzerMutate(data->mutator_buf, buf_size, max_size); + + /* return size of mutated data */ + *out_buf = data->mutator_buf; + return ret; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +extern "C" void afl_custom_deinit(my_mutator_t *data) { + + free(data->mutator_buf); + free(data); + +} + diff --git a/custom_mutators/libfuzzer/libfuzzer.inc b/custom_mutators/libfuzzer/libfuzzer.inc new file mode 100644 index 00000000..01f21dbe --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.inc @@ -0,0 +1,36 @@ + + +extern "C" ATTRIBUTE_INTERFACE void +LLVMFuzzerMyInit(int (*Callback)(const uint8_t *Data, size_t Size), unsigned int Seed) { + Random Rand(Seed); + FuzzingOptions Options; + Options.Verbosity = 3; + Options.MaxLen = 1024000; + Options.LenControl = true; + Options.DoCrossOver = false; + Options.MutateDepth = 6; + Options.UseCounters = false; + Options.UseMemmem = false; + Options.UseCmp = false; + Options.UseValueProfile = false; + Options.Shrink = false; + Options.ReduceInputs = false; + Options.PreferSmall = false; + Options.ReloadIntervalSec = 0; + Options.OnlyASCII = false; + Options.DetectLeaks = false; + Options.PurgeAllocatorIntervalSec = 0; + Options.TraceMalloc = false; + Options.RssLimitMb = 100; + Options.MallocLimitMb = 100; + Options.MaxNumberOfRuns = 0; + Options.ReportSlowUnits = false; + Options.Entropic = false; + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + EF = new ExternalFunctions(); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); +} diff --git a/custom_mutators/symcc/symcc.c b/custom_mutators/symcc/symcc.c index d0572f4e..6f14052f 100644 --- a/custom_mutators/symcc/symcc.c +++ b/custom_mutators/symcc/symcc.c @@ -12,7 +12,8 @@ afl_state_t *afl_struct; #ifdef DEBUG #define DBG(x...) fprintf(stderr, x) #else - #define DBG(x...) {} + #define DBG(x...) \ + {} #endif typedef struct my_mutator { @@ -177,8 +178,8 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, size_t max_size) { struct dirent **nl; - int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); - size_t size = 0; + int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); + size_t size = 0; if (items <= 0) return 0; diff --git a/docs/Changelog.md b/docs/Changelog.md index 1e5c1b95..af52b955 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -33,6 +33,8 @@ sending a mail to . - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/include/afl-prealloc.h b/include/afl-prealloc.h index edf69a67..fa6c9b70 100644 --- a/include/afl-prealloc.h +++ b/include/afl-prealloc.h @@ -60,7 +60,7 @@ typedef enum prealloc_status { \ if ((prealloc_counter) >= (prealloc_size)) { \ \ - el_ptr = (void *)malloc(sizeof(*el_ptr)); \ + el_ptr = (element_t *)malloc(sizeof(*el_ptr)); \ if (!el_ptr) { FATAL("error in list.h -> out of memory for element!"); } \ el_ptr->pre_status = PRE_STATUS_MALLOC; \ \ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 90701d18..36e47810 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -668,7 +668,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { if (likely(*buf)) { /* the size is always stored at buf - 1*size_t */ - new_buf = afl_alloc_bufptr(*buf); + new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); current_size = new_buf->complete_size; } @@ -694,7 +694,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } /* alloc */ - new_buf = realloc(new_buf, next_size); + new_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size); if (unlikely(!new_buf)) { *buf = NULL; diff --git a/include/list.h b/include/list.h index 88cbe062..7ec81cbe 100644 --- a/include/list.h +++ b/include/list.h @@ -81,6 +81,7 @@ static inline void list_append(list_t *list, void *el) { } element_t *el_box = NULL; + PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE, list->element_prealloc_count); if (!el_box) { FATAL("failed to allocate list element"); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 6bee8b38..a00b240d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1508,9 +1508,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < argc; i++) - SAYF(" \"%s\"", argv[i]); + SAYF(" '%s'", argv[i]); SAYF("\n"); } @@ -1536,9 +1536,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < cc_par_cnt; i++) - SAYF(" \"%s\"", cc_params[i]); + SAYF(" '%s'", cc_params[i]); SAYF("\n"); } -- cgit 1.4.1 From 7cdbe3173ef773562e5f117d03a823cd2786a457 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 13 Sep 2020 15:00:10 +0200 Subject: fixed ngram size unset --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index a00b240d..c3b8959d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1108,7 +1108,7 @@ int main(int argc, char **argv, char **envp) { if (!*ptr) { - if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) != NULL) + if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) FATAL( "you must set the NGRAM size with (e.g. for value 2) " "AFL_LLVM_INSTRUMENT=ngram-2"); -- cgit 1.4.1 From 52c135e1a2cf88f3b578af54962feb8272d12fd4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Sep 2020 02:05:31 +0200 Subject: fix warning --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index c3b8959d..c516dc4c 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1537,7 +1537,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); - for (i = 0; i < cc_par_cnt; i++) + for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); -- cgit 1.4.1 From f810639ab188bf3bbd27fe58fb0b0bb2fe4fbdf0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 31 Oct 2020 14:18:58 +0100 Subject: add our own inline trace-pc-guard --- GNUmakefile.llvm | 5 +- docs/Changelog.md | 3 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 1331 ++++++++++++++++++++++++ src/afl-cc.c | 15 +- 4 files changed, 1351 insertions(+), 3 deletions(-) create mode 100644 instrumentation/SanitizerCoveragePCGUARD.so.cc (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 1f67ea7f..3605b425 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -297,7 +297,7 @@ ifeq "$(TEST_MMAP)" "1" endif PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o -PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so +PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -382,6 +382,9 @@ ifeq "$(LLVM_MIN_4_0_1)" "0" endif $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + ./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o ifeq "$(LLVM_LTO)" "1" $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o diff --git a/docs/Changelog.md b/docs/Changelog.md index f8f15fc8..798a056f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -40,6 +40,9 @@ sending a mail to . - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore + - we now have our own trace-pc-guard implementation. It is the same as + -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline + and b) works from llvm 10+ on :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc new file mode 100644 index 00000000..97e8d32b --- /dev/null +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -0,0 +1,1331 @@ +//===-- SanitizerCoverage.cpp - coverage instrumentation for sanitizers ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Coverage instrumentation done on LLVM IR level, works with Sanitizers. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +#include "config.h" +#include "debug.h" +#include "afl-llvm-common.h" + +namespace llvm { + +/// This is the ModuleSanitizerCoverage pass used in the new pass manager. The +/// pass instruments functions for coverage, adds initialization calls to the +/// module for trace PC guards and 8bit counters if they are requested, and +/// appends globals to llvm.compiler.used. +class ModuleSanitizerCoveragePass + : public PassInfoMixin { + + public: + explicit ModuleSanitizerCoveragePass( + SanitizerCoverageOptions Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + + } + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + static bool isRequired() { + + return true; + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +// Insert SanitizerCoverage instrumentation. +ModulePass *createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = std::vector(), + const std::vector &BlocklistFiles = + std::vector()); + +} // namespace llvm + +using namespace llvm; + +#define DEBUG_TYPE "sancov" + +static const char *const SanCovTracePCIndirName = + "__sanitizer_cov_trace_pc_indir"; +static const char *const SanCovTracePCName = "__sanitizer_cov_trace_pc"; +static const char *const SanCovTraceCmp1 = "__sanitizer_cov_trace_cmp1"; +static const char *const SanCovTraceCmp2 = "__sanitizer_cov_trace_cmp2"; +static const char *const SanCovTraceCmp4 = "__sanitizer_cov_trace_cmp4"; +static const char *const SanCovTraceCmp8 = "__sanitizer_cov_trace_cmp8"; +static const char *const SanCovTraceConstCmp1 = + "__sanitizer_cov_trace_const_cmp1"; +static const char *const SanCovTraceConstCmp2 = + "__sanitizer_cov_trace_const_cmp2"; +static const char *const SanCovTraceConstCmp4 = + "__sanitizer_cov_trace_const_cmp4"; +static const char *const SanCovTraceConstCmp8 = + "__sanitizer_cov_trace_const_cmp8"; +static const char *const SanCovTraceDiv4 = "__sanitizer_cov_trace_div4"; +static const char *const SanCovTraceDiv8 = "__sanitizer_cov_trace_div8"; +static const char *const SanCovTraceGep = "__sanitizer_cov_trace_gep"; +static const char *const SanCovTraceSwitchName = "__sanitizer_cov_trace_switch"; +static const char *const SanCovModuleCtorTracePcGuardName = + "sancov.module_ctor_trace_pc_guard"; +static const char *const SanCovModuleCtor8bitCountersName = + "sancov.module_ctor_8bit_counters"; +static const char *const SanCovModuleCtorBoolFlagName = + "sancov.module_ctor_bool_flag"; +static const uint64_t SanCtorAndDtorPriority = 2; + +static const char *const SanCovTracePCGuardName = + "__sanitizer_cov_trace_pc_guard"; +static const char *const SanCovTracePCGuardInitName = + "__sanitizer_cov_trace_pc_guard_init"; +static const char *const SanCov8bitCountersInitName = + "__sanitizer_cov_8bit_counters_init"; +static const char *const SanCovBoolFlagInitName = + "__sanitizer_cov_bool_flag_init"; +static const char *const SanCovPCsInitName = "__sanitizer_cov_pcs_init"; + +static const char *const SanCovGuardsSectionName = "sancov_guards"; +static const char *const SanCovCountersSectionName = "sancov_cntrs"; +static const char *const SanCovBoolFlagSectionName = "sancov_bools"; +static const char *const SanCovPCsSectionName = "sancov_pcs"; + +static const char *const SanCovLowestStackName = "__sancov_lowest_stack"; + +static char *skip_nozero; + +/* +static cl::opt ClCoverageLevel( + "sanitizer-coverage-level", + cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " + "3: all blocks and critical edges"), + cl::Hidden, cl::init(3)); + +static cl::opt ClTracePC("sanitizer-coverage-trace-pc", + cl::desc("Experimental pc tracing"), cl::Hidden, + cl::init(false)); + +static cl::opt ClTracePCGuard("sanitizer-coverage-trace-pc-guard", + cl::desc("pc tracing with a guard"), + cl::Hidden, cl::init(true)); + +// If true, we create a global variable that contains PCs of all instrumented +// BBs, put this global into a named section, and pass this section's bounds +// to __sanitizer_cov_pcs_init. +// This way the coverage instrumentation does not need to acquire the PCs +// at run-time. Works with trace-pc-guard, inline-8bit-counters, and +// inline-bool-flag. +static cl::opt ClCreatePCTable("sanitizer-coverage-pc-table", + cl::desc("create a static PC table"), + cl::Hidden, cl::init(false)); + +static cl::opt ClInline8bitCounters( + "sanitizer-coverage-inline-8bit-counters", + cl::desc("increments 8-bit counter for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClInlineBoolFlag( + "sanitizer-coverage-inline-bool-flag", + cl::desc("sets a boolean flag for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClCMPTracing( + "sanitizer-coverage-trace-compares", + cl::desc("Tracing of CMP and similar instructions"), cl::Hidden, + cl::init(false)); + +static cl::opt ClDIVTracing("sanitizer-coverage-trace-divs", + cl::desc("Tracing of DIV instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClGEPTracing("sanitizer-coverage-trace-geps", + cl::desc("Tracing of GEP instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClPruneBlocks( + "sanitizer-coverage-prune-blocks", + cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, + cl::init(true)); + +static cl::opt ClStackDepth("sanitizer-coverage-stack-depth", + cl::desc("max stack depth tracing"), + cl::Hidden, cl::init(false)); +*/ +namespace { + +/* +SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { + + SanitizerCoverageOptions Res; + switch (LegacyCoverageLevel) { + + case 0: + Res.CoverageType = SanitizerCoverageOptions::SCK_None; + break; + case 1: + Res.CoverageType = SanitizerCoverageOptions::SCK_Function; + break; + case 2: + Res.CoverageType = SanitizerCoverageOptions::SCK_BB; + break; + case 3: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + break; + case 4: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + Res.IndirectCalls = true; + break; + + } + + return Res; + +} + +*/ + +SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { + + // Sets CoverageType and IndirectCalls. + // SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); + Options.CoverageType = + SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, + // CLOpts.CoverageType); + Options.IndirectCalls = true; // CLOpts.IndirectCalls; + Options.TraceCmp = false; //|= ClCMPTracing; + Options.TraceDiv = false; //|= ClDIVTracing; + Options.TraceGep = false; //|= ClGEPTracing; + Options.TracePC = false; //|= ClTracePC; + Options.TracePCGuard = true; // |= ClTracePCGuard; + Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; + // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; + Options.PCTable = false; //|= ClCreatePCTable; + Options.NoPrune = false; //|= !ClPruneBlocks; + Options.StackDepth = false; //|= ClStackDepth; + if (!Options.TracePCGuard && !Options.TracePC && + !Options.Inline8bitCounters && !Options.StackDepth /*&& + !Options.InlineBoolFlag*/) + Options.TracePCGuard = true; // TracePCGuard is default. + + return Options; + +} + +using DomTreeCallback = function_ref; +using PostDomTreeCallback = + function_ref; + +class ModuleSanitizerCoverage { + + public: + ModuleSanitizerCoverage( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const SpecialCaseList * Allowlist = nullptr, + const SpecialCaseList * Blocklist = nullptr) + : Options(OverrideFromCL(Options)), + Allowlist(Allowlist), + Blocklist(Blocklist) { + + } + + bool instrumentModule(Module &M, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + + private: + void instrumentFunction(Function &F, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + void InjectCoverageForIndirectCalls(Function & F, + ArrayRef IndirCalls); + void InjectTraceForCmp(Function &F, ArrayRef CmpTraceTargets); + void InjectTraceForDiv(Function & F, + ArrayRef DivTraceTargets); + void InjectTraceForGep(Function & F, + ArrayRef GepTraceTargets); + void InjectTraceForSwitch(Function & F, + ArrayRef SwitchTraceTargets); + bool InjectCoverage(Function &F, ArrayRef AllBlocks, + bool IsLeafFunc = true); + GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, + Function &F, Type *Ty, + const char *Section); + GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, + bool IsLeafFunc = true); + Function *CreateInitCallsForSections(Module &M, const char *CtorName, + const char *InitFunctionName, Type *Ty, + const char *Section); + std::pair CreateSecStartEnd(Module &M, const char *Section, + Type *Ty); + + void SetNoSanitizeMetadata(Instruction *I) { + + I->setMetadata(I->getModule()->getMDKindID("nosanitize"), + MDNode::get(*C, None)); + + } + + std::string getSectionName(const std::string &Section) const; + std::string getSectionStart(const std::string &Section) const; + std::string getSectionEnd(const std::string &Section) const; + FunctionCallee SanCovTracePCIndir; + FunctionCallee SanCovTracePC, SanCovTracePCGuard; + FunctionCallee SanCovTraceCmpFunction[4]; + FunctionCallee SanCovTraceConstCmpFunction[4]; + FunctionCallee SanCovTraceDivFunction[2]; + FunctionCallee SanCovTraceGepFunction; + FunctionCallee SanCovTraceSwitchFunction; + GlobalVariable *SanCovLowestStack; + Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, + *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy; + Module * CurModule; + std::string CurModuleUniqueId; + Triple TargetTriple; + LLVMContext * C; + const DataLayout *DL; + + GlobalVariable *FunctionGuardArray; // for trace-pc-guard. + GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters. + GlobalVariable *FunctionBoolArray; // for inline-bool-flag. + GlobalVariable *FunctionPCsArray; // for pc-table. + SmallVector GlobalsToAppendToUsed; + SmallVector GlobalsToAppendToCompilerUsed; + + SanitizerCoverageOptions Options; + + const SpecialCaseList *Allowlist; + const SpecialCaseList *Blocklist; + + uint32_t instr = 0; + GlobalVariable *AFLMapPtr = NULL; + ConstantInt * One = NULL; + ConstantInt * Zero = NULL; + +}; + +class ModuleSanitizerCoverageLegacyPass : public ModulePass { + + public: + ModuleSanitizerCoverageLegacyPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : ModulePass(ID), Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + initializeModuleSanitizerCoverageLegacyPassPass( + *PassRegistry::getPassRegistry()); + + } + + bool runOnModule(Module &M) override { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto DTCallback = [this](Function &F) -> const DominatorTree * { + + return &this->getAnalysis(F).getDomTree(); + + }; + + auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { + + return &this->getAnalysis(F) + .getPostDomTree(); + + }; + + return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); + + } + + static char ID; // Pass identification, replacement for typeid + StringRef getPassName() const override { + + return "ModuleSanitizerCoverage"; + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + AU.addRequired(); + AU.addRequired(); + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +} // namespace + +PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, + ModuleAnalysisManager &MAM) { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto &FAM = MAM.getResult(M).getManager(); + auto DTCallback = [&FAM](Function &F) -> const DominatorTree * { + + return &FAM.getResult(F); + + }; + + auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * { + + return &FAM.getResult(F); + + }; + + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); + +} + +std::pair ModuleSanitizerCoverage::CreateSecStartEnd( + Module &M, const char *Section, Type *Ty) { + + GlobalVariable *SecStart = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionStart(Section)); + SecStart->setVisibility(GlobalValue::HiddenVisibility); + GlobalVariable *SecEnd = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionEnd(Section)); + SecEnd->setVisibility(GlobalValue::HiddenVisibility); + IRBuilder<> IRB(M.getContext()); + if (!TargetTriple.isOSBinFormatCOFF()) + return std::make_pair(SecStart, SecEnd); + + // Account for the fact that on windows-msvc __start_* symbols actually + // point to a uint64_t before the start of the array. + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); + auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd); + +} + +Function *ModuleSanitizerCoverage::CreateInitCallsForSections( + Module &M, const char *CtorName, const char *InitFunctionName, Type *Ty, + const char *Section) { + + auto SecStartEnd = CreateSecStartEnd(M, Section, Ty); + auto SecStart = SecStartEnd.first; + auto SecEnd = SecStartEnd.second; + Function *CtorFunc; + std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( + M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd}); + assert(CtorFunc->getName() == CtorName); + + if (TargetTriple.supportsCOMDAT()) { + + // Use comdat to dedup CtorFunc. + CtorFunc->setComdat(M.getOrInsertComdat(CtorName)); + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority, CtorFunc); + + } else { + + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority); + + } + + if (TargetTriple.isOSBinFormatCOFF()) { + + // In COFF files, if the contructors are set as COMDAT (they are because + // COFF supports COMDAT) and the linker flag /OPT:REF (strip unreferenced + // functions and data) is used, the constructors get stripped. To prevent + // this, give the constructors weak ODR linkage and ensure the linker knows + // to include the sancov constructor. This way the linker can deduplicate + // the constructors but always leave one copy. + CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); + appendToUsed(M, CtorFunc); + + } + + return CtorFunc; + +} + +bool ModuleSanitizerCoverage::instrumentModule( + Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + setvbuf(stdout, NULL, _IONBF, 0); + if (getenv("AFL_DEBUG")) debug = 1; + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n"); + + } else + + be_quiet = 1; + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + // scanForDangerousFunctions(&M); + + if (debug) { + + fprintf(stderr, + "SANCOV: covtype:%u indirect:%d stack:%d noprune:%d " + "createtable:%d tracepcguard:%d tracepc:%d\n", + Options.CoverageType, Options.IndirectCalls == true ? 1 : 0, + Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0, + // Options.InlineBoolFlag == true ? 1 : 0, + Options.PCTable == true ? 1 : 0, + Options.TracePCGuard == true ? 1 : 0, + Options.TracePC == true ? 1 : 0); + + } + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; + if (Allowlist && + !Allowlist->inSection("coverage", "src", M.getSourceFileName())) + return false; + if (Blocklist && + Blocklist->inSection("coverage", "src", M.getSourceFileName())) + return false; + C = &(M.getContext()); + DL = &M.getDataLayout(); + CurModule = &M; + CurModuleUniqueId = getUniqueModuleId(CurModule); + TargetTriple = Triple(M.getTargetTriple()); + FunctionGuardArray = nullptr; + Function8bitCounterArray = nullptr; + FunctionBoolArray = nullptr; + FunctionPCsArray = nullptr; + IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits()); + IntptrPtrTy = PointerType::getUnqual(IntptrTy); + Type * VoidTy = Type::getVoidTy(*C); + IRBuilder<> IRB(*C); + Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty()); + Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); + Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty()); + Int1PtrTy = PointerType::getUnqual(IRB.getInt1Ty()); + Int64Ty = IRB.getInt64Ty(); + Int32Ty = IRB.getInt32Ty(); + Int16Ty = IRB.getInt16Ty(); + Int8Ty = IRB.getInt8Ty(); + Int1Ty = IRB.getInt1Ty(); + LLVMContext &Ctx = M.getContext(); + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1); + Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0); + + SanCovTracePCIndir = + M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); + // Make sure smaller parameters are zero-extended to i64 if required by the + // target ABI. + AttributeList SanCovTraceCmpZeroExtAL; + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 1, Attribute::ZExt); + + SanCovTraceCmpFunction[0] = + M.getOrInsertFunction(SanCovTraceCmp1, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt8Ty(), IRB.getInt8Ty()); + SanCovTraceCmpFunction[1] = + M.getOrInsertFunction(SanCovTraceCmp2, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt16Ty(), IRB.getInt16Ty()); + SanCovTraceCmpFunction[2] = + M.getOrInsertFunction(SanCovTraceCmp4, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt32Ty(), IRB.getInt32Ty()); + SanCovTraceCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceCmp8, VoidTy, Int64Ty, Int64Ty); + + SanCovTraceConstCmpFunction[0] = M.getOrInsertFunction( + SanCovTraceConstCmp1, SanCovTraceCmpZeroExtAL, VoidTy, Int8Ty, Int8Ty); + SanCovTraceConstCmpFunction[1] = M.getOrInsertFunction( + SanCovTraceConstCmp2, SanCovTraceCmpZeroExtAL, VoidTy, Int16Ty, Int16Ty); + SanCovTraceConstCmpFunction[2] = M.getOrInsertFunction( + SanCovTraceConstCmp4, SanCovTraceCmpZeroExtAL, VoidTy, Int32Ty, Int32Ty); + SanCovTraceConstCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty); + + { + + AttributeList AL; + AL = AL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceDivFunction[0] = + M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty()); + + } + + SanCovTraceDivFunction[1] = + M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty); + SanCovTraceGepFunction = + M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy); + SanCovTraceSwitchFunction = + M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy); + + Constant *SanCovLowestStackConstant = + M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); + SanCovLowestStack = dyn_cast(SanCovLowestStackConstant); + if (!SanCovLowestStack) { + + C->emitError(StringRef("'") + SanCovLowestStackName + + "' should not be declared by the user"); + return true; + + } + + SanCovLowestStack->setThreadLocalMode( + GlobalValue::ThreadLocalMode::InitialExecTLSModel); + if (Options.StackDepth && !SanCovLowestStack->isDeclaration()) + SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy)); + + SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); + SanCovTracePCGuard = + M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy); + + for (auto &F : M) + instrumentFunction(F, DTCallback, PDTCallback); + + Function *Ctor = nullptr; + + if (FunctionGuardArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName, + SanCovTracePCGuardInitName, Int32PtrTy, + SanCovGuardsSectionName); + if (Function8bitCounterArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName, + SanCov8bitCountersInitName, Int8PtrTy, + SanCovCountersSectionName); + if (FunctionBoolArray) { + + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName, + SanCovBoolFlagInitName, Int1PtrTy, + SanCovBoolFlagSectionName); + + } + + if (Ctor && Options.PCTable) { + + auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy); + FunctionCallee InitFunction = declareSanitizerInitFunction( + M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy}); + IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); + IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second}); + + } + + // We don't reference these arrays directly in any of our runtime functions, + // so we need to prevent them from being dead stripped. + if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); + appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); + + if (!be_quiet) { + + if (!instr) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (%s mode).", instr, + modeline); + + } + + } + + return true; + +} + +// True if block has successors and it dominates all of them. +static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { + + if (succ_begin(BB) == succ_end(BB)) return false; + + for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { + + if (!DT->dominates(BB, SUCC)) return false; + + } + + return true; + +} + +// True if block has predecessors and it postdominates all of them. +static bool isFullPostDominator(const BasicBlock * BB, + const PostDominatorTree *PDT) { + + if (pred_begin(BB) == pred_end(BB)) return false; + + for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { + + if (!PDT->dominates(BB, PRED)) return false; + + } + + return true; + +} + +static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, + const DominatorTree * DT, + const PostDominatorTree * PDT, + const SanitizerCoverageOptions &Options) { + + // Don't insert coverage for blocks containing nothing but unreachable: we + // will never call __sanitizer_cov() for them, so counting them in + // NumberOfInstrumentedBlocks() might complicate calculation of code coverage + // percentage. Also, unreachable instructions frequently have no debug + // locations. + if (isa(BB->getFirstNonPHIOrDbgOrLifetime())) return false; + + // Don't insert coverage into blocks without a valid insertion point + // (catchswitch blocks). + if (BB->getFirstInsertionPt() == BB->end()) return false; + + if (Options.NoPrune || &F.getEntryBlock() == BB) return true; + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && + &F.getEntryBlock() != BB) + return false; + + // Do not instrument full dominators, or full post-dominators with multiple + // predecessors. + return !isFullDominator(BB, DT) && + !(isFullPostDominator(BB, PDT) && !BB->getSinglePredecessor()); + +} + +// Returns true iff From->To is a backedge. +// A twist here is that we treat From->To as a backedge if +// * To dominates From or +// * To->UniqueSuccessor dominates From +static bool IsBackEdge(BasicBlock *From, BasicBlock *To, + const DominatorTree *DT) { + + if (DT->dominates(To, From)) return true; + if (auto Next = To->getUniqueSuccessor()) + if (DT->dominates(Next, From)) return true; + return false; + +} + +// Prunes uninteresting Cmp instrumentation: +// * CMP instructions that feed into loop backedge branch. +// +// Note that Cmp pruning is controlled by the same flag as the +// BB pruning. +static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, + const SanitizerCoverageOptions &Options) { + + if (!Options.NoPrune) + if (CMP->hasOneUse()) + if (auto BR = dyn_cast(CMP->user_back())) + for (BasicBlock *B : BR->successors()) + if (IsBackEdge(BR->getParent(), B, DT)) return false; + return true; + +} + +void ModuleSanitizerCoverage::instrumentFunction( + Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + if (F.empty()) return; + if (F.getName().find(".module_ctor") != std::string::npos) + return; // Should not instrument sanitizer init functions. + if (F.getName().startswith("__sanitizer_")) + return; // Don't instrument __sanitizer_* callbacks. + // Don't touch available_externally functions, their actual body is elewhere. + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; + // Don't instrument MSVC CRT configuration helpers. They may run before normal + // initialization. + if (F.getName() == "__local_stdio_printf_options" || + F.getName() == "__local_stdio_scanf_options") + return; + if (isa(F.getEntryBlock().getTerminator())) return; + // Don't instrument functions using SEH for now. Splitting basic blocks like + // we do for coverage breaks WinEHPrepare. + // FIXME: Remove this when SEH no longer uses landingpad pattern matching. + if (F.hasPersonalityFn() && + isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + return; + if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName())) + return; + if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) return; + if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) + SplitAllCriticalEdges( + F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); + SmallVector IndirCalls; + SmallVector BlocksToInstrument; + SmallVector CmpTraceTargets; + SmallVector SwitchTraceTargets; + SmallVector DivTraceTargets; + SmallVector GepTraceTargets; + + const DominatorTree * DT = DTCallback(F); + const PostDominatorTree *PDT = PDTCallback(F); + bool IsLeafFunc = true; + + for (auto &BB : F) { + + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); + for (auto &Inst : BB) { + + if (Options.IndirectCalls) { + + CallBase *CB = dyn_cast(&Inst); + if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + + } + + if (Options.TraceCmp) { + + if (ICmpInst *CMP = dyn_cast(&Inst)) + if (IsInterestingCmp(CMP, DT, Options)) + CmpTraceTargets.push_back(&Inst); + if (isa(&Inst)) SwitchTraceTargets.push_back(&Inst); + + } + + if (Options.TraceDiv) + if (BinaryOperator *BO = dyn_cast(&Inst)) + if (BO->getOpcode() == Instruction::SDiv || + BO->getOpcode() == Instruction::UDiv) + DivTraceTargets.push_back(BO); + if (Options.TraceGep) + if (GetElementPtrInst *GEP = dyn_cast(&Inst)) + GepTraceTargets.push_back(GEP); + if (Options.StackDepth) + if (isa(Inst) || + (isa(Inst) && !isa(Inst))) + IsLeafFunc = false; + + } + + } + + InjectCoverage(F, BlocksToInstrument, IsLeafFunc); + InjectCoverageForIndirectCalls(F, IndirCalls); + InjectTraceForCmp(F, CmpTraceTargets); + InjectTraceForSwitch(F, SwitchTraceTargets); + InjectTraceForDiv(F, DivTraceTargets); + InjectTraceForGep(F, GepTraceTargets); + +} + +GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection( + size_t NumElements, Function &F, Type *Ty, const char *Section) { + + ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); + auto Array = new GlobalVariable( + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); + + if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) + if (auto Comdat = + GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) + Array->setComdat(Comdat); + Array->setSection(getSectionName(Section)); + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + GlobalsToAppendToUsed.push_back(Array); + GlobalsToAppendToCompilerUsed.push_back(Array); + MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); + Array->addMetadata(LLVMContext::MD_associated, *MD); + + return Array; + +} + +GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( + Function &F, ArrayRef AllBlocks) { + + size_t N = AllBlocks.size(); + assert(N); + SmallVector PCs; + IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt()); + for (size_t i = 0; i < N; i++) { + + if (&F.getEntryBlock() == AllBlocks[i]) { + + PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 1), IntptrPtrTy)); + + } else { + + PCs.push_back((Constant *)IRB.CreatePointerCast( + BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); + + } + + } + + auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy, + SanCovPCsSectionName); + PCArray->setInitializer( + ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs)); + PCArray->setConstant(true); + + return PCArray; + +} + +void ModuleSanitizerCoverage::CreateFunctionLocalArrays( + Function &F, ArrayRef AllBlocks) { + + if (Options.TracePCGuard) + FunctionGuardArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + + if (Options.Inline8bitCounters) + Function8bitCounterArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); + /* + if (Options.InlineBoolFlag) + FunctionBoolArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); + */ + if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); + +} + +bool ModuleSanitizerCoverage::InjectCoverage(Function & F, + ArrayRef AllBlocks, + bool IsLeafFunc) { + + if (AllBlocks.empty()) return false; + CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) + InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + return true; + +} + +// On every indirect call we call a run-time function +// __sanitizer_cov_indir_call* with two parameters: +// - callee address, +// - global cache array that contains CacheSize pointers (zero-initialized). +// The cache is used to speed up recording the caller-callee pairs. +// The address of the caller is passed implicitly via caller PC. +// CacheSize is encoded in the name of the run-time function. +void ModuleSanitizerCoverage::InjectCoverageForIndirectCalls( + Function &F, ArrayRef IndirCalls) { + + if (IndirCalls.empty()) return; + assert(Options.TracePC || Options.TracePCGuard || + Options.Inline8bitCounters /*|| Options.InlineBoolFlag*/); + for (auto I : IndirCalls) { + + IRBuilder<> IRB(I); + CallBase & CB = cast(*I); + Value * Callee = CB.getCalledOperand(); + if (isa(Callee)) continue; + IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); + + } + +} + +// For every switch statement we insert a call: +// __sanitizer_cov_trace_switch(CondValue, +// {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... }) + +void ModuleSanitizerCoverage::InjectTraceForSwitch( + Function &, ArrayRef SwitchTraceTargets) { + + for (auto I : SwitchTraceTargets) { + + if (SwitchInst *SI = dyn_cast(I)) { + + IRBuilder<> IRB(I); + SmallVector Initializers; + Value * Cond = SI->getCondition(); + if (Cond->getType()->getScalarSizeInBits() > + Int64Ty->getScalarSizeInBits()) + continue; + Initializers.push_back(ConstantInt::get(Int64Ty, SI->getNumCases())); + Initializers.push_back( + ConstantInt::get(Int64Ty, Cond->getType()->getScalarSizeInBits())); + if (Cond->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + Cond = IRB.CreateIntCast(Cond, Int64Ty, false); + for (auto It : SI->cases()) { + + Constant *C = It.getCaseValue(); + if (C->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + C = ConstantExpr::getCast(CastInst::ZExt, It.getCaseValue(), Int64Ty); + Initializers.push_back(C); + + } + + llvm::sort(Initializers.begin() + 2, Initializers.end(), + [](const Constant *A, const Constant *B) { + + return cast(A)->getLimitedValue() < + cast(B)->getLimitedValue(); + + }); + + ArrayType *ArrayOfInt64Ty = ArrayType::get(Int64Ty, Initializers.size()); + GlobalVariable *GV = new GlobalVariable( + *CurModule, ArrayOfInt64Ty, false, GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayOfInt64Ty, Initializers), + "__sancov_gen_cov_switch_values"); + IRB.CreateCall(SanCovTraceSwitchFunction, + {Cond, IRB.CreatePointerCast(GV, Int64PtrTy)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForDiv( + Function &, ArrayRef DivTraceTargets) { + + for (auto BO : DivTraceTargets) { + + IRBuilder<> IRB(BO); + Value * A1 = BO->getOperand(1); + if (isa(A1)) continue; + if (!A1->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType()); + int CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1; + if (CallbackIdx < 0) continue; + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx], + {IRB.CreateIntCast(A1, Ty, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForGep( + Function &, ArrayRef GepTraceTargets) { + + for (auto GEP : GepTraceTargets) { + + IRBuilder<> IRB(GEP); + for (auto I = GEP->idx_begin(); I != GEP->idx_end(); ++I) + if (!isa(*I) && (*I)->getType()->isIntegerTy()) + IRB.CreateCall(SanCovTraceGepFunction, + {IRB.CreateIntCast(*I, IntptrTy, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForCmp( + Function &, ArrayRef CmpTraceTargets) { + + for (auto I : CmpTraceTargets) { + + if (ICmpInst *ICMP = dyn_cast(I)) { + + IRBuilder<> IRB(ICMP); + Value * A0 = ICMP->getOperand(0); + Value * A1 = ICMP->getOperand(1); + if (!A0->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A0->getType()); + int CallbackIdx = + TypeSize == 8 + ? 0 + : TypeSize == 16 ? 1 + : TypeSize == 32 ? 2 : TypeSize == 64 ? 3 : -1; + if (CallbackIdx < 0) continue; + // __sanitizer_cov_trace_cmp((type_size << 32) | predicate, A0, A1); + auto CallbackFunc = SanCovTraceCmpFunction[CallbackIdx]; + bool FirstIsConst = isa(A0); + bool SecondIsConst = isa(A1); + // If both are const, then we don't need such a comparison. + if (FirstIsConst && SecondIsConst) continue; + // If only one is const, then make it the first callback argument. + if (FirstIsConst || SecondIsConst) { + + CallbackFunc = SanCovTraceConstCmpFunction[CallbackIdx]; + if (SecondIsConst) std::swap(A0, A1); + + } + + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(CallbackFunc, {IRB.CreateIntCast(A0, Ty, true), + IRB.CreateIntCast(A1, Ty, true)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, + size_t Idx, + bool IsLeafFunc) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + bool IsEntryBB = &BB == &F.getEntryBlock(); + DebugLoc EntryLoc; + if (IsEntryBB) { + + if (auto SP = F.getSubprogram()) + EntryLoc = DebugLoc::get(SP->getScopeLine(), 0, SP); + // Keep static allocas and llvm.localescape calls in the entry block. Even + // if we aren't splitting the block, it's nice for allocas to be before + // calls. + IP = PrepareToSplitEntryBlock(BB, IP); + + } else { + + EntryLoc = IP->getDebugLoc(); + + } + + IRBuilder<> IRB(&*IP); + IRB.SetCurrentDebugLocation(EntryLoc); + if (Options.TracePC) { + + IRB.CreateCall(SanCovTracePC); + // ->setCannotMerge(); // gets the PC using GET_CALLER_PC. + + } + + if (Options.TracePCGuard) { + + /* Get CurLoc */ + + Value *GuardPtr = IRB.CreateIntToPtr( + IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), + ConstantInt::get(IntptrTy, Idx * 4)), + Int32PtrTy); + + LoadInst *CurLoc = IRB.CreateLoad(GuardPtr); + + /* Load SHM pointer */ + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + + /* Load counter for CurLoc */ + + Value * MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + + /* Update bitmap */ + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero == NULL) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx); + + // done :) + + // IRB.CreateCall(SanCovTracePCGuard, Offset)->setCannotMerge(); + // IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); + ++instr; + + } + + if (Options.Inline8bitCounters) { + + auto CounterPtr = IRB.CreateGEP( + Function8bitCounterArray->getValueType(), Function8bitCounterArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); + auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); + auto Store = IRB.CreateStore(Inc, CounterPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + /* + if (Options.InlineBoolFlag) { + + auto FlagPtr = IRB.CreateGEP( + FunctionBoolArray->getValueType(), FunctionBoolArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); + auto ThenTerm = + SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + */ + + if (Options.StackDepth && IsEntryBB && !IsLeafFunc) { + + // Check stack depth. If it's the deepest so far, record it. + Module * M = F.getParent(); + Function *GetFrameAddr = Intrinsic::getDeclaration( + M, Intrinsic::frameaddress, + IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace())); + auto FrameAddrPtr = + IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)}); + auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy); + auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack); + auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack); + auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack); + SetNoSanitizeMetadata(LowestStack); + SetNoSanitizeMetadata(Store); + + } + +} + +std::string ModuleSanitizerCoverage::getSectionName( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatCOFF()) { + + if (Section == SanCovCountersSectionName) return ".SCOV$CM"; + if (Section == SanCovBoolFlagSectionName) return ".SCOV$BM"; + if (Section == SanCovPCsSectionName) return ".SCOVP$M"; + return ".SCOV$GM"; // For SanCovGuardsSectionName. + + } + + if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; + return "__" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionStart( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$start$__DATA$__" + Section; + return "__start___" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionEnd( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$end$__DATA$__" + Section; + return "__stop___" + Section; + +} + +char ModuleSanitizerCoverageLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) +INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options, + const std::vector &AllowlistFiles, + const std::vector &BlocklistFiles) { + + return new ModuleSanitizerCoverageLegacyPass(Options, AllowlistFiles, + BlocklistFiles); + +} + +static void registerPCGUARDPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new ModuleSanitizerCoverageLegacyPass(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCompTransPass( + PassManagerBuilder::EP_OptimizerLast, registerPCGUARDPass); + +static RegisterStandardPasses RegisterCompTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerPCGUARDPass); + diff --git a/src/afl-cc.c b/src/afl-cc.c index c516dc4c..1a7a837f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -501,11 +501,22 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 4 +#if LLVM_MAJOR >= 10 + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = - "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); #else + #if LLVM_MAJOR >= 4 + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to llvm 10+ 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 { -- cgit 1.4.1 From 6abe4d124ec23d3ab9fc5f7bec4ffa1c0a1eba22 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 5 Nov 2020 08:53:16 +0100 Subject: require llvm 10.0.1+ for enhanced pcguard, correct 1MB checks --- src/afl-cc.c | 8 ++++---- src/afl-fuzz.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 1a7a837f..46468dda 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -501,7 +501,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 10 +#if LLVM_MAJOR >= 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; @@ -511,7 +511,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #if LLVM_MAJOR >= 4 if (!be_quiet) SAYF( - "Using unoptimized trace-pc-guard, upgrade to llvm 10+ for " + "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 @@ -715,14 +715,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { "int __afl_sharedmem_fuzzing = 1;" "extern unsigned int *__afl_fuzz_len;" "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1024000];" + "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; 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, 1024000)) == 0xffffffff " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " "? 0 : *__afl_fuzz_len)"; cc_params[cc_par_cnt++] = diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 67cde96a..269ce1bf 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1064,13 +1064,13 @@ int main(int argc, char **argv_orig, char **envp) { } 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) % 1024000 == 0 ? (2 * MAX_FILE) / 1048576 + (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 / 1024000); + afl->q_testcase_max_cache_size / 1048576); } -- cgit 1.4.1 From 585ba4c1dda6d8706db122e15718b867fd5489cd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 8 Nov 2020 16:33:25 +0100 Subject: fix for llvm 10.0.0 --- GNUmakefile.llvm | 2 +- src/afl-cc.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 2bb0263b..cc28695d 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -357,7 +357,7 @@ instrumentation/afl-common.o: ./src/afl-common.c $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ $(LDFLAGS) ./afl-cc: src/afl-cc.c instrumentation/afl-common.o - $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" + $(CC) $(CLANG_CFL) $(CFLAGS) $(CPPFLAGS) $< instrumentation/afl-common.o -o $@ -DLLVM_MINOR=$(LLVM_MINOR) -DLLVM_MAJOR=$(LLVM_MAJOR) $(LDFLAGS) -DCFLAGS_OPT=\"$(CFLAGS_OPT)\" @ln -sf afl-cc ./afl-c++ @ln -sf afl-cc ./afl-gcc @ln -sf afl-cc ./afl-g++ diff --git a/src/afl-cc.c b/src/afl-cc.c index 46468dda..a1c1d676 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -38,6 +38,12 @@ #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 */ -- cgit 1.4.1 From 82d1c3e18dd1b90fa15f7c056f94dc1a06ee345d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 8 Nov 2020 17:08:30 +0100 Subject: fix wrong llvm version comparison --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index a1c1d676..771a58f5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -507,7 +507,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; -- cgit 1.4.1 From 1cc637a0a05a043a223f69fb9661ecc3d5597d23 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 16 Nov 2020 10:59:09 +0100 Subject: support AFL_LLVM_INSTRUMENT env for our own PCGUARD --- docs/Changelog.md | 13 +-- instrumentation/SanitizerCoveragePCGUARD.so.cc | 8 +- src/afl-cc.c | 113 ++++++++++++++----------- src/afl-fuzz-init.c | 3 +- src/afl-fuzz.c | 42 +++++++-- 5 files changed, 116 insertions(+), 63 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index a69f2ff4..baa2667b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,24 +37,27 @@ sending a mail to . - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt - - added INTROSPECTION support for custom modules - print special compile time options used in help output + - somewhere we broke -n dumb fuzzing, fixed - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore - we now have our own trace-pc-guard implementation. It is the same as -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline - and b) works from llvm 10+ on :) + and b) works from llvm 10.0.1 + onwards :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - fix crash in dict2file for integers > 64 bit + - custom mutators + - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ + - added INTROSPECTION support for custom modules + - python fuzz function was not optional, fixed - unicornafl synced with upstream (arm64 fix, better rust bindings) - - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ - - added a new custom mutator: libfuzzer that integrates libfuzzer mutations - - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index b3c55108..e85f9cd3 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -544,7 +544,9 @@ bool ModuleSanitizerCoverage::instrumentModule( be_quiet = 1; skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - // scanForDangerousFunctions(&M); + + initInstrumentList(); + scanForDangerousFunctions(&M); if (debug) { @@ -819,6 +821,8 @@ void ModuleSanitizerCoverage::instrumentFunction( Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { if (F.empty()) return; + if (!isInInstrumentList(&F)) return; + if (F.getName().find(".module_ctor") != std::string::npos) return; // Should not instrument sanitizer init functions. if (F.getName().startswith("__sanitizer_")) @@ -1315,6 +1319,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( } char ModuleSanitizerCoverageLegacyPass::ID = 0; + INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) @@ -1323,6 +1328,7 @@ INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) + ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( const SanitizerCoverageOptions &Options, const std::vector &AllowlistFiles, diff --git a/src/afl-cc.c b/src/afl-cc.c index 771a58f5..5d8d33a5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -49,14 +49,14 @@ 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 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, - compiler_mode, plusplus_mode; -static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto; -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 instrument_mode, instrument_opt_mode, ngram_size, 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 { @@ -354,19 +354,13 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && plusplus_mode) cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early - if (lto_mode) { - - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + 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); - - } + 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); } @@ -508,11 +502,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - 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); + 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); + + } + #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -590,6 +598,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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; @@ -856,6 +867,14 @@ int main(int argc, char **argv, char **envp) { 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 ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1015,14 +1034,14 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && - getenv("AFL_DONT_OPTIMIZE")) + 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")) { @@ -1426,22 +1445,20 @@ int main(int argc, char **argv, char **envp) { #if LLVM_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (have_instr_env) { instrument_mode = INSTRUMENT_AFL; - WARNF( - "switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " - "SanitizerCoverage.html#partially-disabling-instrumentation"); + 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 } @@ -1487,18 +1504,16 @@ int main(int argc, char **argv, char **envp) { "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " "together"); - if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) +#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 " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " - "12+), see " - "https://clang.llvm.org/docs/" - "SanitizerCoverage.html#partially-disabling-instrumentation"); + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif u8 *ptr2; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 8b9b0a6f..6884bb1d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2497,7 +2497,8 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || afl->non_instrumented_mode) { + if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + afl->non_instrumented_mode) { return; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c1ddd413..cedfdf8f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -435,11 +435,23 @@ 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->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"); } + /* 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 @@ -469,11 +481,23 @@ 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->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"); } + /* 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; @@ -1354,7 +1378,11 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { check_binary(afl, afl->cmplog_binary); } + if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { + + check_binary(afl, afl->cmplog_binary); + + } } -- cgit 1.4.1 From 54fdec0e51d17ac47582ca23c32ce10da5591aa2 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 17 Nov 2020 21:07:29 +0100 Subject: fix: avoid preprocessor logic in macro arguments (not portable) --- src/afl-cc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 5d8d33a5..ef4d2c74 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1326,15 +1326,18 @@ int main(int argc, char **argv, char **envp) { " 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" -#if LLVM_MAJOR < 9 - " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" -#else - " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" -#endif + + COUNTER_BEHAVIOUR + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " "comparisons\n" " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" -- cgit 1.4.1 From 23f37ff5054d77abf7baf7b6d01d660b435d81cd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 02:33:47 +0100 Subject: fixed alloc errors, code format --- examples/afl_network_proxy/afl-network-server.c | 8 ++++---- include/alloc-inl.h | 11 ++++++----- src/afl-cc.c | 6 ++++-- src/afl-fuzz-python.c | 6 +++--- src/afl-fuzz-queue.c | 7 ++++++- src/afl-fuzz.c | 2 ++ 6 files changed, 25 insertions(+), 15 deletions(-) (limited to 'src/afl-cc.c') diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index 75eb3d20..3831f985 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -358,8 +358,8 @@ int recv_testcase(int s, void **buf) { if ((size & 0xff000000) != 0xff000000) { - *buf = afl_realloc((void **)&buf, size); - if (unlikely(!buf)) { PFATAL("Alloc"); } + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } received = 0; // fprintf(stderr, "unCOMPRESS (%u)\n", size); while (received < size && @@ -371,8 +371,8 @@ int recv_testcase(int s, void **buf) { #ifdef USE_DEFLATE u32 clen; size -= 0xff000000; - *buf = afl_realloc((void **)&buf, size); - if (unlikely(!buf)) { PFATAL("Alloc"); } + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } received = 0; while (received < 4 && (ret = recv(s, &clen + received, 4 - received, 0)) > 0) diff --git a/include/alloc-inl.h b/include/alloc-inl.h index a6194f86..68255fb6 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -694,10 +694,11 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } /* alloc */ - struct afl_alloc_buf *newer_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size); + struct afl_alloc_buf *newer_buf = + (struct afl_alloc_buf *)realloc(new_buf, next_size); if (unlikely(!newer_buf)) { - free(new_buf); // avoid a leak + free(new_buf); // avoid a leak *buf = NULL; return NULL; @@ -707,7 +708,6 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } - new_buf->complete_size = next_size; *buf = (void *)(new_buf->buf); return *buf; @@ -736,10 +736,11 @@ static inline void *afl_realloc_exact(void **buf, size_t size_needed) { if (unlikely(current_size == size_needed)) { return *buf; } /* alloc */ - struct afl_alloc_buf *newer_buf = (struct afl_alloc_buf *)realloc(new_buf, size_needed); + struct afl_alloc_buf *newer_buf = + (struct afl_alloc_buf *)realloc(new_buf, size_needed); if (unlikely(!newer_buf)) { - free(new_buf); // avoid a leak + free(new_buf); // avoid a leak *buf = NULL; return NULL; diff --git a/src/afl-cc.c b/src/afl-cc.c index ef4d2c74..9c23c18b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1327,9 +1327,11 @@ int main(int argc, char **argv, char **envp) { "filename\n"); #if LLVM_MAJOR < 9 -#define COUNTER_BEHAVIOUR " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" + #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" + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" #endif if (have_llvm) SAYF( diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index cfaf055d..80532774 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -96,7 +96,7 @@ 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 = afl_realloc(BUF_PARAMS(fuzz), mutated_size); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); @@ -579,7 +579,7 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { ret = PyByteArray_Size(py_value); *out_buf = afl_realloc(BUF_PARAMS(trim), ret); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } memcpy(*out_buf, PyByteArray_AsString(py_value), ret); Py_DECREF(py_value); @@ -645,7 +645,7 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, /* A new buf is needed... */ *out_buf = afl_realloc(BUF_PARAMS(havoc), mutated_size); - if (unlikely(!out_buf)) { PFATAL("alloc"); } + if (unlikely(!*out_buf)) { PFATAL("alloc"); } } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index c78df8be..32bed06f 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -56,7 +56,12 @@ void create_alias_table(afl_state_t *afl) { 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) { FATAL("could not aquire memory for alias table"); } + if (!P || !S || !L || !afl->alias_table || !afl->alias_probability) { + + FATAL("could not aquire memory for alias table"); + + } + memset((void *)afl->alias_table, 0, n * sizeof(u32)); memset((void *)afl->alias_probability, 0, n * sizeof(double)); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cedfdf8f..ac77bb1f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -350,6 +350,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; @@ -419,6 +420,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 (afl->in_dir == NULL) { FATAL("Invalid -i option (got NULL)."); } afl->in_dir = optarg; if (!strcmp(afl->in_dir, "-")) { afl->in_place_resume = 1; } -- cgit 1.4.1 From b260204b728efcc4ba13dfa77692cfc5721db606 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 18 Nov 2020 18:13:03 +0000 Subject: Solaris/Illumos build fix. (#609) --- include/afl-fuzz.h | 1 + src/afl-cc.c | 2 +- src/afl-fuzz-init.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b484b93e..423230f1 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -113,6 +113,7 @@ #include #include #include + #include #endif #endif /* __linux__ */ diff --git a/src/afl-cc.c b/src/afl-cc.c index 9c23c18b..19dc9a6a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -837,7 +837,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - #ifndef __APPLE__ + #if !defined(__APPLE__) && !defined(__sun) if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 6884bb1d..0360cdb0 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -355,7 +355,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)) { -- cgit 1.4.1 From 27c3423fb6f1cf568cf0e50b16a82c035945ae7c Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 24 Nov 2020 19:38:55 +0100 Subject: test-pre.sh: remove old uses of afl-clang, afl-cc.c: add missing env.var. AFL_LLVM_LAF_ALL --- src/afl-cc.c | 6 +++--- test/test-pre.sh | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 19dc9a6a..1ad43c43 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -686,7 +686,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || lto_mode) { + 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"; @@ -1030,7 +1030,7 @@ int main(int argc, char **argv, char **envp) { if (instrument_mode == 0) instrument_mode = INSTRUMENT_PCGUARD; else if (instrument_mode != INSTRUMENT_PCGUARD) - FATAL("you can not set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); } @@ -1049,7 +1049,7 @@ int main(int argc, char **argv, char **envp) { instrument_mode = INSTRUMENT_CFG; else if (instrument_mode != INSTRUMENT_CFG) FATAL( - "you can not set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); + "you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); } diff --git a/test/test-pre.sh b/test/test-pre.sh index fc14ee0b..fc84cb47 100755 --- a/test/test-pre.sh +++ b/test/test-pre.sh @@ -106,14 +106,7 @@ export AFL_LLVM_INSTRUMENT=AFL test -e /usr/local/bin/opt && { export PATH="/usr/local/bin:${PATH}" } -# on MacOS X we prefer afl-clang over afl-gcc, because -# afl-gcc does not work there -test `uname -s` = 'Darwin' -o `uname -s` = 'FreeBSD' && { - AFL_GCC=afl-clang -} || { - AFL_GCC=afl-gcc -} -command -v gcc >/dev/null 2>&1 || AFL_GCC=afl-clang +AFL_GCC=afl-gcc SYS=`uname -m` @@ -135,4 +128,4 @@ test -z "$SYS" && $ECHO "$YELLOW[-] uname -m did not succeed" CODE=0 INCOMPLETE=0 -fi \ No newline at end of file +fi -- cgit 1.4.1 From ded80870a933b83d8186a160f7717641ce441a81 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sun, 1 Nov 2020 06:22:18 +0100 Subject: reenable afl-clang(++) --- GNUmakefile | 4 +++- GNUmakefile.llvm | 2 ++ src/afl-cc.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile b/GNUmakefile index 764c9baa..00753241 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -538,7 +538,7 @@ all_done: test_build .PHONY: clean clean: - rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ test/unittests/unit_hash test/unittests/unit_rand + rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ afl-clang afl-clang++ test/unittests/unit_hash test/unittests/unit_rand -$(MAKE) -f GNUmakefile.llvm clean -$(MAKE) -f GNUmakefile.gcc_plugin clean $(MAKE) -C libdislocator clean @@ -633,6 +633,8 @@ install: all $(MANPAGES) -$(MAKE) -f GNUmakefile.gcc_plugin install ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-g++ + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang + ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang++ @mkdir -m 0755 -p ${DESTDIR}$(MAN_PATH) install -m0644 *.8 ${DESTDIR}$(MAN_PATH) install -m 755 afl-as $${DESTDIR}$(HELPER_PATH) diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index cc28695d..5ab998bb 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -361,6 +361,8 @@ instrumentation/afl-common.o: ./src/afl-common.c @ln -sf afl-cc ./afl-c++ @ln -sf afl-cc ./afl-gcc @ln -sf afl-cc ./afl-g++ + @ln -sf afl-cc ./afl-clang + @ln -sf afl-cc ./afl-clang++ @ln -sf afl-cc ./afl-clang-fast @ln -sf afl-cc ./afl-clang-fast++ ifneq "$(AFL_CLANG_FLTO)" "" diff --git a/src/afl-cc.c b/src/afl-cc.c index 1ad43c43..25ed923a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -48,6 +48,7 @@ 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, lto_mode; static u8 compiler_mode, plusplus_mode, have_instr_env = 0; @@ -288,7 +289,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode >= GCC_PLUGIN) { - alt_cxx = "g++"; + if (compiler_mode == GCC) { + + alt_cxx = clang_mode ? "clang++" : "g++"; + + } else { + + alt_cxx = "g++"; + + } } else { @@ -313,7 +322,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode >= GCC_PLUGIN) { - alt_cc = "gcc"; + if (compiler_mode == GCC) { + + alt_cc = clang_mode ? "clang" : "gcc"; + + } else { + + alt_cc = "gcc"; + + } } else { @@ -337,6 +354,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; + if (clang_mode) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } } if (compiler_mode == GCC_PLUGIN) { @@ -934,7 +956,9 @@ int main(int argc, char **argv, char **envp) { } else if (strncmp(callname, "afl-gcc", 7) == 0 || - strncmp(callname, "afl-g++", 7) == 0) { + strncmp(callname, "afl-g++", 7) == 0 || + + strncmp(callname, "afl-clang", 9) == 0) { compiler_mode = GCC; @@ -978,6 +1002,19 @@ int main(int argc, char **argv, char **envp) { } + + if (strncmp(callname, "afl-clang", 9) == 0) { + + clang_mode = 1; + + if (strncmp(callname, "afl-clang++", 11) == 0) { + + plusplus_mode = 1; + + } + + } + for (i = 1; i < argc; i++) { if (strncmp(argv[i], "--afl", 5) == 0) { -- cgit 1.4.1 From 1b75cc9f742ca6f5c95788269c66c346036b7233 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 30 Nov 2020 21:38:15 +0100 Subject: add DEBUGF --- src/afl-cc.c | 4 ++-- src/afl-ld-lto.c | 10 ++++------ src/afl-showmap.c | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 25ed923a..6d39b890 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1582,7 +1582,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); + DEBUGF("cd '%s';", getthecwd()); for (i = 0; i < argc; i++) SAYF(" '%s'", argv[i]); SAYF("\n"); @@ -1610,7 +1610,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); + DEBUGF("cd '%s';", getthecwd()); for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 771e2d0d..e6ea1f1d 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -182,9 +182,7 @@ static void edit_params(int argc, char **argv) { instrim = 1; if (debug) - SAYF(cMGN "[D] " cRST - "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " - "inst_present=%s rt_present=%s rt_lto_present=%s\n", + DEBUGF("passthrough=%s instrim=%d, gold_pos=%d, 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"); @@ -280,7 +278,7 @@ int main(int argc, char **argv) { if (getcwd(thecwd, sizeof(thecwd)) != 0) strcpy(thecwd, "."); - SAYF(cMGN "[D] " cRST "cd \"%s\";", thecwd); + DEBUGF("cd \"%s\";", thecwd); for (i = 0; i < argc; i++) SAYF(" \"%s\"", argv[i]); SAYF("\n"); @@ -315,7 +313,7 @@ int main(int argc, char **argv) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", thecwd); + DEBUGF("cd \"%s\";", thecwd); for (i = 0; i < ld_param_cnt; i++) SAYF(" \"%s\"", ld_params[i]); SAYF("\n"); @@ -333,7 +331,7 @@ int main(int argc, char **argv) { if (pid < 0) PFATAL("fork() failed"); if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed"); - if (debug) SAYF(cMGN "[D] " cRST "linker result: %d\n", status); + if (debug) DEBUGF("linker result: %d\n", status); if (!just_version) { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 69527007..a8e7d3f9 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -904,7 +904,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"); @@ -1066,7 +1066,7 @@ 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); + DEBUGF("%s:", fsrv->target_path); while (argv[i] != NULL) { SAYF(" \"%s\"", argv[i++]); -- cgit 1.4.1 From e769102491a4a5aa90afe57cce48211338133d3f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 30 Nov 2020 21:54:18 +0100 Subject: more DEBUGF --- include/debug.h | 10 +++++++ instrumentation/LLVMInsTrim.so.cc | 4 +-- instrumentation/afl-gcc-pass.so.cc | 36 +++++++++++------------ instrumentation/afl-llvm-common.cc | 34 ++++++++++----------- instrumentation/afl-llvm-lto-instrumentlist.so.cc | 9 +++--- src/afl-cc.c | 18 ++++-------- src/afl-ld-lto.c | 10 ++++--- 7 files changed, 60 insertions(+), 61 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/debug.h b/include/debug.h index e6d3c3fc..5512023c 100644 --- a/include/debug.h +++ b/include/debug.h @@ -270,6 +270,16 @@ \ } while (0) +/* Show a prefixed debug output. */ + +#define DEBUGF(x...) \ + do { \ + \ + SAYF(cMGN "[D] " cBRI "DEBUG: " cRST x); \ + SAYF(cRST ""); \ + \ + } while (0) + /* Error-checking versions of read() and write() that call RPFATAL() as appropriate. */ diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 61a420ba..6b3231e6 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -268,8 +268,8 @@ struct InsTrim : public ModulePass { for (auto &BB : F) if (BB.size() > 0) ++bb_cnt; - SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", - F.getName().str().c_str(), F.size(), bb_cnt); + DEBUGF("Function %s size %zu %u\n", F.getName().str().c_str(), F.size(), + bb_cnt); } diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index f94bb57f..e116e7d1 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -627,9 +627,8 @@ struct afl_pass : gimple_opt_pass { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -702,9 +701,8 @@ struct afl_pass : gimple_opt_pass { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -745,10 +743,10 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the deny function list, not " + "instrumenting ... \n", + instFunction.c_str()); return false; } @@ -825,10 +823,10 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); return true; } @@ -859,11 +857,11 @@ struct afl_pass : gimple_opt_pass { if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - IDENTIFIER_POINTER(DECL_NAME(F->decl)), - source_file.c_str()); + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); return true; } diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 189b4ec6..21c4d204 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -173,9 +173,8 @@ void initInstrumentList() { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } @@ -248,9 +247,8 @@ void initInstrumentList() { } if (debug) - SAYF(cMGN "[D] " cRST - "loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); } @@ -409,10 +407,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the deny function list, not instrumenting " + "... \n", + instFunction.c_str()); return false; } @@ -489,10 +487,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", - instFunction.c_str()); + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); return true; } @@ -523,10 +521,10 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { if (debug) - SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " - "instrumenting ... \n", - F->getName().str().c_str(), source_file.c_str()); + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + F->getName().str().c_str(), source_file.c_str()); return true; } diff --git a/instrumentation/afl-llvm-lto-instrumentlist.so.cc b/instrumentation/afl-llvm-lto-instrumentlist.so.cc index a7331444..416dbb88 100644 --- a/instrumentation/afl-llvm-lto-instrumentlist.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentlist.so.cc @@ -105,15 +105,14 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { if (isInInstrumentList(&F)) { if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); + DEBUGF("function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + DEBUGF("function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); auto & Ctx = F.getContext(); AttributeList Attrs = F.getAttributes(); diff --git a/src/afl-cc.c b/src/afl-cc.c index 6d39b890..cc9854b6 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -354,11 +354,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; - if (clang_mode) { + if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } - cc_params[cc_par_cnt++] = "-no-integrated-as"; - - } } if (compiler_mode == GCC_PLUGIN) { @@ -708,7 +705,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || lto_mode) { + 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"; @@ -1002,16 +1000,11 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0) { clang_mode = 1; - if (strncmp(callname, "afl-clang++", 11) == 0) { - - plusplus_mode = 1; - - } + if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } } @@ -1085,8 +1078,7 @@ int main(int argc, char **argv, char **envp) { 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"); + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_LLVM_INSTRIM together"); } diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index e6ea1f1d..16feaa80 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -182,10 +182,12 @@ static void edit_params(int argc, char **argv) { instrim = 1; if (debug) - DEBUGF("passthrough=%s instrim=%d, gold_pos=%d, 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"); + DEBUGF( + "passthrough=%s instrim=%d, gold_pos=%d, 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 < argc; i++) { -- cgit 1.4.1 From 1890d7b9cf5a32368c0700ef790f97087b948e1f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Dec 2020 15:03:21 +0100 Subject: very complete runtime lookup rewrite --- src/afl-cc.c | 197 ++++++++++++++++++++++++++++---------------------- test/test-llvm-lto.sh | 2 +- 2 files changed, 113 insertions(+), 86 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index cc9854b6..2b5ae7c3 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -106,20 +106,42 @@ u8 *getthecwd() { } -/* Try to find the runtime libraries. If that fails, abort. */ +/* 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 has path information and use it + a) we also check ../lib/ + 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + FreeBSD with procfs + a) and check here in ../lib/ too + 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) + 6. we finally try the current directory + + if this all fail - we fail. +*/ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; + if (obj_path) { + + tmp = alloc_printf("%s/%s", obj_path, obj); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + if (afl_path) { -#ifdef __ANDROID__ tmp = alloc_printf("%s/%s", afl_path, obj); -#else - tmp = alloc_printf("%s/%s", afl_path, obj); -#endif if (!access(tmp, R_OK)) { @@ -132,125 +154,117 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } - if (argv0) slash = strrchr(argv0, '/'); + if (argv0) { - if (slash) { + slash = strrchr(argv0, '/'); - u8 *dir; + if (slash) { - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; + u8 *dir = ck_strdup(argv0); -#ifdef __ANDROID__ - tmp = alloc_printf("%s/%s", dir, obj); -#else - tmp = alloc_printf("%s/%s", dir, obj); -#endif + slash = strrchr(dir, '/'); + *slash = 0; - if (!access(tmp, R_OK)) { + tmp = alloc_printf("%s/%s", dir, obj); - obj_path = dir; - return tmp; + if (!access(tmp, R_OK)) { - } + obj_path = dir; + return tmp; - ck_free(tmp); - ck_free(dir); + } - } + ck_free(tmp); + tmp = alloc_printf("%s/../lib/%s", dir, obj); - tmp = alloc_printf("%s/%s", AFL_PATH, obj); -#ifdef __ANDROID__ - if (!access(tmp, R_OK)) { + if (!access(tmp, R_OK)) { -#else - if (!access(tmp, R_OK)) { + u8 *dir2 = alloc_printf("%s/../lib", dir); + obj_path = dir2; + ck_free(dir); + return tmp; -#endif + } - obj_path = AFL_PATH; - return tmp; + ck_free(tmp); + ck_free(dir); - } + } else { - ck_free(tmp); - return NULL; + char *procname = NULL; +#if defined(__FreeBSD__) || defined(__DragonFly__) + procname = "/proc/curproc/file"; + #elsif defined(__linux__) || defined(__ANDROID__) + procname = "/proc/self/exe"; + #elsif 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) { -/* Try to find the runtime libraries. If that fails, abort. */ + exepath[exepath_len] = 0; + slash = strrchr(exepath, '/'); -static void find_obj(u8 *argv0) { + if (slash) { - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash, *tmp; + *slash = 0; + tmp = alloc_printf("%s/%s", exepath, obj); - if (afl_path) { + if (!access(tmp, R_OK)) { -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-compiler-rt.so", afl_path); -#else - tmp = alloc_printf("%s/afl-compiler-rt.o", afl_path); -#endif + u8 *dir = alloc_printf("%s/../lib/", exepath); + obj_path = dir; + return tmp; - if (!access(tmp, R_OK)) { + } - obj_path = afl_path; - ck_free(tmp); - return; + ck_free(tmp); + tmp = alloc_printf("%s/../lib/%s", exepath, obj); - } + if (!access(tmp, R_OK)) { - ck_free(tmp); + u8 *dir = alloc_printf("%s/../lib/", exepath); + obj_path = dir; + return tmp; - } - - slash = strrchr(argv0, '/'); + } - if (slash) { + } - u8 *dir; + } - *slash = 0; - dir = ck_strdup(argv0); - *slash = '/'; + } -#ifdef __ANDROID__ - tmp = alloc_printf("%s/afl-compiler-rt.so", dir); -#else - tmp = alloc_printf("%s/afl-compiler-rt.o", dir); -#endif + } - if (!access(tmp, R_OK)) { + } - obj_path = dir; - ck_free(tmp); - return; + tmp = alloc_printf("%s/%s", AFL_PATH, obj); - } + if (!access(tmp, R_OK)) { - ck_free(tmp); - ck_free(dir); + obj_path = AFL_PATH; + return tmp; } -#ifdef __ANDROID__ - if (!access(AFL_PATH "/afl-compiler-rt.so", R_OK)) { + ck_free(tmp); -#else - if (!access(AFL_PATH "/afl-compiler-rt.o", R_OK)) { + tmp = alloc_printf("./%s", obj); -#endif + if (!access(tmp, R_OK)) { - obj_path = AFL_PATH; - return; + obj_path = "."; + return tmp; } - FATAL( - "Unable to find 'afl-compiler-rt.o' or 'afl-llvm-pass.so'. Please set " - "AFL_PATH"); + ck_free(tmp); + + return NULL; } @@ -360,8 +374,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode == GCC_PLUGIN) { - char *fplugin_arg = - alloc_printf("-fplugin=%s", find_object("afl-gcc-pass.so", argvnull)); + char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); cc_params[cc_par_cnt++] = fplugin_arg; } @@ -1594,10 +1607,24 @@ int main(int argc, char **argv, char **envp) { if (!be_quiet && cmplog_mode) printf("CmpLog mode by \n"); -#ifndef __ANDROID__ - find_obj(argv[0]); +#ifdef __ANDROID__ + ptr = find_object("afl-compiler-rt.so", argv[0]); +#else + ptr = find_object("afl-compiler-rt.o", argv[0]); #endif + if (debug) { DEBUGF("obj_path=%s\n", obj_path); } + + if (!ptr) { + + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); + + } + + ck_free(ptr); + edit_params(argc, argv, envp); if (debug) { diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index e10f4cf7..d0b8f8fc 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -57,7 +57,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_mode.c > /dev/null 2>&1 + ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" -- cgit 1.4.1 From 0f803c63dfb1dafdef3bfe1b43674157efcd7107 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Dec 2020 15:08:08 +0100 Subject: move debug print --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 2b5ae7c3..854359e2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1613,8 +1613,6 @@ int main(int argc, char **argv, char **envp) { ptr = find_object("afl-compiler-rt.o", argv[0]); #endif - if (debug) { DEBUGF("obj_path=%s\n", obj_path); } - if (!ptr) { FATAL( @@ -1623,6 +1621,8 @@ int main(int argc, char **argv, char **envp) { } + if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } + ck_free(ptr); edit_params(argc, argv, envp); -- cgit 1.4.1 From 295ddaf96b411b41017e609b6b3537db78147dfa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 3 Dec 2020 15:19:10 +0100 Subject: fix for afl-cc --- src/afl-cc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 854359e2..cc4f44f5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -114,10 +114,10 @@ u8 *getthecwd() { 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 has path information and use it - a) we also check ../lib/ + 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/ too + 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 @@ -175,11 +175,11 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } ck_free(tmp); - tmp = alloc_printf("%s/../lib/%s", dir, obj); + tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); if (!access(tmp, R_OK)) { - u8 *dir2 = alloc_printf("%s/../lib", dir); + u8 *dir2 = alloc_printf("%s/../lib/afl", dir); obj_path = dir2; ck_free(dir); return tmp; @@ -215,18 +215,18 @@ static u8 *find_object(u8 *obj, u8 *argv0) { if (!access(tmp, R_OK)) { - u8 *dir = alloc_printf("%s/../lib/", exepath); + u8 *dir = alloc_printf("%s", exepath); obj_path = dir; return tmp; } ck_free(tmp); - tmp = alloc_printf("%s/../lib/%s", exepath, obj); + tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); if (!access(tmp, R_OK)) { - u8 *dir = alloc_printf("%s/../lib/", exepath); + u8 *dir = alloc_printf("%s/../lib/afl/", exepath); obj_path = dir; return tmp; -- cgit 1.4.1 From f8c33f29e8cf7de30604be2c6818dd8d2d0ad3a6 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 5 Dec 2020 09:19:14 +0100 Subject: Typos --- src/afl-cc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index cc4f44f5..fcc10122 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -113,15 +113,15 @@ u8 *getthecwd() { 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 has path information and use it + 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 + 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 this all fail - we fail. + if all this fails - we fail. */ static u8 *find_object(u8 *obj, u8 *argv0) { -- cgit 1.4.1 From 8f79116a15e53f0ecd4889ebf8980cc0cd85a494 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 5 Dec 2020 09:48:55 +0100 Subject: fix find_object proc search (#elsif -> #elif), optimize static if away --- src/afl-cc.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index fcc10122..1cd53f98 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -189,14 +189,25 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); ck_free(dir); - } else { + } + +#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"; - #elsif defined(__linux__) || defined(__ANDROID__) + #elif defined(__linux__) || defined(__ANDROID__) procname = "/proc/self/exe"; - #elsif defined(__NetBSD__) + #elif defined(__NetBSD__) procname = "/proc/curproc/exe"; #endif if (procname) { @@ -239,6 +250,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } } +#endif +#undef HAS_PROC_FS } -- cgit 1.4.1 From 4c2e375e22a0b3c88380e6dd51dbac5d2c5ac5ff Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 7 Dec 2020 14:29:59 +0100 Subject: little fixes --- README.md | 2 +- src/afl-cc.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/README.md b/README.md index e2cb04f7..94d5008e 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). - * utils/ got renamed to utils/ + * examples/ got renamed to utils/ ## Contents diff --git a/src/afl-cc.c b/src/afl-cc.c index 1cd53f98..50334139 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -121,7 +121,8 @@ u8 *getthecwd() { 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) 6. we finally try the current directory - if all this fails - we fail. + 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) { -- cgit 1.4.1 From e6de85861c4ec8fcc13a7f945bc8b3dfefa193bc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 7 Dec 2020 14:36:04 +0100 Subject: fixes and code format --- instrumentation/README.lto.md | 4 ++-- src/afl-cc.c | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index 37d51de8..a2814173 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -80,8 +80,8 @@ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - apt-get update && apt-get upgrade -y apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \ - libclang-common-12-dev libclang-cpp11 libclang-cpp11-dev liblld-12 \ - liblld-12-dev liblldb-12 liblldb-12-dev libllvm11 libomp-12-dev \ + libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \ + liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \ libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools ``` diff --git a/src/afl-cc.c b/src/afl-cc.c index 50334139..273a9f2f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -192,25 +192,21 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } -#if \ - defined(__FreeBSD__) \ -|| defined(__DragonFly__) \ -|| defined(__linux__) \ -|| defined(__ANDROID__) \ -|| defined(__NetBSD__) -#define HAS_PROC_FS 1 +#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__) + #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 + #endif if (procname) { char exepath[PATH_MAX]; @@ -251,6 +247,7 @@ static u8 *find_object(u8 *obj, u8 *argv0) { } } + #endif #undef HAS_PROC_FS -- cgit 1.4.1 From a686c1361ce353c6f47939eb7af1f264f0ce4853 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Dec 2020 15:31:26 +0100 Subject: fix afl-cc library search --- src/afl-cc.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 273a9f2f..14d8e070 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -130,20 +130,27 @@ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; - if (obj_path) { + /* + if (obj_path) { - tmp = alloc_printf("%s/%s", obj_path, obj); + tmp = alloc_printf("%s/%s", obj_path, obj); - if (!access(tmp, R_OK)) { return tmp; } + if (debug) DEBUGF("Trying %s\n", tmp); - ck_free(tmp); + if (!access(tmp, R_OK)) { return tmp; } - } + ck_free(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; @@ -168,6 +175,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", dir, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = dir; @@ -178,6 +187,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { 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); @@ -232,6 +243,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { 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); @@ -255,6 +268,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", AFL_PATH, obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = AFL_PATH; @@ -266,6 +281,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("./%s", obj); + if (debug) DEBUGF("Trying %s\n", tmp); + if (!access(tmp, R_OK)) { obj_path = "."; @@ -275,6 +292,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); + if (debug) DEBUGF("Trying ... giving up\n"); + return NULL; } -- cgit 1.4.1 From 2bf68a0bf45fb2bb3bc0f574f20959a62c9f8239 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 11:19:26 +0100 Subject: fix MMAP --- GNUmakefile.llvm | 4 ++-- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-cc.c | 27 +++++++++++---------------- src/afl-fuzz.c | 8 +++++++- 4 files changed, 21 insertions(+), 20 deletions(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 6e80de81..414cd487 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -288,13 +288,13 @@ ifeq "$(shell echo '$(HASH)include @$(HASH)include @int ma SHMAT_OK=1 else SHMAT_OK=0 - #CFLAGS+=-DUSEMMAP=1 + CFLAGS_SAFE += -DUSEMMAP=1 LDFLAGS += -Wno-deprecated-declarations endif ifeq "$(TEST_MMAP)" "1" SHMAT_OK=0 - CFLAGS+=-DUSEMMAP=1 + CFLAGS_SAFE += -DUSEMMAP=1 LDFLAGS += -Wno-deprecated-declarations endif diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 99dcbb67..afe0839e 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1107,7 +1107,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ - *(start++) = R(MAP_SIZE - 1) + 1; + *(start++) = ++__afl_final_loc; while (start < stop) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 14d8e070..37cfc7c8 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -130,21 +130,6 @@ static u8 *find_object(u8 *obj, u8 *argv0) { u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; - /* - if (obj_path) { - - tmp = alloc_printf("%s/%s", obj_path, obj); - - if (debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - - } - - */ - if (afl_path) { tmp = alloc_printf("%s/%s", afl_path, obj); @@ -906,6 +891,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif +#ifdef USEMMAP + cc_params[cc_par_cnt++] = "-lrt"; +#endif + } #endif @@ -1475,9 +1464,15 @@ int main(int argc, char **argv, char **envp) { if (have_llvm) SAYF("afl-cc LLVM version %d with the the binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); - if (have_lto || have_llvm) SAYF("\n"); #endif +#ifdef USEMMAP + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); +#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" diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bbe6aec6..391d4c4f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -230,6 +230,12 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled without python module support\n"); #endif +#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 @@ -254,7 +260,7 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS\n\n"); #endif - SAYF("For additional help please consult %s/README.md\n\n", doc_path); + SAYF("For additional help please consult %s/README.md :)\n\n", doc_path); exit(1); #undef PHYTON_SUPPORT -- cgit 1.4.1 From cc781e44f36d716f05da13bf760154a7810bfe43 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 11:21:28 +0100 Subject: code format --- src/afl-cc.c | 8 ++++---- src/afl-fuzz-queue.c | 13 ++++++++----- src/afl-fuzz-state.c | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 37cfc7c8..c43ac2c1 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -891,9 +891,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif -#ifdef USEMMAP + #ifdef USEMMAP cc_params[cc_par_cnt++] = "-lrt"; -#endif + #endif } @@ -1467,9 +1467,9 @@ int main(int argc, char **argv, char **envp) { #endif #ifdef USEMMAP - SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); #else - SAYF("Compiled with shmat support.\n"); + SAYF("Compiled with shmat support.\n"); #endif SAYF("\n"); diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index d74c07a1..54afa17c 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -46,14 +46,17 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, double avg_exec_us, double avg_bitmap_size) { u32 hits; - + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { - + hits = afl->n_fuzz[q->n_fuzz_entry]; if (hits == 0) { hits = 1; } - - } else { hits = 1; } - + + } else { + + hits = 1; + + } double weight = 1.0; weight *= avg_exec_us / q->exec_us; diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 36da2730..9c51a3ef 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ 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 = FAST ; /* Power schedule (default: FAST) */ + afl->schedule = FAST; /* Power schedule (default: FAST) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ -- cgit 1.4.1 From e0ab846f7fd3e45bbf76e4ab82eef19d9aaf5494 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 09:37:52 +0100 Subject: v3.00c --- README.md | 2 +- include/config.h | 2 +- src/afl-cc.c | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) (limited to 'src/afl-cc.c') diff --git a/README.md b/README.md index dc009def..68b64ce6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Travis State](https://api.travis-ci.com/AFLplusplus/AFLplusplus.svg?branch=stable) - Release Version: [2.68c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases) Github Version: 3.00a diff --git a/include/config.h b/include/config.h index 491d8132..93249ed9 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++3.00a" +#define VERSION "++3.00c" /****************************************************** * * diff --git a/src/afl-cc.c b/src/afl-cc.c index c43ac2c1..2aeb2178 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -69,6 +69,7 @@ enum { INSTRUMENT_INSTRIM = 3, INSTRUMENT_CFG = 3, INSTRUMENT_LTO = 4, + INSTRUMENT_LLVMNATIVE = 5, INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16 @@ -76,8 +77,9 @@ enum { char instrument_mode_string[18][18] = { - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "", "", "CTX", "", - "", "", "", "", "", "", "NGRAM", "" + "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "PCGUARD-NATIVE", + "", "CTX", "", "", "", "", "", + "", "", "NGRAM", "" }; @@ -580,6 +582,14 @@ static void edit_params(u32 argc, char **argv, char **envp) { #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"; @@ -1162,6 +1172,18 @@ int main(int argc, char **argv, char **envp) { } + // this is a hidden option + if (strncasecmp(ptr, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr, "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(ptr, "cfg", strlen("cfg")) == 0 || strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { -- cgit 1.4.1 From 98ee17bc47b1f1d4664be9955a72727fff8e51fa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 14:30:06 +0100 Subject: fix endless loop in afl-cc allow/blocklists starting a line with a comment --- docs/Changelog.md | 8 +++- include/config.h | 13 ++++--- include/debug.h | 76 ++++++++++++++++++++++---------------- include/envs.h | 4 +- instrumentation/afl-gcc-pass.so.cc | 6 ++- instrumentation/afl-llvm-common.cc | 6 ++- src/afl-cc.c | 2 +- src/afl-fuzz-state.c | 3 +- src/afl-fuzz.c | 14 +++++-- 9 files changed, 80 insertions(+), 52 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index ebc514f3..28b7e723 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,9 +10,13 @@ sending a mail to . ### Version ++3.01a (release) - - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - fix crash for very, very fast targets+systems (thanks to mhlakhani + for reporting) - added dummy Makefile to instrumentation/ - - allow instrumenting LLVMFuzzerTestOneInput + - afl-cc + - allow instrumenting LLVMFuzzerTestOneInput + - fixed endless loop for allow/blocklist lines starting with a + comment (thanks to Zherya for reporting) ### Version ++3.00c (release) diff --git a/include/config.h b/include/config.h index d57ef223..e8a49270 100644 --- a/include/config.h +++ b/include/config.h @@ -37,14 +37,14 @@ ******************************************************/ /* console output colors: There are three ways to configure its behavior - * 1. default: colored outputs fixed on: defined USE_COLOR && defined ALWAYS_COLORED - * The env var. AFL_NO_COLOR will have no effect + * 1. default: colored outputs fixed on: defined USE_COLOR && defined + * ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect * 2. defined USE_COLOR && !defined ALWAYS_COLORED * -> depending on env var AFL_NO_COLOR=1 colors can be switched off * at run-time. Default is to use colors. * 3. colored outputs fixed off: !defined USE_COLOR * The env var. AFL_NO_COLOR will have no effect -*/ + */ /* Comment out to disable terminal colors (note that this makes afl-analyze a lot less nice): */ @@ -52,9 +52,10 @@ #define USE_COLOR #ifdef USE_COLOR -/* Comment in to always enable terminal colors */ -/* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR */ -#define ALWAYS_COLORED 1 + /* Comment in to always enable terminal colors */ + /* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR + */ + #define ALWAYS_COLORED 1 #endif /* StatsD config diff --git a/include/debug.h b/include/debug.h index 7a1725b5..7f4a6be1 100644 --- a/include/debug.h +++ b/include/debug.h @@ -169,65 +169,77 @@ ************************/ #if defined USE_COLOR && !defined ALWAYS_COLORED -#include -#pragma GCC diagnostic ignored "-Wformat-security" -static inline const char * colorfilter(const char * x) { + #include + #pragma GCC diagnostic ignored "-Wformat-security" +static inline const char *colorfilter(const char *x) { + static int once = 1; static int disabled = 0; if (once) { + /* when there is no tty -> we always want filtering * when AFL_NO_UI is set filtering depends on AFL_NO_COLOR * otherwise we want always colors */ - disabled = isatty(2) && (!getenv("AFL_NO_UI") || (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); + disabled = + isatty(2) && (!getenv("AFL_NO_UI") || + (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); once = 0; + } + if (likely(disabled)) return x; static char monochromestring[4096]; - char *d = monochromestring; - int in_seq = 0; + char * d = monochromestring; + int in_seq = 0; + + while (*x) { - while(*x) { if (in_seq && *x == 'm') { + in_seq = 0; + } else { + if (!in_seq && *x == '\x1b') { in_seq = 1; } - if (!in_seq) { - *d++ = *x; - } + if (!in_seq) { *d++ = *x; } + } + ++x; + } *d = '\0'; return monochromestring; + } + #else -#define colorfilter(x) x /* no filtering necessary */ + #define colorfilter(x) x /* no filtering necessary */ #endif /* macro magic to transform the first parameter to SAYF * through colorfilter which strips coloring */ -#define GET_MACRO(_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,_26,_27,_28,_29,_30,\ -_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,\ -NAME,...) NAME - -#define SAYF(...) GET_MACRO(__VA_ARGS__, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) - -#define SAYF_1(x) MY_SAYF(colorfilter(x)) -#define SAYF_N(x,...) MY_SAYF(colorfilter(x), __VA_ARGS__) +#define GET_MACRO(_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, _26, \ + _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, \ + _39, _40, NAME, ...) \ + NAME + +#define SAYF(...) \ + GET_MACRO(__VA_ARGS__, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_1) \ + (__VA_ARGS__) + +#define SAYF_1(x) MY_SAYF(colorfilter(x)) +#define SAYF_N(x, ...) MY_SAYF(colorfilter(x), __VA_ARGS__) /* Just print stuff to the appropriate stream. */ #ifdef MESSAGES_TO_STDOUT @@ -282,7 +294,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ @@ -295,7 +307,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ abort(); \ @@ -309,7 +321,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) \ fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/include/envs.h b/include/envs.h index f16e61f0..e4e49c4d 100644 --- a/include/envs.h +++ b/include/envs.h @@ -78,8 +78,8 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CTX", "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", - "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", + "AFL_LLVM_INSTRUMENT", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", "AFL_LLVM_SKIPSINGLEBLOCK", @@ -103,7 +103,7 @@ static char *afl_environment_variables[] = { "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED "AFL_NO_COLOR", "AFL_NO_COLOUR", #endif diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c95ead8f..25437609 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -622,10 +622,11 @@ struct afl_pass : gimple_opt_pass { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -696,10 +697,11 @@ struct afl_pass : gimple_opt_pass { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 557939fd..a27c4069 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -168,10 +168,11 @@ void initInstrumentList() { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -242,10 +243,11 @@ void initInstrumentList() { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/src/afl-cc.c b/src/afl-cc.c index 2aeb2178..8593f9b8 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1432,7 +1432,7 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " "functions\n" - " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " "instrument allow/\n" " deny listing (selective instrumentation)\n"); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e863c4c7..7053572b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -401,7 +401,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_crash_exitcode = (u8 *)get_afl_env(afl_environment_variables[i]); -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED + } else if (!strncmp(env, "AFL_NO_COLOR", afl_environment_variable_len)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e0d46f7e..2af374f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -157,7 +157,8 @@ static void usage(u8 *argv0, int more_help) { 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" + #define DYN_COLOR \ + "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" #else #define DYN_COLOR #endif @@ -307,11 +308,16 @@ int main(int argc, char **argv_orig, char **envp) { struct timeval tv; struct timezone tz; -#if defined USE_COLOR && defined ALWAYS_COLORED + #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)"); + + WARNF( + "Setting AFL_NO_COLOR has no effect (colors are configured on at " + "compile time)"); + } -#endif + + #endif char **argv = argv_cpy_dup(argc, argv_orig); -- cgit 1.4.1 From 2e3cf10070681375a6c0e63ad39e7ce04ff22684 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 22:53:41 +0100 Subject: document AFL_LLVM_INSTRUMENT option NATIVE --- docs/Changelog.md | 2 ++ docs/env_variables.md | 2 ++ instrumentation/README.llvm.md | 21 +-------------------- src/afl-cc.c | 3 +++ 4 files changed, 8 insertions(+), 20 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 28b7e723..a26a4e0e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,8 @@ sending a mail to . - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) + - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support + (less performant than our own) ### Version ++3.00c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index 74863d8d..c1693748 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -113,6 +113,8 @@ Then there are a few specific features that are only available in instrumentatio - `AFL_LLVM_INSTRUMENT` - this configures the instrumentation mode. Available options: + PCGUARD - our own pcgard based instrumentation (default) + NATIVE - clang's original pcguard based instrumentation CLASSIC - classic AFL (map[cur_loc ^ prev_loc >> 1]++) (default) CFG - InsTrim instrumentation (see below) LTO - LTO instrumentation (see below) diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 07636970..2705ce0d 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -168,26 +168,7 @@ This is the most powerful and effective fuzzing you can do. Please see [README.persistent_mode.md](README.persistent_mode.md) for a full explanation. -## 7) Bonus feature: 'trace-pc-guard' mode - -LLVM is shipping with a built-in execution tracing feature -that provides AFL with the necessary tracing data without the need to -post-process the assembly or install any compiler plugins. See: - - http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards - -If you have not an outdated compiler and want to give it a try, build -targets this way: - -``` -AFL_LLVM_INSTRUMENT=PCGUARD make -``` - -Note that this is currently the default if you use LLVM >= 7, as it is the best -mode. Recommended is LLVM >= 9. -If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. - -## 8) Bonus feature: 'dict2file' pass +## 7) Bonus feature: 'dict2file' pass Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation all constant string compare parameters will be written to this file to be diff --git a/src/afl-cc.c b/src/afl-cc.c index 8593f9b8..6f4801de 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1346,6 +1346,9 @@ int main(int argc, char **argv, char **envp) { "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + " NATIVE: use llvm's native PCGUARD instrumentation (less performant)\n" +#endif " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " -- cgit 1.4.1 From 7e27448dac2191060320831904f32fe9d572bc3d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 21 Dec 2020 12:19:22 +0100 Subject: another 32 bit fix --- include/afl-fuzz.h | 4 ++++ include/coverage-32.h | 2 +- src/afl-cc.c | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 99647c5b..e2fb0344 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1022,7 +1022,11 @@ u32 count_bytes(afl_state_t *, u8 *); u32 count_non_255_bytes(afl_state_t *, u8 *); void simplify_trace(afl_state_t *, u8 *); void classify_counts(afl_forkserver_t *); +#ifdef WORD_SIZE_64 void discover_word(u8 *ret, u64 *current, u64 *virgin); +#else +void discover_word(u8 *ret, u32 *current, u32 *virgin); +#endif void init_count_class16(void); void minimize_bits(afl_state_t *, u8 *, u8 *); #ifndef SIMPLE_FILES diff --git a/include/coverage-32.h b/include/coverage-32.h index 124d6ee5..a5cc498c 100644 --- a/include/coverage-32.h +++ b/include/coverage-32.h @@ -47,7 +47,7 @@ void simplify_trace(afl_state_t *afl, u8 *bytes) { inline void classify_counts(afl_forkserver_t *fsrv) { - u64 *mem = (u32 *)fsrv->trace_bits; + u32 *mem = (u32 *)fsrv->trace_bits; u32 i = (fsrv->map_size >> 2); while (i--) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 6f4801de..3b8092a9 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1347,7 +1347,8 @@ int main(int argc, char **argv, char **envp) { "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - " NATIVE: use llvm's native PCGUARD instrumentation (less performant)\n" + " NATIVE: use llvm's native PCGUARD instrumentation (less " + "performant)\n" #endif " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" -- cgit 1.4.1 From 27b9ba45026397ee0605dd88aab359c4c1dea4cc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Dec 2020 10:51:40 +0100 Subject: better gcc and clang support for afl-cc --- docs/Changelog.md | 3 +- docs/env_variables.md | 2 + src/afl-cc.c | 134 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 123 insertions(+), 16 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index e36e4e9f..cf9bfbe1 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,7 +20,8 @@ sending a mail to . - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard - support (less performant than our own) + support (less performant than our own), GCC for old afl-gcc and + CLANG for old afl-clang - added dummy Makefile to instrumentation/ diff --git a/docs/env_variables.md b/docs/env_variables.md index c1693748..e6b9381b 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -120,6 +120,8 @@ Then there are a few specific features that are only available in instrumentatio LTO - LTO instrumentation (see below) CTX - context sensitive instrumentation (see below) NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16) + GCC - outdated gcc instrumentation + CLANG - outdated clang instrumentation In CLASSIC (default) and CFG/INSTRIM you can also specify CTX and/or NGRAM, seperate the options with a comma "," then, e.g.: `AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4` diff --git a/src/afl-cc.c b/src/afl-cc.c index 3b8092a9..a18f87db 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -62,7 +62,7 @@ u8 use_stdin; /* dummy */ enum { - INSTURMENT_DEFAULT = 0, + INSTRUMENT_DEFAULT = 0, INSTRUMENT_CLASSIC = 1, INSTRUMENT_AFL = 1, INSTRUMENT_PCGUARD = 2, @@ -70,6 +70,8 @@ enum { INSTRUMENT_CFG = 3, INSTRUMENT_LTO = 4, INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16 @@ -77,9 +79,24 @@ enum { char instrument_mode_string[18][18] = { - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "PCGUARD-NATIVE", - "", "CTX", "", "", "", "", "", - "", "", "NGRAM", "" + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" }; @@ -89,14 +106,15 @@ enum { LTO = 1, LLVM = 2, GCC_PLUGIN = 3, - GCC = 4 + GCC = 4, + CLANG = 5 }; -char compiler_mode_string[6][12] = { +char compiler_mode_string[7][12] = { "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", - "GCC", "" + "GCC", "CLANG", "" }; @@ -324,6 +342,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cxx = clang_mode ? "clang++" : "g++"; + } else if (compiler_mode == CLANG) { + + alt_cxx = "clang++"; + } else { alt_cxx = "g++"; @@ -357,6 +379,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cc = clang_mode ? "clang" : "gcc"; + } else if (compiler_mode == CLANG) { + + alt_cc = "clang"; + } else { alt_cc = "gcc"; @@ -380,12 +406,16 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (compiler_mode == GCC) { + if (compiler_mode == GCC || compiler_mode == CLANG) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; - if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } + if (clang_mode || compiler_mode == CLANG) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } } @@ -996,12 +1026,14 @@ int main(int argc, char **argv, char **envp) { } else if (strncmp(callname, "afl-gcc", 7) == 0 || - strncmp(callname, "afl-g++", 7) == 0 || - - strncmp(callname, "afl-clang", 9) == 0) { + strncmp(callname, "afl-g++", 7) == 0) { compiler_mode = GCC; + } else if (strncmp(callname, "afl-clang", 9) == 0) { + + compiler_mode = CLANG; + } if ((ptr = getenv("AFL_CC_COMPILER"))) { @@ -1045,6 +1077,7 @@ int main(int argc, char **argv, char **envp) { if (strncmp(callname, "afl-clang", 9) == 0) { clang_mode = 1; + compiler_mode = CLANG; if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } @@ -1072,6 +1105,34 @@ int main(int argc, char **argv, char **envp) { 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 || @@ -1083,6 +1144,10 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; + } else if (strcasecmp(ptr, "CLANG") == 0) { + + compiler_mode = CLANG; + } else FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); @@ -1212,6 +1277,28 @@ int main(int argc, char **argv, char **envp) { } + if (strcasecmp(ptr, "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(ptr, "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(ptr, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; @@ -1270,6 +1357,22 @@ int main(int argc, char **argv, char **envp) { } + if (compiler_mode == GCC) { + + if (clang_mode) { + + instrument_mode = CLANG; + + } else { + + instrument_mode = GCC; + + } + + } + + if (compiler_mode == CLANG) { instrument_mode = CLANG; } + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { printf("afl-cc" VERSION @@ -1316,7 +1419,7 @@ int main(int argc, char **argv, char **envp) { " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " " yes\n" - " [GCC] simple gcc: %s%s\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", have_lto ? "AVAILABLE" : "unavailable!", @@ -1328,7 +1431,7 @@ int main(int argc, char **argv, char **envp) { have_gcc_plugin ? "AVAILABLE" : "unavailable!", compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", have_gcc ? "AVAILABLE" : "unavailable!", - compiler_mode == GCC ? " [SELECTED]" : ""); + (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : ""); SAYF( "Modes:\n" @@ -1445,7 +1548,8 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " + "NGRAM-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" -- cgit 1.4.1 From 9759320266d3f334f71d06eed5267d78de1837d8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Dec 2020 12:33:58 +0100 Subject: afl-clang-fast fix --- src/afl-cc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index a18f87db..66f4860f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1030,7 +1030,9 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strncmp(callname, "afl-clang", 9) == 0) { + } else if (strncmp(callname, "afl-clang", 9) == 0 && + + strstr(callname, "fast") == NULL) { compiler_mode = CLANG; @@ -1074,7 +1076,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0) { + if (strncmp(callname, "afl-clang", 9) == 0 && + strstr(callname, "fast") == NULL) { clang_mode = 1; compiler_mode = CLANG; @@ -1144,7 +1147,7 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strcasecmp(ptr, "CLANG") == 0) { + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { compiler_mode = CLANG; -- cgit 1.4.1 From a4fd4ea0f46529feb09577a13cc7c053fb22146f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 25 Dec 2020 12:13:45 +0100 Subject: fix LTO --- GNUmakefile | 6 +++--- src/afl-cc.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/afl-cc.c') diff --git a/GNUmakefile b/GNUmakefile index b9c806c3..db2ad572 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -307,7 +307,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu .PHONY: llvm llvm: - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm @test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; } .PHONY: gcc_plugin @@ -579,7 +579,7 @@ deepclean: clean .PHONY: distrib distrib: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap @@ -602,7 +602,7 @@ binary-only: test_shm test_python ready $(PROGS) .PHONY: source-only source-only: all - -$(MAKE) -j4 -f GNUmakefile.llvm + -$(MAKE) -j -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap diff --git a/src/afl-cc.c b/src/afl-cc.c index 66f4860f..00e9cfce 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1030,9 +1030,9 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; - } else if (strncmp(callname, "afl-clang", 9) == 0 && + } else if (strcmp(callname, "afl-clang") == 0 || - strstr(callname, "fast") == NULL) { + strcmp(callname, "afl-clang++") == 0) { compiler_mode = CLANG; @@ -1076,13 +1076,13 @@ int main(int argc, char **argv, char **envp) { } - if (strncmp(callname, "afl-clang", 9) == 0 && - strstr(callname, "fast") == NULL) { + if (strcmp(callname, "afl-clang") == 0 || + strcmp(callname, "afl-clang++") == 0) { clang_mode = 1; compiler_mode = CLANG; - if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } + if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; } } -- cgit 1.4.1 From d103e39f5866fa29a13e7e8d4204b1e962100369 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 28 Dec 2020 17:06:55 +0100 Subject: fix instrumentation type display --- src/afl-cc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 00e9cfce..758b958b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1364,17 +1364,17 @@ int main(int argc, char **argv, char **envp) { if (clang_mode) { - instrument_mode = CLANG; + instrument_mode = INSTRUMENT_CLANG; } else { - instrument_mode = GCC; + instrument_mode = INSTRUMENT_GCC; } } - if (compiler_mode == CLANG) { instrument_mode = CLANG; } + if (compiler_mode == CLANG) { instrument_mode = INSTRUMENT_CLANG; } if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { -- cgit 1.4.1 From 83c1378fc1810d177d9cdfa2c00f62bbadd98f00 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 14:19:35 +0100 Subject: fix afl-clang on 32-Bit systems --- qemu_mode/qemuafl | 1 - src/afl-cc.c | 2 +- unicorn_mode/unicornafl | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 160000 qemu_mode/qemuafl delete mode 160000 unicorn_mode/unicornafl (limited to 'src/afl-cc.c') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl deleted file mode 160000 index 21ff3438..00000000 --- a/qemu_mode/qemuafl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-cc.c b/src/afl-cc.c index 758b958b..e6a6718e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC) { + if (compiler_mode != GCC && compiler_mode != CLANG) { switch (bit_mode) { diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl deleted file mode 160000 index 8cca4801..00000000 --- a/unicorn_mode/unicornafl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 80767480f0f40d380c83069b5f82b808c76493cc Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 18:13:36 +0100 Subject: Revert "fix afl-clang on 32-Bit systems" This reverts commit 83c1378fc1810d177d9cdfa2c00f62bbadd98f00. --- qemu_mode/qemuafl | 1 + src/afl-cc.c | 2 +- unicorn_mode/unicornafl | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) create mode 160000 qemu_mode/qemuafl create mode 160000 unicorn_mode/unicornafl (limited to 'src/afl-cc.c') diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl new file mode 160000 index 00000000..21ff3438 --- /dev/null +++ b/qemu_mode/qemuafl @@ -0,0 +1 @@ +Subproject commit 21ff34383764a8c6f66509b3b8d5282468c721e1 diff --git a/src/afl-cc.c b/src/afl-cc.c index e6a6718e..758b958b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC && compiler_mode != CLANG) { + if (compiler_mode != GCC) { switch (bit_mode) { diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl new file mode 160000 index 00000000..8cca4801 --- /dev/null +++ b/unicorn_mode/unicornafl @@ -0,0 +1 @@ +Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 107c79b84b7cd7d90e8f9e10b3a46fbafcae39eb Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 29 Dec 2020 18:17:50 +0100 Subject: now fix afl-clang compiles on 32-Bit properly --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 758b958b..e6a6718e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -879,7 +879,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { #ifndef __ANDROID__ - if (compiler_mode != GCC) { + if (compiler_mode != GCC && compiler_mode != CLANG) { switch (bit_mode) { -- cgit 1.4.1 From c6e038fe25789caa8da777f53154de1bd7b4e178 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 4 Jan 2021 20:40:53 +0100 Subject: code cleanups (shadowed vars, (un)signed type mismatches, format types, etc.) --- include/afl-fuzz.h | 4 +-- include/config.h | 46 +++++++++++++++--------------- src/afl-analyze.c | 4 +-- src/afl-cc.c | 74 ++++++++++++++++++++++++++++--------------------- src/afl-common.c | 20 ++++++------- src/afl-forkserver.c | 4 +-- src/afl-fuzz-bitmap.c | 2 +- src/afl-fuzz-extras.c | 6 ++-- src/afl-fuzz-mutators.c | 28 ++++++++++++------- src/afl-fuzz-one.c | 24 ++++++++-------- src/afl-fuzz-queue.c | 5 ++-- src/afl-fuzz-redqueen.c | 9 ++++-- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-stats.c | 8 +++--- src/afl-fuzz.c | 8 +++--- src/afl-ld-lto.c | 6 ++-- src/afl-showmap.c | 12 ++++---- src/afl-tmin.c | 4 +-- 18 files changed, 144 insertions(+), 122 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index d6a322cc..ede54f0e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -573,7 +573,7 @@ typedef struct afl_state { u8 stage_name_buf[STAGE_BUF_SIZE]; /* reused stagename buf with len 64 */ - s32 stage_cur, stage_max; /* Stage progression */ + u32 stage_cur, stage_max; /* Stage progression */ s32 splicing_with; /* Splicing with which test case? */ u32 main_node_id, main_node_max; /* Main instance job splitting */ @@ -648,7 +648,7 @@ typedef struct afl_state { double last_avg_execs_saved; /* foreign sync */ -#define FOREIGN_SYNCS_MAX 32 +#define FOREIGN_SYNCS_MAX 32U u8 foreign_sync_cnt; struct foreign_sync foreign_syncs[FOREIGN_SYNCS_MAX]; diff --git a/include/config.h b/include/config.h index e8a49270..7c75e9c9 100644 --- a/include/config.h +++ b/include/config.h @@ -80,11 +80,11 @@ /* Default timeout for fuzzed code (milliseconds). This is the upper bound, also used for detecting hangs; the actual value is auto-scaled: */ -#define EXEC_TIMEOUT 1000 +#define EXEC_TIMEOUT 1000U /* Timeout rounding factor when auto-scaling (milliseconds): */ -#define EXEC_TM_ROUND 20 +#define EXEC_TM_ROUND 20U /* 64bit arch MACRO */ #if (defined(__x86_64__) || defined(__arm64__) || defined(__aarch64__)) @@ -93,48 +93,48 @@ /* Default memory limit for child process (MB) 0 = disabled : */ -#define MEM_LIMIT 0 +#define MEM_LIMIT 0U /* Default memory limit when running in QEMU mode (MB) 0 = disabled : */ -#define MEM_LIMIT_QEMU 0 +#define MEM_LIMIT_QEMU 0U /* Default memory limit when running in Unicorn mode (MB) 0 = disabled : */ -#define MEM_LIMIT_UNICORN 0 +#define MEM_LIMIT_UNICORN 0U /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ -#define CAL_CYCLES 8 -#define CAL_CYCLES_LONG 40 +#define CAL_CYCLES 8U +#define CAL_CYCLES_LONG 40U /* Number of subsequent timeouts before abandoning an input file: */ -#define TMOUT_LIMIT 250 +#define TMOUT_LIMIT 250U /* Maximum number of unique hangs or crashes to record: */ -#define KEEP_UNIQUE_HANG 500 -#define KEEP_UNIQUE_CRASH 5000 +#define KEEP_UNIQUE_HANG 500U +#define KEEP_UNIQUE_CRASH 5000U /* Baseline number of random tweaks during a single 'havoc' stage: */ -#define HAVOC_CYCLES 256 -#define HAVOC_CYCLES_INIT 1024 +#define HAVOC_CYCLES 256U +#define HAVOC_CYCLES_INIT 1024U /* Maximum multiplier for the above (should be a power of two, beware of 32-bit int overflows): */ -#define HAVOC_MAX_MULT 64 -#define HAVOC_MAX_MULT_MOPT 64 +#define HAVOC_MAX_MULT 64U +#define HAVOC_MAX_MULT_MOPT 64U /* Absolute minimum number of havoc cycles (after all adjustments): */ -#define HAVOC_MIN 12 +#define HAVOC_MIN 12U /* Power Schedule Divisor */ -#define POWER_BETA 1 +#define POWER_BETA 1U #define MAX_FACTOR (POWER_BETA * 32) /* Maximum stacking for havoc-stage tweaks. The actual value is calculated @@ -146,19 +146,19 @@ In other words, the default (n = 4) produces 2, 4, 8, 16 stacked tweaks: */ -#define HAVOC_STACK_POW2 4 +#define HAVOC_STACK_POW2 4U /* Caps on block sizes for cloning and deletion operations. Each of these ranges has a 33% probability of getting picked, except for the first two cycles where smaller blocks are favored: */ -#define HAVOC_BLK_SMALL 32 -#define HAVOC_BLK_MEDIUM 128 -#define HAVOC_BLK_LARGE 1500 +#define HAVOC_BLK_SMALL 32U +#define HAVOC_BLK_MEDIUM 128U +#define HAVOC_BLK_LARGE 1500U /* Extra-large blocks, selected very rarely (<5% of the time): */ -#define HAVOC_BLK_XL 32768 +#define HAVOC_BLK_XL 32768U /* Probabilities of skipping non-favored entries in the queue, expressed as percentages: */ @@ -188,11 +188,11 @@ /* Maximum size of input file, in bytes (keep under 100MB): */ -#define MAX_FILE (1 * 1024 * 1024) +#define MAX_FILE (1 * 1024 * 1024U) /* The same, for the test case minimizer: */ -#define TMIN_MAX_FILE (10 * 1024 * 1024) +#define TMIN_MAX_FILE (10 * 1024 * 1024U) /* Block normalization steps for afl-tmin: */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 6dac415b..8fc4434a 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -903,8 +903,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 " diff --git a/src/afl-cc.c b/src/afl-cc.c index e6a6718e..180ab3c4 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -120,8 +120,10 @@ char compiler_mode_string[7][12] = { u8 *getthecwd() { - static u8 fail[] = ""; - if (getcwd(cwd, sizeof(cwd)) == NULL) return fail; + if (getcwd(cwd, sizeof(cwd)) == NULL) { + static u8 fail[] = ""; + return fail; + } return cwd; } @@ -654,9 +656,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - u32 idx; if (lto_mode && argc > 1) { + u32 idx; for (idx = 1; idx < argc; idx++) { if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; @@ -1208,12 +1210,12 @@ int main(int argc, char **argv, char **envp) { if (getenv("AFL_LLVM_INSTRUMENT")) { - u8 *ptr = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); - while (ptr) { + while (ptr2) { - if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || - strncasecmp(ptr, "classic", strlen("classic")) == 0) { + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { if (instrument_mode == INSTRUMENT_LTO) { @@ -1229,8 +1231,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr, "pcguard", strlen("pcguard")) == 0) { + 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; @@ -1241,8 +1243,8 @@ int main(int argc, char **argv, char **envp) { } // this is a hidden option - if (strncasecmp(ptr, "llvmnative", strlen("llvmnative")) == 0 || - strncasecmp(ptr, "llvm-native", strlen("llvm-native")) == 0) { + 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; @@ -1252,8 +1254,8 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "cfg", strlen("cfg")) == 0 || - strncasecmp(ptr, "instrim", strlen("instrim")) == 0) { + if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { if (instrument_mode == INSTRUMENT_LTO) { @@ -1269,7 +1271,7 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "lto", strlen("lto")) == 0) { + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { lto_mode = 1; if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) @@ -1280,7 +1282,7 @@ int main(int argc, char **argv, char **envp) { } - if (strcasecmp(ptr, "gcc") == 0) { + if (strcasecmp(ptr2, "gcc") == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) instrument_mode = INSTRUMENT_GCC; @@ -1291,7 +1293,7 @@ int main(int argc, char **argv, char **envp) { } - if (strcasecmp(ptr, "clang") == 0) { + if (strcasecmp(ptr2, "clang") == 0) { if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) instrument_mode = INSTRUMENT_CLANG; @@ -1302,29 +1304,29 @@ int main(int argc, char **argv, char **envp) { } - if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { + if (strncasecmp(ptr2, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; setenv("AFL_LLVM_CTX", "1", 1); } - if (strncasecmp(ptr, "ngram", strlen("ngram")) == 0) { + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - ptr += strlen("ngram"); - while (*ptr && (*ptr < '0' || *ptr > '9')) - ptr++; + ptr2 += strlen("ngram"); + while (*ptr2 && (*ptr2 < '0' || *ptr2 > '9')) + ptr2++; - if (!*ptr) { + if (!*ptr2) { - if ((ptr = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + if ((ptr2 = 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(ptr); + ngram_size = atoi(ptr2); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( "NGRAM instrumentation option must be between 2 and " @@ -1332,12 +1334,12 @@ int main(int argc, char **argv, char **envp) { "(%u)", NGRAM_SIZE_MAX); instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - ptr = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr, 1); + ptr2 = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr2, 1); } - ptr = strtok(NULL, ":,;"); + ptr2 = strtok(NULL, ":,;"); } @@ -1448,20 +1450,28 @@ int main(int argc, char **argv, char **envp) { " 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" -#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - " NATIVE: use llvm's native PCGUARD instrumentation (less " - "performant)\n" -#endif + + NATIVE_MSG + " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + 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" @@ -1625,7 +1635,7 @@ int main(int argc, char **argv, char **envp) { if (!instrument_mode) { instrument_mode = INSTRUMENT_CFG; - ptr = instrument_mode_string[instrument_mode]; + //ptr = instrument_mode_string[instrument_mode]; } diff --git a/src/afl-common.c b/src/afl-common.c index 1928663d..21cb6ab4 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -696,16 +696,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; @@ -858,16 +858,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; @@ -895,8 +895,8 @@ u32 get_map_size(void) { map_size = atoi(ptr); if (map_size < 8 || 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, 8U, + 1U << 29); } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 90fa55e9..d6195cb5 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -213,7 +213,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:"); } @@ -1104,7 +1104,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" diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 62a8211c..85a01f98 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); - classified = 1; +// classified = 1; } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 171cce96..04f0878c 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -266,7 +266,7 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { - WARNF("More than %d tokens - will use them probabilistically.", + WARNF("More than %u tokens - will use them probabilistically.", afl->max_det_extras); } @@ -431,7 +431,6 @@ void dedup_extras(afl_state_t *afl) { /* Adds a new extra / dict entry. */ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { - u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; u32 i, found = 0; for (i = 0; i < afl->extras_cnt; i++) { @@ -451,6 +450,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { 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)); @@ -481,7 +481,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { - WARNF("More than %d tokens - will use them probabilistically.", + WARNF("More than %u tokens - will use them probabilistically.", afl->max_det_extras); } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 0c85458e..5da692d3 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -316,16 +316,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", afl->stage_max); + FATAL("custom_init_trim error ret: %d", retval); + } else { + + 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); } @@ -343,7 +347,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)) { @@ -409,7 +413,7 @@ 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); } @@ -417,16 +421,20 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, } else { /* 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 e6fa6064..f9509e86 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -368,7 +368,7 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) { u8 fuzz_one_original(afl_state_t *afl) { - s32 len, temp_len; + u32 len, temp_len; u32 j; u32 i; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -545,7 +545,7 @@ u8 fuzz_one_original(afl_state_t *afl) { else 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 (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { @@ -902,7 +902,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -945,7 +945,7 @@ u8 fuzz_one_original(afl_state_t *afl) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -1405,7 +1405,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -1493,7 +1493,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; i++) { + for (i = 0; i < len - 3; i++) { u32 orig = *(u32 *)(out_buf + i); @@ -1850,7 +1850,7 @@ custom_mutator_stage: if (unlikely(!mutated_buf)) { - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + FATAL("Error in custom_fuzz. Size returned: %zu", mutated_size); } @@ -2026,7 +2026,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); } @@ -2458,7 +2458,7 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - if ((s32)extra_len > temp_len) { break; } + if (extra_len > temp_len) { break; } u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION @@ -2476,7 +2476,7 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - if ((s32)extra_len > temp_len) { break; } + if (extra_len > temp_len) { break; } u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION @@ -2577,7 +2577,7 @@ havoc_stage: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if ((s32)copy_len > temp_len) copy_len = temp_len; + 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); @@ -2952,7 +2952,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else 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 (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 9a0d199e..cd41bafc 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -489,11 +489,12 @@ 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; u32 i; for (i = 0; i < afl->queued_paths; i++) { + struct queue_entry *q; + q = afl->queue_buf[i]; ck_free(q->fname); ck_free(q->trace_mini); @@ -996,7 +997,7 @@ inline void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, if (unlikely(!q->testcase_buf)) { - PFATAL("Unable to malloc '%s' with len %d", q->fname, len); + PFATAL("Unable to malloc '%s' with len %u", q->fname, len); } diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index c5db8fa1..7dba1caa 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -445,6 +445,9 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; + + if (shape > sizeof(v)) FATAL("shape is greater than %zu, please report!", sizeof(v)); + for (k = 0; k < shape; ++k) { if (b[k] == 0) { @@ -453,7 +456,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 { @@ -667,12 +670,12 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { u8 status = 0; // opt not in the paper - u32 fails = 0; +// u32 fails = 0; u8 found_one = 0; for (i = 0; i < loggeds; ++i) { - fails = 0; + u32 fails = 0; struct cmpfn_operands *o = &((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[i]; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 339fb9c3..11d8204b 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -682,7 +682,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); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 1c211da6..c8366174 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -31,7 +31,6 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { - char *val; u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); @@ -44,6 +43,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { 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); @@ -228,7 +228,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, if (afl->virgin_bits[i] != 0xff) { - fprintf(f, " %d[%02x]", i, afl->virgin_bits[i]); + fprintf(f, " %u[%02x]", i, afl->virgin_bits[i]); } @@ -238,7 +238,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); } } @@ -1163,7 +1163,7 @@ void show_init_stats(afl_state_t *afl) { } else { - ACTF("-t option specified. We'll use an exec timeout of %d ms.", + ACTF("-t option specified. We'll use an exec timeout of %u ms.", afl->fsrv.exec_tmout); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 897c2f1e..e239b47f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -99,8 +99,8 @@ static void usage(u8 *argv0, int more_help) { " lin, 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, 0 = no limit)\n" + " -t msec - timeout for each run (auto-scaled, 50-%u ms)\n" + " -m megs - memory limit for child process (%u MB, 0 = no limit)\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 " @@ -299,7 +299,7 @@ int main(int argc, char **argv_orig, char **envp) { 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 = MAP_SIZE; + 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*/; @@ -326,7 +326,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } - map_size = get_map_size(); +// map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index fccdb1a5..1d54fda0 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -187,7 +187,7 @@ static void edit_params(int argc, char **argv) { if (debug) DEBUGF( - "passthrough=%s instrim=%d, gold_pos=%d, gold_present=%s " + "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", @@ -253,10 +253,10 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { s32 pid, i, status; - u8 * ptr; +// u8 * ptr; char thecwd[PATH_MAX]; - if ((ptr = getenv("AFL_LD_CALLER")) != NULL) { + if (getenv("AFL_LD_CALLER") != NULL) { FATAL("ld loop detected! Set AFL_REAL_LD!\n"); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 355b2dc3..c0223a07 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -662,7 +662,7 @@ 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" @@ -1014,7 +1014,7 @@ int main(int argc, char **argv_orig, char **envp) { DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; - int done = 0; +// int done = 0; u8 infile[PATH_MAX], outfile[PATH_MAX]; u8 wait_for_gdb = 0; #if !defined(DT_REG) @@ -1090,11 +1090,11 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { - int i = optind; + int j = optind; DEBUGF("%s:", fsrv->target_path); - while (argv[i] != NULL) { + while (argv[j] != NULL) { - SAYF(" \"%s\"", argv[i++]); + SAYF(" \"%s\"", argv[j++]); } @@ -1143,7 +1143,7 @@ int main(int argc, char **argv_orig, char **envp) { 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] == '.') { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index ed928c7c..09d97f58 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -835,8 +835,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 " -- cgit 1.4.1 From 6c095b3937565e0e3c645cab706269e8c764701e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 4 Jan 2021 23:13:56 +0100 Subject: code format --- instrumentation/afl-compiler-rt.o.c | 19 ++++++++++++++----- instrumentation/afl-llvm-lto-instrumentation.so.cc | 6 +++--- instrumentation/cmplog-instructions-pass.cc | 14 +++++++------- instrumentation/compare-transform-pass.so.cc | 21 ++++++++++++--------- src/afl-cc.c | 15 +++++++++------ src/afl-fuzz-bitmap.c | 2 +- src/afl-fuzz-extras.c | 2 +- src/afl-fuzz-mutators.c | 2 +- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-redqueen.c | 7 ++++--- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-stats.c | 2 +- src/afl-fuzz.c | 5 +++-- src/afl-ld-lto.c | 4 ++-- src/afl-showmap.c | 6 +++--- 15 files changed, 63 insertions(+), 46 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 0b6c6e47..fdfc8d58 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -691,17 +691,26 @@ static void __afl_start_forkserver(void) { void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) { + status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (__afl_dictionary_len && __afl_dictionary) status_for_fsrv |= FS_OPT_AUTODICT; - if (__afl_sharedmem_fuzzing != 0) status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; - if (status_for_fsrv) status_for_fsrv |= (FS_OPT_ENABLED); + + } + + if (__afl_dictionary_len && __afl_dictionary) { + + status_for_fsrv |= FS_OPT_AUTODICT; + + } + + if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } + if (status_for_fsrv) { status_for_fsrv |= (FS_OPT_ENABLED); } memcpy(tmp, &status_for_fsrv, 4); /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 89d49936..9cacacf9 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -100,9 +100,9 @@ class AFLLTOPass : public ModulePass { bool AFLLTOPass::runOnModule(Module &M) { - LLVMContext & C = M.getContext(); - std::vector dictionary; -// std::vector calls; + LLVMContext & C = M.getContext(); + std::vector dictionary; + // std::vector calls; DenseMap valueMap; std::vector BlockList; char * ptr; diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 154bec2b..3499ccf0 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -234,9 +234,9 @@ bool CmpLogInstructions::hookInstrs(Module &M) { auto ty0 = op0->getType(); if (ty0->isHalfTy() #if LLVM_VERSION_MAJOR >= 11 - || ty0->isBFloatTy() + || ty0->isBFloatTy() #endif - ) + ) max_size = 16; else if (ty0->isFloatTy()) max_size = 32; @@ -253,15 +253,15 @@ bool CmpLogInstructions::hookInstrs(Module &M) { if (intTyOp0 && intTyOp1) { max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); + ? intTyOp0->getBitWidth() + : intTyOp1->getBitWidth(); args.push_back(V0); args.push_back(V1); - + } else { - + max_size = 0; - + } } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 887970a0..da5cf7e9 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -68,7 +68,7 @@ class CompareTransform : public ModulePass { const char *getPassName() const override { #else - StringRef getPassName() const override { + StringRef getPassName() const override { #endif return "transforms compare functions"; @@ -106,23 +106,26 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, FunctionCallee tolowerFn; #endif { + #if LLVM_VERSION_MAJOR < 9 - Constant * + Constant * #else - FunctionCallee + FunctionCallee #endif - c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty + c = M.getOrInsertFunction("tolower", Int32Ty, Int32Ty #if LLVM_VERSION_MAJOR < 5 - , - NULL + , + NULL #endif - ); + ); #if LLVM_VERSION_MAJOR < 9 - tolowerFn = cast(c); + tolowerFn = cast(c); #else - tolowerFn = c; + tolowerFn = c; #endif + } + /* iterate over all functions, bbs and instruction and add suitable calls to * strcmp/memcmp/strncmp/strcasecmp/strncasecmp */ for (auto &F : M) { diff --git a/src/afl-cc.c b/src/afl-cc.c index 180ab3c4..db2dcd14 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -121,9 +121,12 @@ char compiler_mode_string[7][12] = { u8 *getthecwd() { if (getcwd(cwd, sizeof(cwd)) == NULL) { + static u8 fail[] = ""; return fail; + } + return cwd; } @@ -1451,11 +1454,11 @@ int main(int argc, char **argv, char **envp) { "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" + #define NATIVE_MSG \ + " NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" #else -#define NATIVE_MSG "" + #define NATIVE_MSG "" #endif SAYF( @@ -1463,7 +1466,7 @@ int main(int argc, char **argv, char **envp) { "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" - NATIVE_MSG + NATIVE_MSG " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" @@ -1635,7 +1638,7 @@ int main(int argc, char **argv, char **envp) { if (!instrument_mode) { instrument_mode = INSTRUMENT_CFG; - //ptr = instrument_mode_string[instrument_mode]; + // ptr = instrument_mode_string[instrument_mode]; } diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 85a01f98..ed8c2510 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); -// classified = 1; + // classified = 1; } diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 04f0878c..a3583651 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -450,7 +450,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; + 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)); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 5da692d3..089707b9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -324,7 +324,7 @@ u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, } else { afl->stage_max = retval; - + } if (afl->not_on_tty && afl->debug) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index cd41bafc..7b8c039b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -489,7 +489,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - u32 i; + u32 i; for (i = 0; i < afl->queued_paths; i++) { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 7dba1caa..28585afe 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -446,7 +446,8 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; - if (shape > sizeof(v)) FATAL("shape is greater than %zu, please report!", sizeof(v)); + if (shape > sizeof(v)) + FATAL("shape is greater than %zu, please report!", sizeof(v)); for (k = 0; k < shape; ++k) { @@ -670,8 +671,8 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { u8 status = 0; // opt not in the paper -// u32 fails = 0; - u8 found_one = 0; + // u32 fails = 0; + u8 found_one = 0; for (i = 0; i < loggeds; ++i) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 11d8204b..41557707 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -682,7 +682,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 path2[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); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index c8366174..e86f2aeb 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -31,7 +31,7 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { - u8 fn[PATH_MAX]; + u8 fn[PATH_MAX]; snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); FILE *f = create_ffile(fn); u32 i; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e239b47f..063134fb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -299,7 +299,8 @@ int main(int argc, char **argv_orig, char **envp) { 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(); + 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*/; @@ -326,7 +327,7 @@ int main(int argc, char **argv_orig, char **envp) { if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; } -// map_size = get_map_size(); + // map_size = get_map_size(); afl_state_init(afl, map_size); afl->debug = debug; afl_fsrv_init(&afl->fsrv); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 1d54fda0..7a4d9132 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -252,8 +252,8 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { - s32 pid, i, status; -// u8 * ptr; + s32 pid, i, status; + // u8 * ptr; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { diff --git a/src/afl-showmap.c b/src/afl-showmap.c index c0223a07..d50601fc 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1014,9 +1014,9 @@ int main(int argc, char **argv_orig, char **envp) { 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 -- cgit 1.4.1 From 6b54310452a1b743a90ad45fcc511f59dd7821ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 5 Jan 2021 12:30:26 +0100 Subject: selective instrumentation documented --- docs/Changelog.md | 5 ++- include/config.h | 4 ++- instrumentation/README.instrument_list.md | 53 ++++++++++++++++++++----------- src/afl-cc.c | 14 ++++++++ 4 files changed, 55 insertions(+), 21 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 88dc74d2..03b8e036 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,8 +9,11 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.01a (release) +### Version ++3.01a (dev) - Mac OS ARM64 support + - New selective instrumentation option with __AFL_COVERAGE_... commands + to be placed in the source code. + Check out instrumentation/README.instrument_list.md - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) diff --git a/include/config.h b/include/config.h index ba62d4c6..c0cd0ef1 100644 --- a/include/config.h +++ b/include/config.h @@ -186,7 +186,9 @@ #define TRIM_START_STEPS 16 #define TRIM_END_STEPS 1024 -/* Maximum size of input file, in bytes (keep under 100MB): */ +/* Maximum size of input file, in bytes (keep under 100MB, default 1MB): + (note that if this value is changed, several areas in afl-cc.c, afl-fuzz.c + and afl-fuzz-state.c have to be changed as well! */ #define MAX_FILE (1 * 1024 * 1024U) diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 122be2b6..83197954 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -1,8 +1,9 @@ # Using afl++ with partial instrumentation - This file describes how to selectively instrument only source files - or functions that are of interest to you using the LLVM and GCC_PLUGIN - instrumentation provided by afl++. + This file describes two different mechanisms to selectively instrument + only specific parts in the target. + + Both mechanisms work for LLVM and GCC_PLUGIN, but not for afl-clang/afl-gcc. ## 1) Description and purpose @@ -12,28 +13,42 @@ the program, leaving the rest uninstrumented. This helps to focus the fuzzer on the important parts of the program, avoiding undesired noise and disturbance by uninteresting code being exercised. -For this purpose, a "partial instrumentation" support en par with llvm sancov -is provided by afl++ that allows to specify on a source file and function -level which function should be compiled with or without instrumentation. +For this purpose, "partial instrumentation" support is provided by afl++ that +allows to specify what should be instrumented and what not. + +Both mechanisms can be used together. + +## 2) Selective instrumentation with __AFL_COVERAGE_... directives + +In this mechanism the selective instrumentation is done in the source code. -Note: When using PCGUARD mode - and llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation +After the includes a special define has to be made, eg.: + +``` +#include +#include +// ... + +__AFL_COVERAGE(); // <- required for this feature to work +``` -The llvm sancov list format is fully supported by afl++, however afl++ has -more flexibility. +If you want to disable the coverage at startup until you specify coverage +should be started, then add `__AFL_COVERAGE_START_OFF();` at that position. -## 2a) Building the LLVM module +From here on out you have the following macros available that you can use +in any function where you want: -The new code is part of the existing afl++ LLVM module in the instrumentation/ -subdirectory. There is nothing specifically to do for the build :) + * `__AFL_COVERAGE_ON();` - enable coverage from this point onwards + * `__AFL_COVERAGE_OFF();` - disable coverage from this point onwards + * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point + * `__AFL_COVERAGE_ABORT();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. -## 2b) Building the GCC module +## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -The new code is part of the existing afl++ GCC_PLUGIN module in the -instrumentation/ subdirectory. There is nothing specifically to do for -the build :) +This feature is equivalent to llvm 12 sancov feature and allows to specify +on a filename and/or function name level to instrument these or skip them. -## 3) How to use the partial instrumentation mode +### 3a) How to use the partial instrumentation mode In order to build with partial instrumentation, you need to build with afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. @@ -90,7 +105,7 @@ fun: MallocFoo ``` Note that whitespace is ignored and comments (`# foo`) are supported. -## 4) UNIX-style pattern matching +### 3b) UNIX-style pattern matching You can add UNIX-style pattern matching in the "instrument file list" entries. See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/src/afl-cc.c b/src/afl-cc.c index db2dcd14..964df57f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -827,6 +827,20 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_abort();" + "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_ABORT()=__afl_coverage_abort()"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; -- cgit 1.4.1 From faefad564be4b6bd8b311cb4990e8fd33acb5c1c Mon Sep 17 00:00:00 2001 From: hexcoder Date: Tue, 5 Jan 2021 16:49:04 +0000 Subject: Haiku afl-system-config disable debugger, afl-cc.c avoid -lrt --- afl-system-config | 9 +++++++++ src/afl-cc.c | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src/afl-cc.c') diff --git a/afl-system-config b/afl-system-config index 919932c3..456cccac 100755 --- a/afl-system-config +++ b/afl-system-config @@ -84,5 +84,14 @@ if [ "$PLATFORM" = "Darwin" ] ; then fi DONE=1 fi +if [ "$PLATFORM" = "Haiku" ] ; then + SETTINGS=~/config/settings/system/debug_server/settings + [ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \ + echo We change the debug_server default_action from user to silenty kill; \ + [ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \ + echo Settings applied.; \ + } + DONE=1 +fi test -z "$DONE" && echo Error: Unknown platform: $PLATFORM exit 0 diff --git a/src/afl-cc.c b/src/afl-cc.c index 964df57f..27bf8cf0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -792,8 +792,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#if defined(USEMMAP) && !defined(__HAIKU__) +#if defined(USEMMAP) +#if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; +#endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -950,9 +952,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif - #ifdef USEMMAP + #if defined(USEMMAP) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; #endif + #endif } @@ -1622,12 +1626,17 @@ int main(int argc, char **argv, char **envp) { 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 with the the binary path \"%s\".\n", + SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); #endif -#ifdef USEMMAP +#if defined(USEMMAP) +#if !defined(__HAIKU__) + cc_params[cc_par_cnt++] = "-lrt"; SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); +#else + SAYF("Compiled with shm_open support.\n"); +#endif #else SAYF("Compiled with shmat support.\n"); #endif -- cgit 1.4.1 From 4c78bb70802a85ffad6aee0f234f26901cd563f9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 5 Jan 2021 22:13:55 +0100 Subject: cpp fix --- instrumentation/afl-compiler-rt.o.c | 4 ++-- src/afl-cc.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index daa50ffa..bb62fb38 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1409,8 +1409,8 @@ void __afl_coverage_on() { // discard all coverage up to this point void __afl_coverage_discard() { - memset(__afl_area_ptr, 0, __afl_map_size); - __afl_area_ptr[0] = 1; + memset(__afl_area_ptr_backup, 0, __afl_map_size); + __afl_area_ptr_backup[0] = 1; if (__afl_cmp_map) { memset(__afl_cmp_map, 0, sizeof(struct cmp_map)); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 27bf8cf0..e61dc6d2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -829,12 +829,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; - cc_params[cc_par_cnt++] = - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "void __afl_coverage_discard();" - "void __afl_coverage_abort();" - "void __afl_coverage_on();" - "void __afl_coverage_off();"; + if (plusplus_mode) { + + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_abort();" + "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_abort();" + "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;"; -- cgit 1.4.1 From 52c221fc484317d2cd3926ae31ac891bad8cc93a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 6 Jan 2021 09:35:47 +0100 Subject: selective coverage fix --- instrumentation/afl-compiler-rt.o.c | 10 +++------- src/afl-cc.c | 30 ++++++++++++++++-------------- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index bb62fb38..add303d7 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1435,19 +1435,15 @@ void __afl_coverage_abort() { } // mark this area as especially interesting -void __afl_coverage_interesting(u32 id, u8 val) { +void __afl_coverage_interesting(u8 val, u32 id) { - if (val) { + if (id) { __afl_area_ptr[id] = val; } else { - do { - - __afl_area_ptr[id] = (u8)rand(); - - } while (!__afl_area_ptr[id]); + __afl_area_ptr[(rand() % __afl_map_size)] = val; } diff --git a/src/afl-cc.c b/src/afl-cc.c index e61dc6d2..999ee7f1 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -793,9 +793,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) -#if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; -#endif + #endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -829,13 +829,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + if (plusplus_mode) { - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "extern \"C\" void __afl_coverage_discard();" - "extern \"C\" void __afl_coverage_abort();" - "extern \"C\" void __afl_coverage_on();" - "extern \"C\" void __afl_coverage_off();"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_abort();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"; } else { @@ -966,9 +968,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif #if defined(USEMMAP) - #if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif + #endif #endif } @@ -1639,17 +1641,17 @@ int main(int argc, char **argv, char **envp) { 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 binary path \"%s\".\n", - LLVM_MAJOR, LLVM_BINDIR); + SAYF("afl-cc LLVM version %d using binary path \"%s\".\n", LLVM_MAJOR, + LLVM_BINDIR); #endif #if defined(USEMMAP) -#if !defined(__HAIKU__) + #if !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); -#else + #else SAYF("Compiled with shm_open support.\n"); -#endif + #endif #else SAYF("Compiled with shmat support.\n"); #endif -- cgit 1.4.1 From 8eb00a5dfab01330f576d173038a9561951371ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 15 Jan 2021 14:55:58 +0100 Subject: remove warning --- src/afl-cc.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 999ee7f1..8fb42718 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1785,15 +1785,6 @@ int main(int argc, char **argv, char **envp) { } - if (!be_quiet && !lto_mode && - ((ptr2 = getenv("AFL_MAP_SIZE")) || (ptr2 = getenv("AFL_MAPSIZE")))) { - - u32 map_size = atoi(ptr2); - if (map_size != MAP_SIZE) - WARNF("AFL_MAP_SIZE is not supported by afl-clang-fast"); - - } - if (debug) { DEBUGF("cd '%s';", getthecwd()); -- cgit 1.4.1 From a0e884cf8bffe1a0394d106375f6a23edd2b60e6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 15 Jan 2021 16:56:40 +0100 Subject: merge cmplog --- include/afl-fuzz.h | 21 +- include/cmplog.h | 17 +- include/debug.h | 24 +- include/types.h | 43 +- instrumentation/afl-compiler-rt.o.c | 89 +- instrumentation/cmplog-instructions-pass.cc | 586 +++++++++-- src/afl-cc.c | 49 +- src/afl-fuzz-init.c | 38 + src/afl-fuzz-one.c | 81 +- src/afl-fuzz-queue.c | 1 + src/afl-fuzz-redqueen.c | 1437 +++++++++++++++++++++++---- src/afl-fuzz-state.c | 1 + src/afl-fuzz.c | 60 +- 13 files changed, 2055 insertions(+), 392 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 988a907d..8a2122dc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -145,12 +145,22 @@ extern s16 interesting_16[INTERESTING_8_LEN + INTERESTING_16_LEN]; extern s32 interesting_32[INTERESTING_8_LEN + INTERESTING_16_LEN + INTERESTING_32_LEN]; +struct tainted { + + u32 pos; + u32 len; + struct tainted *next; + struct tainted *prev; + +}; + struct queue_entry { u8 *fname; /* File name for the test case */ u32 len; /* Input length */ - u8 cal_failed; /* Calibration failed? */ + u8 colorized, /* Do not run redqueen stage again */ + cal_failed; /* Calibration failed? */ bool trim_done, /* Trimmed? */ was_fuzzed, /* historical, but needed for MOpt */ passed_det, /* Deterministic stages passed? */ @@ -158,7 +168,6 @@ struct queue_entry { var_behavior, /* Variable behavior? */ favored, /* Currently favored? */ fs_redundant, /* Marked as redundant in the fs? */ - fully_colorized, /* Do not run redqueen stage again */ is_ascii, /* Is the input just ascii text? */ disabled; /* Is disabled from fuzz selection */ @@ -183,7 +192,11 @@ struct queue_entry { u8 *testcase_buf; /* The testcase buffer, if loaded. */ - struct queue_entry *next; /* Next element, if any */ + u8 * cmplog_colorinput; /* the result buf of colorization */ + struct tainted *taint; /* Taint information from CmpLog */ + + struct queue_entry *mother, /* queue entry this based on */ + *next; /* Next element, if any */ }; @@ -636,6 +649,8 @@ typedef struct afl_state { /* cmplog forkserver ids */ s32 cmplog_fsrv_ctl_fd, cmplog_fsrv_st_fd; u32 cmplog_prev_timed_out; + u32 cmplog_max_filesize; + u32 cmplog_lvl; struct afl_pass_stat *pass_stats; struct cmp_map * orig_cmp_map; diff --git a/include/cmplog.h b/include/cmplog.h index bf557785..6392c503 100644 --- a/include/cmplog.h +++ b/include/cmplog.h @@ -30,8 +30,10 @@ #include "config.h" +#define CMPLOG_LVL_MAX 3 + #define CMP_MAP_W 65536 -#define CMP_MAP_H 256 +#define CMP_MAP_H 32 #define CMP_MAP_RTN_H (CMP_MAP_H / 4) #define SHAPE_BYTES(x) (x + 1) @@ -41,13 +43,12 @@ struct cmp_header { - unsigned hits : 20; - - unsigned cnt : 20; - unsigned id : 16; - - unsigned shape : 5; // from 0 to 31 + unsigned hits : 24; + unsigned id : 24; + unsigned shape : 5; unsigned type : 1; + unsigned attribute : 4; + unsigned reserved : 6; } __attribute__((packed)); @@ -55,6 +56,8 @@ struct cmp_operands { u64 v0; u64 v1; + u64 v0_128; + u64 v1_128; }; diff --git a/include/debug.h b/include/debug.h index ef5b195b..fc1f39cb 100644 --- a/include/debug.h +++ b/include/debug.h @@ -295,8 +295,8 @@ static inline const char *colorfilter(const char *x) { \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Location : " cRST "%s(), %s:%d\n\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, (u32)__LINE__); \ exit(1); \ \ } while (0) @@ -308,8 +308,8 @@ static inline const char *colorfilter(const char *x) { \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] PROGRAM ABORT : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%d\n\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ + __FILE__, (u32)__LINE__); \ abort(); \ \ } while (0) @@ -322,8 +322,8 @@ static inline const char *colorfilter(const char *x) { fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ "\n[-] SYSTEM ERROR : " cRST x); \ - SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%d\n", __func__, \ - __FILE__, __LINE__); \ + SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ + __FILE__, (u32)__LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ exit(1); \ \ @@ -344,12 +344,12 @@ static inline const char *colorfilter(const char *x) { /* Show a prefixed debug output. */ -#define DEBUGF(x...) \ - do { \ - \ - SAYF(cMGN "[D] " cBRI "DEBUG: " cRST x); \ - SAYF(cRST ""); \ - \ +#define DEBUGF(x...) \ + do { \ + \ + fprintf(stderr, cMGN "[D] " cBRI "DEBUG: " cRST x); \ + fprintf(stderr, cRST ""); \ + \ } while (0) /* Error-checking versions of read() and write() that call RPFATAL() as diff --git a/include/types.h b/include/types.h index 3e3bc953..d5c31597 100644 --- a/include/types.h +++ b/include/types.h @@ -26,9 +26,11 @@ #include #include -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef unsigned __int128 uint128_t; +typedef uint128_t u128; /* Extended forkserver option values */ @@ -57,10 +59,12 @@ typedef uint32_t u32; typedef unsigned long long u64; -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +typedef __int128 int128_t; +typedef int128_t s128; #ifndef MIN #define MIN(a, b) \ @@ -114,6 +118,31 @@ typedef int64_t s64; \ }) +// It is impossible to define 128 bit constants, so ... +#define SWAPN(_x, _l) \ + ({ \ + \ + u128 _res = (_x), _ret; \ + char *d = (char *)&_ret, *s = (char *)&_res; \ + int i; \ + for (i = 0; i < 16; i++) \ + d[15 - i] = s[i]; \ + u32 sr = 128U - ((_l) << 3U); \ + (_ret >>= sr); \ + (u128) _ret; \ + \ + }) + +#define SWAPNN(_x, _y, _l) \ + ({ \ + \ + char *d = (char *)(_x), *s = (char *)(_y); \ + u32 i, l = (_l)-1; \ + for (i = 0; i <= l; i++) \ + d[l - i] = s[i]; \ + \ + }) + #ifdef AFL_LLVM_PASS #if defined(__linux__) || !defined(__ANDROID__) #define AFL_SR(s) (srandom(s)) diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b735d8df..5d75af78 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -161,7 +161,7 @@ void send_forkserver_error(int error) { u32 status; if (!error || error > 0xffff) return; status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; } } @@ -544,11 +544,11 @@ static void __afl_start_snapshots(void) { if (__afl_dictionary_len && __afl_dictionary) status |= FS_OPT_AUTODICT; memcpy(tmp, &status, 4); - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } if (__afl_sharedmem_fuzzing || (__afl_dictionary_len && __afl_dictionary)) { - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); + if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); } if (getenv("AFL_DEBUG")) { @@ -1207,7 +1207,9 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { ///// CmpLog instrumentation -void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { +void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook1 arg0=%02x arg1=%02x attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1216,6 +1218,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1230,7 +1233,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { } -void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { +void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2, uint8_t attr) { if (unlikely(!__afl_cmp_map)) return; @@ -1239,6 +1242,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1251,7 +1255,9 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { } -void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { +void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook4 arg0=%x arg1=%x attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1260,6 +1266,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1272,7 +1279,9 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { } -void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { +void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2, uint8_t attr) { + + // fprintf(stderr, "hook8 arg0=%lx arg1=%lx attr=%u\n", arg1, arg2, attr); if (unlikely(!__afl_cmp_map)) return; @@ -1281,6 +1290,7 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { k &= CMP_MAP_W - 1; __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; u32 hits = __afl_cmp_map->headers[k].hits; __afl_cmp_map->headers[k].hits = hits + 1; @@ -1293,16 +1303,77 @@ void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { } +// support for u24 to u120 via llvm _ExitInt(). size is in bytes minus 1 +void __cmplog_ins_hookN(uint128_t arg1, uint128_t arg2, uint8_t attr, + uint8_t size) { + + // fprintf(stderr, "hookN arg0=%llx:%llx arg1=%llx:%llx bytes=%u attr=%u\n", + // (u64)(arg1 >> 64), (u64)arg1, (u64)(arg2 >> 64), (u64)arg2, size + 1, + // attr); + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = size; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = (u64)arg1; + __afl_cmp_map->log[k][hits].v1 = (u64)arg2; + + if (size > 7) { + + __afl_cmp_map->log[k][hits].v0_128 = (u64)(arg1 >> 64); + __afl_cmp_map->log[k][hits].v1_128 = (u64)(arg2 >> 64); + + } + +} + +void __cmplog_ins_hook16(uint128_t arg1, uint128_t arg2, uint8_t attr) { + + if (unlikely(!__afl_cmp_map)) return; + + uintptr_t k = (uintptr_t)__builtin_return_address(0); + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + __afl_cmp_map->headers[k].attribute = attr; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 15; + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = (u64)arg1; + __afl_cmp_map->log[k][hits].v1 = (u64)arg2; + __afl_cmp_map->log[k][hits].v0_128 = (u64)(arg1 >> 64); + __afl_cmp_map->log[k][hits].v1_128 = (u64)(arg2 >> 64); + +} + #if defined(__APPLE__) #pragma weak __sanitizer_cov_trace_const_cmp1 = __cmplog_ins_hook1 #pragma weak __sanitizer_cov_trace_const_cmp2 = __cmplog_ins_hook2 #pragma weak __sanitizer_cov_trace_const_cmp4 = __cmplog_ins_hook4 #pragma weak __sanitizer_cov_trace_const_cmp8 = __cmplog_ins_hook8 + #pragma weak __sanitizer_cov_trace_const_cmp16 = __cmplog_ins_hook16 #pragma weak __sanitizer_cov_trace_cmp1 = __cmplog_ins_hook1 #pragma weak __sanitizer_cov_trace_cmp2 = __cmplog_ins_hook2 #pragma weak __sanitizer_cov_trace_cmp4 = __cmplog_ins_hook4 #pragma weak __sanitizer_cov_trace_cmp8 = __cmplog_ins_hook8 + #pragma weak __sanitizer_cov_trace_cmp16 = __cmplog_ins_hook16 #else void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__cmplog_ins_hook1"))); @@ -1312,6 +1383,8 @@ void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); +void __sanitizer_cov_trace_const_cmp16(uint128_t arg1, uint128_t arg2) + __attribute__((alias("__cmplog_ins_hook16"))); void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__cmplog_ins_hook1"))); @@ -1321,6 +1394,8 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) __attribute__((alias("__cmplog_ins_hook4"))); void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) __attribute__((alias("__cmplog_ins_hook8"))); +void __sanitizer_cov_trace_cmp16(uint128_t arg1, uint128_t arg2) + __attribute__((alias("__cmplog_ins_hook16"))); #endif /* defined(__APPLE__) */ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 3499ccf0..a74fb6c8 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -85,9 +85,25 @@ class CmpLogInstructions : public ModulePass { char CmpLogInstructions::ID = 0; +template +Iterator Unique(Iterator first, Iterator last) { + + while (first != last) { + + Iterator next(first); + last = std::remove(++next, last, *first); + first = next; + + } + + return last; + +} + bool CmpLogInstructions::hookInstrs(Module &M) { std::vector icomps; + std::vector switches; LLVMContext & C = M.getContext(); Type * VoidTy = Type::getVoidTy(C); @@ -95,13 +111,15 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int16Ty = IntegerType::getInt16Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C); IntegerType *Int64Ty = IntegerType::getInt64Ty(C); + IntegerType *Int128Ty = IntegerType::getInt128Ty(C); #if LLVM_VERSION_MAJOR < 9 Constant * #else FunctionCallee #endif - c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty + c1 = M.getOrInsertFunction("__cmplog_ins_hook1", VoidTy, Int8Ty, Int8Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -118,7 +136,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty + c2 = M.getOrInsertFunction("__cmplog_ins_hook2", VoidTy, Int16Ty, Int16Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -135,7 +154,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty + c4 = M.getOrInsertFunction("__cmplog_ins_hook4", VoidTy, Int32Ty, Int32Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -152,7 +172,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { #else FunctionCallee #endif - c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty + c8 = M.getOrInsertFunction("__cmplog_ins_hook8", VoidTy, Int64Ty, Int64Ty, + Int8Ty #if LLVM_VERSION_MAJOR < 5 , NULL @@ -164,6 +185,42 @@ bool CmpLogInstructions::hookInstrs(Module &M) { FunctionCallee cmplogHookIns8 = c8; #endif +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c16 = M.getOrInsertFunction("__cmplog_ins_hook16", VoidTy, Int128Ty, + Int128Ty, Int8Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookIns16 = cast(c16); +#else + FunctionCallee cmplogHookIns16 = c16; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + cN = M.getOrInsertFunction("__cmplog_ins_hookN", VoidTy, Int128Ty, + Int128Ty, Int8Ty, Int8Ty +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogHookInsN = cast(cN); +#else + FunctionCallee cmplogHookInsN = cN; +#endif + /* iterate over all functions, bbs and instruction and add suitable calls */ for (auto &F : M) { @@ -174,35 +231,16 @@ bool CmpLogInstructions::hookInstrs(Module &M) { for (auto &IN : BB) { CmpInst *selectcmpInst = nullptr; - if ((selectcmpInst = dyn_cast(&IN))) { - if (selectcmpInst->getPredicate() == CmpInst::ICMP_EQ || - selectcmpInst->getPredicate() == CmpInst::ICMP_NE || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGT || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULT || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLT || - selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || - selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLE || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULE || - selectcmpInst->getPredicate() == CmpInst::FCMP_OGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_UGT || - selectcmpInst->getPredicate() == CmpInst::FCMP_OLT || - selectcmpInst->getPredicate() == CmpInst::FCMP_ULT || - selectcmpInst->getPredicate() == CmpInst::FCMP_UEQ || - selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ || - selectcmpInst->getPredicate() == CmpInst::FCMP_UNE || - selectcmpInst->getPredicate() == CmpInst::FCMP_ONE) { - - icomps.push_back(selectcmpInst); + icomps.push_back(selectcmpInst); - } + } + + SwitchInst *switchInst = nullptr; + if ((switchInst = dyn_cast(BB.getTerminator()))) { + + if (switchInst->getNumCases() > 1) { switches.push_back(switchInst); } } @@ -212,101 +250,473 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!icomps.size()) return false; - // if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp - // instructions\n"; + // unique the collected switches + switches.erase(Unique(switches.begin(), switches.end()), switches.end()); + + // Instrument switch values for cmplog + if (switches.size()) { + + if (!be_quiet) + errs() << "Hooking " << switches.size() << " switch instructions\n"; - for (auto &selectcmpInst : icomps) { + for (auto &SI : switches) { - IRBuilder<> IRB(selectcmpInst->getParent()); - IRB.SetInsertPoint(selectcmpInst); + Value * Val = SI->getCondition(); + unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; + unsigned char do_cast = 0; - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); + if (!SI->getNumCases() || max_size <= 8) { - IntegerType * intTyOp0 = NULL; - IntegerType * intTyOp1 = NULL; - unsigned max_size = 0; - std::vector args; + // if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; - if (selectcmpInst->getOpcode() == Instruction::FCmp) { + } + + IRBuilder<> IRB(SI->getParent()); + IRB.SetInsertPoint(SI); + + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + if (max_size > 128) { + + if (!be_quiet) { + + fprintf(stderr, + "Cannot handle this switch bit size: %u (truncating)\n", + max_size); + + } + + max_size = 128; + do_cast = 1; + + } + + // do we need to cast? + switch (max_size) { + + case 8: + case 16: + case 32: + case 64: + case 128: + cast_size = max_size; + break; + default: + cast_size = 128; + do_cast = 1; + + } + + Value *CompareTo = Val; + + if (do_cast) { + + ConstantInt *cint = dyn_cast(Val); + if (cint) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + switch (cast_size) { + + case 8: + CompareTo = ConstantInt::get(Int8Ty, val); + break; + case 16: + CompareTo = ConstantInt::get(Int16Ty, val); + break; + case 32: + CompareTo = ConstantInt::get(Int32Ty, val); + break; + case 64: + CompareTo = ConstantInt::get(Int64Ty, val); + break; + case 128: + CompareTo = ConstantInt::get(Int128Ty, val); + break; + + } - auto ty0 = op0->getType(); - if (ty0->isHalfTy() + } else { + + CompareTo = IRB.CreateBitCast(Val, IntegerType::get(C, cast_size)); + + } + + } + + for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; + ++i) { + +#if LLVM_VERSION_MAJOR < 5 + ConstantInt *cint = i.getCaseValue(); +#else + ConstantInt *cint = i->getCaseValue(); +#endif + + if (cint) { + + std::vector args; + args.push_back(CompareTo); + + Value *new_param = cint; + + if (do_cast) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + } + + if (new_param) { + + args.push_back(new_param); + ConstantInt *attribute = ConstantInt::get(Int8Ty, 1); + args.push_back(attribute); + if (cast_size != max_size) { + + ConstantInt *bitsize = + ConstantInt::get(Int8Ty, (max_size / 8) - 1); + args.push_back(bitsize); + + } + + switch (cast_size) { + + case 8: + IRB.CreateCall(cmplogHookIns1, args); + break; + case 16: + IRB.CreateCall(cmplogHookIns2, args); + break; + case 32: + IRB.CreateCall(cmplogHookIns4, args); + break; + case 64: + IRB.CreateCall(cmplogHookIns8, args); + break; + case 128: + if (max_size == 128) { + + IRB.CreateCall(cmplogHookIns16, args); + + } else { + + IRB.CreateCall(cmplogHookInsN, args); + + } + + break; + + } + + } + + } + + } + + } + + } + + if (icomps.size()) { + + // if (!be_quiet) errs() << "Hooking " << icomps.size() << + // " cmp instructions\n"; + + for (auto &selectcmpInst : icomps) { + + IRBuilder<> IRB(selectcmpInst->getParent()); + IRB.SetInsertPoint(selectcmpInst); + + Value *op0 = selectcmpInst->getOperand(0); + Value *op1 = selectcmpInst->getOperand(1); + + IntegerType * intTyOp0 = NULL; + IntegerType * intTyOp1 = NULL; + unsigned max_size = 0, cast_size = 0; + unsigned char attr = 0, do_cast = 0; + std::vector args; + + CmpInst *cmpInst = dyn_cast(selectcmpInst); + + if (!cmpInst) { continue; } + + switch (cmpInst->getPredicate()) { + + case CmpInst::ICMP_NE: + case CmpInst::FCMP_UNE: + case CmpInst::FCMP_ONE: + break; + case CmpInst::ICMP_EQ: + case CmpInst::FCMP_UEQ: + case CmpInst::FCMP_OEQ: + attr += 1; + break; + case CmpInst::ICMP_UGT: + case CmpInst::ICMP_SGT: + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + attr += 2; + break; + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_SGE: + case CmpInst::FCMP_OGE: + case CmpInst::FCMP_UGE: + attr += 3; + break; + case CmpInst::ICMP_ULT: + case CmpInst::ICMP_SLT: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + attr += 4; + break; + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SLE: + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_ULE: + attr += 5; + break; + default: + break; + + } + + if (selectcmpInst->getOpcode() == Instruction::FCmp) { + + auto ty0 = op0->getType(); + if (ty0->isHalfTy() #if LLVM_VERSION_MAJOR >= 11 - || ty0->isBFloatTy() + || ty0->isBFloatTy() #endif - ) - max_size = 16; - else if (ty0->isFloatTy()) - max_size = 32; - else if (ty0->isDoubleTy()) - max_size = 64; + ) + max_size = 16; + else if (ty0->isFloatTy()) + max_size = 32; + else if (ty0->isDoubleTy()) + max_size = 64; + else if (ty0->isX86_FP80Ty()) + max_size = 80; + else if (ty0->isFP128Ty() || ty0->isPPC_FP128Ty()) + max_size = 128; + + attr += 8; + do_cast = 1; - if (max_size) { + } else { - Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, max_size)); - intTyOp0 = dyn_cast(V0->getType()); - Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, max_size)); - intTyOp1 = dyn_cast(V1->getType()); + intTyOp0 = dyn_cast(op0->getType()); + intTyOp1 = dyn_cast(op1->getType()); if (intTyOp0 && intTyOp1) { max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() ? intTyOp0->getBitWidth() : intTyOp1->getBitWidth(); - args.push_back(V0); - args.push_back(V1); - } else { + } + + } + + if (!max_size) { continue; } + + // _ExtInt() with non-8th values + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + if (max_size > 128) { + + if (!be_quiet) { - max_size = 0; + fprintf(stderr, + "Cannot handle this compare bit size: %u (truncating)\n", + max_size); } + max_size = 128; + do_cast = 1; + + } + + // do we need to cast? + switch (max_size) { + + case 8: + case 16: + case 32: + case 64: + case 128: + cast_size = max_size; + break; + default: + cast_size = 128; + do_cast = 1; + } - } else { + if (do_cast) { + + // F*cking LLVM optimized out any kind of bitcasts of ConstantInt values + // creating illegal calls. WTF. So we have to work around this. + + ConstantInt *cint = dyn_cast(op0); + if (cint) { + + uint64_t val = cint->getZExtValue(); + // fprintf(stderr, "ConstantInt: %lu\n", val); + ConstantInt *new_param = NULL; + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + if (!new_param) { continue; } + args.push_back(new_param); + + } else { + + Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, cast_size)); + args.push_back(V0); + + } + + cint = dyn_cast(op1); + if (cint) { + + uint64_t val = cint->getZExtValue(); + ConstantInt *new_param = NULL; + switch (cast_size) { + + case 8: + new_param = ConstantInt::get(Int8Ty, val); + break; + case 16: + new_param = ConstantInt::get(Int16Ty, val); + break; + case 32: + new_param = ConstantInt::get(Int32Ty, val); + break; + case 64: + new_param = ConstantInt::get(Int64Ty, val); + break; + case 128: + new_param = ConstantInt::get(Int128Ty, val); + break; + + } + + if (!new_param) { continue; } + args.push_back(new_param); + + } else { + + Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, cast_size)); + args.push_back(V1); - intTyOp0 = dyn_cast(op0->getType()); - intTyOp1 = dyn_cast(op1->getType()); + } - if (intTyOp0 && intTyOp1) { + } else { - max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); args.push_back(op0); args.push_back(op1); } - } + ConstantInt *attribute = ConstantInt::get(Int8Ty, attr); + args.push_back(attribute); + + if (cast_size != max_size) { + + ConstantInt *bitsize = ConstantInt::get(Int8Ty, (max_size / 8) - 1); + args.push_back(bitsize); + + } + + // fprintf(stderr, "_ExtInt(%u) castTo %u with attr %u didcast %u\n", + // max_size, cast_size, attr, do_cast); + + switch (cast_size) { - if (max_size < 8 || max_size > 64 || !intTyOp0 || !intTyOp1) continue; - - switch (max_size) { - - case 8: - IRB.CreateCall(cmplogHookIns1, args); - break; - case 16: - IRB.CreateCall(cmplogHookIns2, args); - break; - case 32: - IRB.CreateCall(cmplogHookIns4, args); - break; - case 64: - IRB.CreateCall(cmplogHookIns8, args); - break; - default: - break; + case 8: + IRB.CreateCall(cmplogHookIns1, args); + break; + case 16: + IRB.CreateCall(cmplogHookIns2, args); + break; + case 32: + IRB.CreateCall(cmplogHookIns4, args); + break; + case 64: + IRB.CreateCall(cmplogHookIns8, args); + break; + case 128: + if (max_size == 128) { + + IRB.CreateCall(cmplogHookIns16, args); + + } else { + + IRB.CreateCall(cmplogHookInsN, args); + + } + + break; + + } } } - return true; + if (switches.size() || icomps.size()) + return true; + else + return false; } diff --git a/src/afl-cc.c b/src/afl-cc.c index 8fb42718..02c9c7c5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -528,10 +528,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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/split-switches-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 { @@ -541,18 +541,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("%s/cmplog-routines-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); + 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/cmplog-instructions-pass.so", obj_path); + alloc_printf("%s/split-switches-pass.so", obj_path); } @@ -792,10 +792,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#if defined(USEMMAP) - #if !defined(__HAIKU__) +#if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -858,6 +856,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; + cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; @@ -967,10 +966,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif - #if defined(USEMMAP) - #if !defined(__HAIKU__) + #if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; - #endif #endif } @@ -1278,7 +1275,6 @@ int main(int argc, char **argv, char **envp) { } - // this is a hidden option if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0) { @@ -1349,29 +1345,28 @@ int main(int argc, char **argv, char **envp) { if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - ptr2 += strlen("ngram"); - while (*ptr2 && (*ptr2 < '0' || *ptr2 > '9')) - ptr2++; + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; - if (!*ptr2) { + if (!*ptr3) { - if ((ptr2 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + 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(ptr2); + 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 (%u)", NGRAM_SIZE_MAX); instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - ptr2 = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr2, 1); + u8 *ptr4 = alloc_printf("%u", ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); } @@ -1507,6 +1502,7 @@ int main(int argc, char **argv, char **envp) { "((instrumentation/README.ngram.md)\n" " INSTRIM: Dominator tree (for LLVM <= 6.0) " "(instrumentation/README.instrim.md)\n\n"); + #undef NATIVE_MSG SAYF( @@ -1641,16 +1637,15 @@ int main(int argc, char **argv, char **envp) { 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 binary path \"%s\".\n", LLVM_MAJOR, + SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); #endif -#if defined(USEMMAP) +#ifdef USEMMAP #if !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; - SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); - #else 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"); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index dbffa4f9..cbff6d7e 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -729,6 +729,30 @@ void read_testcases(afl_state_t *afl, u8 *directory) { 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); @@ -756,6 +780,20 @@ void read_testcases(afl_state_t *afl, u8 *directory) { } + 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; diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index f9509e86..596bae22 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -165,7 +165,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); @@ -193,7 +193,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); @@ -290,7 +290,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; (s32)i < blen - 1; ++i) { + for (i = 0; (u8)i < blen - 1; ++i) { for (j = 0; j < sizeof(interesting_16) / 2; ++j) { @@ -545,14 +545,31 @@ u8 fuzz_one_original(afl_state_t *afl) { else 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 (unlikely(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 = 0xff; + + } 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 > 15000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum)) { + + goto abandon_entry; + + } + + } } @@ -2796,7 +2813,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { } - s32 len, temp_len; + u32 len, temp_len; u32 i; u32 j; u8 *in_buf, *out_buf, *orig_in, *ex_tmp, *eff_map = 0; @@ -2952,14 +2969,31 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else 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 (unlikely(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 = 0xff; + + } 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 > 15000) { + + if (input_to_state_stage(afl, in_buf, out_buf, len, + afl->queue_cur->exec_cksum)) { + + goto abandon_entry; + + } + + } } @@ -3315,7 +3349,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { /* Let's consult the effector map... */ @@ -3357,7 +3391,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { /* Let's consult the effector map... */ if (!eff_map[EFF_APOS(i)] && !eff_map[EFF_APOS(i + 1)] && @@ -3489,7 +3523,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3615,7 +3649,7 @@ skip_bitflip: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -3805,7 +3839,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 1; ++i) { + for (i = 0; i < len - 1; ++i) { u16 orig = *(u16 *)(out_buf + i); @@ -3891,7 +3925,7 @@ skip_arith: orig_hit_cnt = new_hit_cnt; - for (i = 0; (s32)i < len - 3; ++i) { + for (i = 0; i < len - 3; ++i) { u32 orig = *(u32 *)(out_buf + i); @@ -4120,7 +4154,7 @@ skip_user_extras: /* See the comment in the earlier code; extras are sorted by size. */ - if ((s32)(afl->a_extras[j].len) > (s32)(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))) { @@ -4837,7 +4871,7 @@ pacemaker_fuzzing: u32 copy_from, copy_to, copy_len; copy_len = choose_block_len(afl, new_len - 1); - if ((s32)copy_len > temp_len) copy_len = temp_len; + 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); @@ -5033,8 +5067,7 @@ pacemaker_fuzzing: the last differing byte. Bail out if the difference is just a single byte or so. */ - locate_diffs(in_buf, new_buf, MIN(len, (s32)target->len), &f_diff, - &l_diff); + locate_diffs(in_buf, new_buf, MIN(len, target->len), &f_diff, &l_diff); if (f_diff < 0 || l_diff < 2 || f_diff == l_diff) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 66938635..aec57a6e 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -433,6 +433,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->passed_det = passed_det; q->trace_mini = NULL; q->testcase_buf = NULL; + q->mother = afl->queue_cur; #ifdef INTROSPECTION q->bitsmap_size = afl->bitsmap_size; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 28585afe..955a9232 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -28,6 +28,8 @@ #include "afl-fuzz.h" #include "cmplog.h" +//#define _DEBUG + ///// Colorization struct range { @@ -35,6 +37,8 @@ struct range { u32 start; u32 end; struct range *next; + struct range *prev; + u8 ok; }; @@ -44,6 +48,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,45 +57,61 @@ 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; + rmax = r; - max_size = s; - prev_rmax = prev; - rmax = r; + } } - prev = r; r = r->next; } - if (rmax) { + return rmax; - if (prev_rmax) { +} - prev_rmax->next = rmax->next; +#ifdef _DEBUG +// static int logging = 0; +static void dump(char *txt, u8 *buf, u32 len) { - } else { + u32 i; + fprintf(stderr, "DUMP %s %llx ", txt, hash64(buf, len, 0)); + for (i = 0; i < len; i++) + fprintf(stderr, "%02x", buf[i]); + fprintf(stderr, "\n"); - *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) { - return rmax; + write(fd, buf, len); + close(fd); + + } } +#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; } @@ -99,107 +121,270 @@ static u8 get_exec_checksum(afl_state_t *afl, u8 *buf, u32 len, u64 *cksum) { } -static void xor_replace(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] ^= 0xff; + // 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': + case '\n': + // nothing ... + break; + */ + default: + c = (buf[i] ^ 0xff); + + } + + } 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, u64 exec_cksum, + 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); u64 orig_hit_cnt, new_hit_cnt; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; afl->stage_name = "colorization"; afl->stage_short = "colorization"; - afl->stage_max = 1000; + afl->stage_max = (len << 1); - struct range *rng = NULL; afl->stage_cur = 0; + 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); - if (s != 0) { + u64 cksum; + u64 start_us = get_cur_time_us(); + if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { - /* Range not empty */ + goto checksum_fail; - memcpy(backup, buf + rng->start, s); - xor_replace(buf + rng->start, s); + } + + u64 stop_us = get_cur_time_us(); + + /* 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))) { + + memcpy(buf + rng->start, backup + rng->start, s); - u64 cksum; - u64 start_us = get_cur_time_us(); - if (unlikely(get_exec_checksum(afl, buf, len, &cksum))) { + if (s > 1) { // to not add 0 size ranges - goto checksum_fail; + 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; } + + } else if (rng->next) { + + rng->prev->next = rng->next; + rng->next->prev = rng->prev; - /* 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) && - likely(!afl->fixed_seed))) { + } else { - 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); + 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 < 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; - ck_free(rng); - rng = NULL; + } - while (ranges) { + u32 i = 1; + u32 positions = 0; + while (i) { + restart: + i = 0; + struct range *r = NULL; + u32 pos = (u32)-1; rng = ranges; - ranges = rng->next; - ck_free(rng); - rng = NULL; - } + while (rng) { - return 0; + if (rng->ok == 1 && rng->start < pos) { -checksum_fail: - if (rng) { ck_free(rng); } - ck_free(backup); + 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; + + } + + } + + 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; + } + + } + + *taints = taint; + + /* temporary: clean ranges */ while (ranges) { rng = ranges; ranges = rng->next; ck_free(rng); - rng = NULL; } + new_hit_cnt = afl->queued_paths + afl->unique_crashes; + +#ifdef _DEBUG + /* + char fn[4096]; + snprintf(fn, sizeof(fn), "%s/introspection_color.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + */ + FILE *f = stderr; + fprintf(f, + "Colorization: fname=%s len=%u result=%u execs=%u found=%llu " + "taint=%u\n", + afl->queue_cur->fname, len, afl->queue_cur->colorized, afl->stage_cur, + new_hit_cnt - orig_hit_cnt, positions); +/* + fclose(f); + + } + +*/ +#endif + + 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: + ck_free(backup); + ck_free(changed); + return 1; } @@ -212,12 +397,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 { @@ -278,11 +470,33 @@ static int strntoull(const char *str, size_t sz, char **end, int base, } 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) { - - if (!buf) { FATAL("BUG: buf was NULL. Please report this.\n"); } + 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) { + + // (void)(changed_val); // TODO + // we can use the information in changed_val to see if there is a + // computable i2s transformation. + // if (pattern != o_pattern && repl != changed_val) { + + // u64 in_diff = pattern - o_pattern, out_diff = repl - changed_val; + // if (in_diff != out_diff) { + + // switch(in_diff) { + + // detect uppercase <-> lowercase, base64, hex encoding, etc.: + // repl = reverse_transform(TYPE, pattern); + // } + // } + // } + // not 100% but would have a chance to be detected + + // fprintf(stderr, + // "Encode: %llx->%llx into %llx(<-%llx) at pos=%u " + // "taint_len=%u shape=%u attr=%u\n", + // o_pattern, pattern, repl, changed_val, idx, taint_len, + // h->shape + 1, attr); u64 *buf_64 = (u64 *)&buf[idx]; u32 *buf_32 = (u32 *)&buf[idx]; @@ -293,76 +507,215 @@ 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) { + // reverse atoi()/strnu?toll() is expensive, so we only to it in lvl == 3 + if (lvl & 4) { - endptr = buf_8; - if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { + if (afl->queue_cur->is_ascii) { - if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) - use_unum = 1; + endptr = buf_8; + if (strntoll(buf_8, len - idx, (char **)&endptr, 0, &num)) { - } else + if (!strntoull(buf_8, len - idx, (char **)&endptr, 0, &unum)) + use_unum = 1; - use_num = 1; + } else - } + use_num = 1; - if (use_num && (u64)num == pattern) { + } - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%lld", num); +#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 - u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - memcpy(new_buf, buf, idx); + // num is likely not pattern as atoi("AAA") will be zero... + if (use_num && ((u64)num == pattern || !num)) { - snprintf(new_buf + idx, num_len, "%lld", num); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%lld", repl); + size_t old_len = endptr - buf_8; - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } - } else if (use_unum && unum == pattern) { + 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); - size_t old_len = endptr - buf_8; - size_t num_len = snprintf(NULL, 0, "%llu", unum); + if (new_buf[idx + num_len] >= '0' && new_buf[idx + num_len] <= '9') { + + new_buf[idx + num_len] = ' '; + + } - u8 *new_buf = afl_realloc((void **)&afl->out_scratch_buf, len + num_len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - memcpy(new_buf, buf, idx); + if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } - snprintf(new_buf + idx, num_len, "%llu", unum); - memcpy(new_buf + idx + num_len, buf_8 + old_len, len - idx - old_len); + } else if (use_unum && (unum == pattern || !unum)) { - if (unlikely(its_fuzz(afl, new_buf, len, status))) { return 1; } + u8 tmp_buf[32]; + size_t num_len = snprintf(tmp_buf, sizeof(tmp_buf), "%llu", 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; } + + } } - if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + // we only allow this for ascii2integer (above) + if (unlikely(pattern == o_pattern)) { return 0; } - if (its_len >= 8 && *buf_64 == pattern && *o_buf_64 == o_pattern) { + if ((lvl & 1) || ((lvl & 2) && (attr >= 8 && attr <= 15)) || attr >= 16) { - *buf_64 = repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_64 = pattern; + if (SHAPE_BYTES(h->shape) >= 8 && *status != 1) { + + // if (its_len >= 8 && (attr == 0 || attr >= 8)) + // fprintf(stderr, + // "TestU64: %u>=4 %x==%llx" + // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", + // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, + // 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 >= 16)) { + + u64 tmp_64 = *buf_64; + *buf_64 = repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_64, 8); } + *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 %x==%llx" + // " %x==%llx (idx=%u attr=%u) <= %llx<-%llx\n", + // its_len, *buf_32, pattern, *o_buf_32, o_pattern, idx, attr, + // repl, changed_val); - return 1; + if (its_len >= 4 && + ((*buf_32 == (u32)pattern && *o_buf_32 == (u32)o_pattern) || + attr >= 16)) { + + u32 tmp_32 = *buf_32; + *buf_32 = (u32)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_32, 4); } + *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 >= 16)) { + + u16 tmp_16 = *buf_16; + *buf_16 = (u16)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, buf_16, 2); } + *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 && (attr <= 1 || attr >= 8)) + // fprintf(stderr, + // "TestU8: %u>=1 %x==%x %x==%x (idx=%u attr=%u) <= %x<-%x\n", + // its_len, *buf_8, pattern, *o_buf_8, o_pattern, idx, attr, + // repl, changed_val); + + if (its_len >= 1 && + ((*buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) || + attr >= 16)) { + + u8 tmp_8 = *buf_8; + *buf_8 = (u8)repl; + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { cbuf[idx] = *buf_8; } + *buf_8 = tmp_8; } @@ -370,49 +723,205 @@ 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, 3 = Lesser, 4 = Float - if (its_len >= 4 && *buf_32 == (u32)pattern && - *o_buf_32 == (u32)o_pattern) { + if (lvl < 4) { return 0; } - *buf_32 = (u32)repl; - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_32 = pattern; + if (attr >= 8 && attr < 16) { // lesser/greater integer comparison + + u64 repl_new; + 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; } - // reverse encoding - if (do_reverse && *status != 1) { + 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; + + } + + 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; + + } + + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + + 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 (unlikely(cmp_extend_encoding(afl, h, SWAP32(pattern), SWAP32(repl), - SWAP32(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", 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))) { + + h->shape = 7; return 1; } + h->shape = 7; // recover shape + + } + + } else if (attr > 1 && attr < 8) { // lesser/greater integer comparison + + u64 repl_new; + + 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; + + } + + 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; + } } - if (SHAPE_BYTES(h->shape) >= 2 && *status != 1) { + return 0; - if (its_len >= 2 && *buf_16 == (u16)pattern && - *o_buf_16 == (u16)o_pattern) { +} - *buf_16 = (u16)repl; +static u8 cmp_extend_encoding128(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) { + + u128 *buf_128 = (u128 *)&buf[idx]; + u64 * buf0 = (u64 *)&buf[idx]; + u64 * buf1 = (u64 *)(buf + idx + 8); + u128 *o_buf_128 = (u128 *)&orig_buf[idx]; + u32 its_len = MIN(len - idx, taint_len); + u64 v10 = (u64)repl; + u64 v11 = (u64)(repl >> 64); + + // 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 >= 16) { + +#ifdef _DEBUG + fprintf(stderr, "TestU128: %u>=16 (idx=%u attr=%u) (%u)\n", its_len, idx, + attr, do_reverse); + u64 v00 = (u64)pattern; + u64 v01 = pattern >> 64; + u64 ov00 = (u64)o_pattern; + u64 ov01 = o_pattern >> 64; + u64 ov10 = (u64)changed_val; + u64 ov11 = changed_val >> 64; + u64 b00 = (u64)*buf_128; + u64 b01 = *buf_128 >> 64; + u64 ob00 = (u64)*o_buf_128; + u64 ob01 = *o_buf_128 >> 64; + fprintf(stderr, + "TestU128: %llx:%llx==%llx:%llx" + " %llx:%llx==%llx:%llx <= %llx:%llx<-%llx:%llx\n", + b01, b00, v01, v00, ob01, ob00, ov01, ov00, v11, v10, ov11, ov10); +#endif + + if (*buf_128 == pattern && *o_buf_128 == o_pattern) { + + u128 tmp_128 = *buf_128; + // *buf_128 = repl; <- this crashes +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + *buf0 = v10; + *buf1 = v11; +#else + *buf1 = v10; + *buf0 = v11; +#endif if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_16 = (u16)pattern; + if (*status == 1) { memcpy(cbuf + idx, buf_128, 16); } + *buf_128 = tmp_128; + +#ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); +#endif } // reverse encoding if (do_reverse && *status != 1) { - if (unlikely(cmp_extend_encoding(afl, h, SWAP16(pattern), SWAP16(repl), - SWAP16(o_pattern), idx, orig_buf, buf, - len, 0, status))) { + if (unlikely(cmp_extend_encoding128( + afl, h, SWAPN(pattern, 128), SWAPN(repl, 128), + SWAPN(o_pattern, 128), SWAPN(changed_val, 128), attr, idx, + taint_len, orig_buf, buf, cbuf, len, 0, lvl, status))) { return 1; @@ -422,14 +931,82 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - /* avoid CodeQL warning on unsigned overflow */ - if (/* SHAPE_BYTES(h->shape) >= 1 && */ *status != 1) { + return 0; - if (its_len >= 1 && *buf_8 == (u8)pattern && *o_buf_8 == (u8)o_pattern) { +} - *buf_8 = (u8)repl; +// uh a pointer read from (long double*) reads 12 bytes, not 10 ... +// so lets make this complicated. +static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, + u8 *pattern, u8 *repl, u8 *o_pattern, + u8 *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 *buf_ld = &buf[idx], *o_buf_ld = &orig_buf[idx], backup[10]; + u32 its_len = MIN(len - idx, taint_len); + + if (its_len >= 10) { + +#ifdef _DEBUG + fprintf(stderr, "TestUld: %u>=10 (len=%u idx=%u attr=%u) (%u)\n", its_len, + len, idx, attr, do_reverse); + fprintf(stderr, "TestUld: "); + u32 i; + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", pattern[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", buf_ld[i]); + fprintf(stderr, " "); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", o_pattern[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", o_buf_ld[i]); + fprintf(stderr, " <= "); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", repl[i]); + fprintf(stderr, "=="); + for (i = 0; i < 10; i++) + fprintf(stderr, "%02x", changed_val[i]); + fprintf(stderr, "\n"); +#endif + + if (!memcmp(pattern, buf_ld, 10) && !memcmp(o_pattern, o_buf_ld, 10)) { + + // 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 + + memcpy(backup, buf_ld, 10); + memcpy(buf_ld, repl, 10); if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } - *buf_8 = (u8)pattern; + if (*status == 1) { memcpy(cbuf + idx, repl, 10); } + memcpy(buf_ld, backup, 10); + +#ifdef _DEBUG + fprintf(stderr, "Status=%u\n", *status); +#endif + + } + + } + + // reverse encoding + if (do_reverse && *status != 1) { + + u8 sp[10], sr[10], osp[10], osr[10]; + SWAPNN(sp, pattern, 10); + SWAPNN(sr, repl, 10); + SWAPNN(osp, o_pattern, 10); + SWAPNN(osr, changed_val, 10); + + if (unlikely(cmp_extend_encoding_ld(afl, h, sp, sr, osp, osr, attr, idx, + taint_len, orig_buf, buf, cbuf, len, 0, + lvl, status))) { + + return 1; } @@ -445,10 +1022,6 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u32 k; u8 cons_ff = 0, cons_0 = 0; - - if (shape > sizeof(v)) - FATAL("shape is greater than %zu, please report!", sizeof(v)); - for (k = 0; k < shape; ++k) { if (b[k] == 0) { @@ -457,7 +1030,7 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } else if (b[k] == 0xff) { - ++cons_ff; + ++cons_0; } else { @@ -493,28 +1066,126 @@ static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { } -static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { +static void try_to_add_to_dict128(afl_state_t *afl, u128 v) { - struct cmp_header *h = &afl->shm.cmp_map->headers[key]; - u32 i, j, idx; + u8 *b = (u8 *)&v; - u32 loggeds = h->hits; + u32 k; + u8 cons_ff = 0, cons_0 = 0; + for (k = 0; k < 16; ++k) { + + if (b[k] == 0) { + + ++cons_0; + + } else if (b[k] == 0xff) { + + ++cons_0; + + } else { + + cons_0 = cons_ff = 0; + + } + + // too many uninteresting values? try adding 2 64-bit values + if (cons_0 > 6 || cons_ff > 6) { + + u64 v64 = (u64)v; + try_to_add_to_dict(afl, v64, 8); + v64 = (u64)(v >> 64); + try_to_add_to_dict(afl, v64, 8); + + return; + + } + + } + + maybe_add_auto(afl, (u8 *)&v, 16); + u128 rev = SWAPN(v, 128); + maybe_add_auto(afl, (u8 *)&rev, 16); + +} + +static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { + + u8 *b = (u8 *)&v; + + u32 k; + u8 cons_ff = 0, cons_0 = 0; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + for (k = 0; k < size; ++k) { + +#else + for (k = 16 - size; k < 16; ++k) { + +#endif + if (b[k] == 0) { + + ++cons_0; + + } else if (b[k] == 0xff) { + + ++cons_0; + + } else { + + cons_0 = cons_ff = 0; + + } + + } + + maybe_add_auto(afl, (u8 *)&v, size); + u128 rev = SWAPN(v, size); + maybe_add_auto(afl, (u8 *)&rev, size); + +} + +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; + u32 have_taint = 1, is_128 = 0, is_n = 0, is_ld = 0; + u32 loggeds = h->hits; if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } u8 status = 0; - // opt not in the paper - u32 fails; - u8 found_one = 0; + u8 found_one = 0; /* loop cmps are useless, detect and ignore them */ - 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; + u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; + long double ld0, ld1, o_ld0, o_ld1; + 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) { + switch (SHAPE_BYTES(h->shape)) { + + case 1: + case 2: + case 4: + case 8: + break; + case 16: + is_128 = 1; + break; + case 10: + if (h->attribute & 8) { is_ld = 1; } + // fall through + default: + is_n = 1; - fails = 0; + } + + // FCmp not in if level 1 only + if ((h->attribute & 8) && lvl < 2) return 0; + + for (i = 0; i < loggeds; ++i) { struct cmp_operands *o = &afl->shm.cmp_map->log[key][i]; @@ -551,55 +1222,242 @@ 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 + + if (taint) { + + t = taint; + + while (t->next) { + + t = t->next; + + } + + } else { + + have_taint = 0; + t = NULL; + + } + + if (unlikely(is_128 || 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); + + if (is_ld) { + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + memcpy((char *)&ld0, (char *)&s128_v0, sizeof(long double)); + memcpy((char *)&ld1, (char *)&s128_v1, sizeof(long double)); + memcpy((char *)&o_ld0, (char *)&orig_s128_v0, sizeof(long double)); + memcpy((char *)&o_ld1, (char *)&orig_s128_v1, sizeof(long double)); +#else + memcpy((char *)&ld0, (char *)(&s128_v0) + 6, sizeof(long double)); + memcpy((char *)&ld1, (char *)(&s128_v1) + 6, sizeof(long double)); + memcpy((char *)&o_ld0, (char *)(&orig_s128_v0) + 6, + sizeof(long double)); + memcpy((char *)&o_ld1, (char *)(&orig_s128_v1) + 6, + sizeof(long double)); +#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; + if (is_ld) { // long double special case + + if (ld0 != o_ld0 && o_ld1 != o_ld0) { + + if (unlikely(cmp_extend_encoding_ld( + afl, h, (u8 *)&ld0, (u8 *)&ld1, (u8 *)&o_ld0, (u8 *)&o_ld1, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + if (ld1 != o_ld1 && o_ld0 != o_ld1) { + + if (unlikely(cmp_extend_encoding_ld( + afl, h, (u8 *)&ld1, (u8 *)&ld0, (u8 *)&o_ld1, (u8 *)&o_ld0, + h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, + lvl, &status))) { + + return 1; + + } + + } + + if (status == 1) { + + found_one = 1; + break; + + } + + } + + if (is_128) { // u128 special case + + if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { + + if (unlikely(cmp_extend_encoding128( + 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_encoding128( + afl, h, s128_v1, s128_v0, orig_s128_v1, orig_s128_v0, + 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) { + // even for u128 and long double 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. - ++fails; + if ((o->v0 != orig_o->v0 || lvl >= 4) && orig_o->v0 != orig_o->v1) { - } else if (status == 1) { + 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))) { + 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 >= 4) && 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, 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 is128=%u " + "isN=%u size=%u\n", + orig_o->v0, o->v0, orig_o->v1, o->v1, h->attribute, i, found_one, + is_128, 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)); + if (unlikely(is_128)) { + + try_to_add_to_dict128(afl, s128_v0); + try_to_add_to_dict128(afl, s128_v1); + + } else 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 { + + try_to_add_to_dict(afl, o->v0, SHAPE_BYTES(h->shape)); + try_to_add_to_dict(afl, o->v1, SHAPE_BYTES(h->shape)); + + } } @@ -630,20 +1488,19 @@ 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, u8 *pattern, u8 *repl, - u8 *o_pattern, u32 idx, u8 *orig_buf, u8 *buf, - u32 len, u8 *status) { + u8 *o_pattern, u32 idx, u32 taint_len, + u8 *orig_buf, u8 *buf, u8 *cbuf, u32 len, + u8 *status) { u32 i; u32 its_len = MIN((u32)32, len - idx); - + its_len = MIN(its_len, taint_len); u8 save[32]; memcpy(save, &buf[idx], its_len); - *status = 0; - for (i = 0; i < its_len; ++i) { - if (pattern[i] != buf[idx + i] || o_pattern[i] != orig_buf[idx + i] || + if ((pattern[i] != buf[idx + i] && o_pattern[i] != orig_buf[idx + i]) || *status == 1) { break; @@ -654,6 +1511,8 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } + if (*status == 1) { memcpy(cbuf + idx, &buf[idx], i); } + } memcpy(&buf[idx], save, i); @@ -661,23 +1520,21 @@ static u8 rtn_extend_encoding(afl_state_t *afl, u8 *pattern, u8 *repl, } -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, 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; u32 loggeds = h->hits; if (h->hits > CMP_MAP_RTN_H) { loggeds = CMP_MAP_RTN_H; } u8 status = 0; - // opt not in the paper - // u32 fails = 0; u8 found_one = 0; for (i = 0; i < loggeds; ++i) { - u32 fails = 0; - struct cmpfn_operands *o = &((struct cmpfn_operands *)afl->shm.cmp_map->log[key])[i]; @@ -696,50 +1553,84 @@ 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) { + if (taint) { - if (unlikely(rtn_extend_encoding(afl, o->v0, o->v1, orig_o->v0, idx, - orig_buf, buf, len, &status))) { + t = taint; + while (t->next) { - return 1; + t = t->next; } - if (status == 2) { + } else { - ++fails; + have_taint = 0; + t = NULL; - } else if (status == 1) { + } - break; + 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; } - if (unlikely(rtn_extend_encoding(afl, 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, idx, + taint_len, orig_buf, buf, cbuf, len, + &status))) { return 1; } - if (status == 2) { + if (status == 1) { + + found_one = 1; + break; + + } + + status = 0; + + if (unlikely(rtn_extend_encoding(afl, o->v1, o->v0, orig_o->v1, idx, + taint_len, orig_buf, buf, cbuf, len, + &status))) { - ++fails; + return 1; - } else if (status == 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) { - if (afl->pass_stats[key].total == 0) { + if (unlikely(!afl->pass_stats[key].total)) { maybe_add_auto(afl, o->v0, SHAPE_BYTES(h->shape)); maybe_add_auto(afl, o->v1, SHAPE_BYTES(h->shape)); @@ -791,7 +1682,44 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, memcpy(afl->orig_cmp_map, afl->shm.cmp_map, sizeof(struct cmp_map)); - if (unlikely(colorization(afl, buf, len, exec_cksum))) { return 1; } + struct tainted *taint = NULL; + + if (!afl->queue_cur->taint || !afl->queue_cur->cmplog_colorinput) { + + if (unlikely(colorization(afl, buf, len, exec_cksum, &taint))) { return 1; } + + // no taint? still try, create a dummy to prevent again colorization + if (!taint) { + + taint = ck_alloc(sizeof(struct tainted)); + taint->len = len; + + } + + } else { + + buf = afl->queue_cur->cmplog_colorinput; + taint = afl->queue_cur->taint; + // reget the cmplog information + if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { return 1; } + + } + +#ifdef _DEBUG + dump("ORIG", orig_buf, len); + dump("NEW ", buf, len); +#endif + + struct tainted *t = taint; + + while (t) { + +#ifdef _DEBUG + fprintf(stderr, "T: pos=%u len=%u\n", t->pos, t->len); +#endif + t = t->next; + + } // do it manually, forkserver clear only afl->fsrv.trace_bits memset(afl->shm.cmp_map->headers, 0, sizeof(afl->shm.cmp_map->headers)); @@ -807,15 +1735,38 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, afl->stage_max = 0; afl->stage_cur = 0; + u32 lvl; + u32 cmplog_done = afl->queue_cur->colorized; + u32 cmplog_lvl = afl->cmplog_lvl; + if (!cmplog_done) { + + lvl = 1; + + } else { + + lvl = 0; + + } + + if (cmplog_lvl >= 2 && cmplog_done < 2) { lvl += 2; } + if (cmplog_lvl >= 3 && cmplog_done < 3) { lvl += 4; } + + 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); + 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 == 0xff || + afl->pass_stats[k].total == 0xff) { + +#ifdef _DEBUG + fprintf(stderr, "DISABLED %u\n", k); +#endif afl->shm.cmp_map->headers[k].hits = 0; // ignore this cmp @@ -841,11 +1792,19 @@ 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) { - 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))) { + + goto exit_its; + + } } else { - if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, len))) { goto exit_its; } + if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { + + goto exit_its; + + } } @@ -854,12 +1813,86 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len, r = 0; exit_its: + + afl->queue_cur->colorized = afl->cmplog_lvl; + if (afl->cmplog_lvl == CMPLOG_LVL_MAX) { + + ck_free(afl->queue_cur->cmplog_colorinput); + t = taint; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + afl->queue_cur->taint = NULL; + + } else { + + 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); + + } + + } + + // 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 = (u64 *)afl->virgin_bits; + u32 *s = (u64 *)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 COMBINED\n"); + + } else { + + fprintf(stderr, "NO new combined\n"); + + } + +#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(buf, orig_buf, len); - return r; } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 60c9684c..8423a3d1 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -102,6 +102,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { 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 diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bb2674f0..1e914ca6 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -77,13 +77,8 @@ static void at_exit() { } int kill_signal = SIGKILL; - /* AFL_KILL_SIGNAL should already be a valid int at this point */ - if (getenv("AFL_KILL_SIGNAL")) { - - kill_signal = atoi(getenv("AFL_KILL_SIGNAL")); - - } + if ((ptr = getenv("AFL_KILL_SIGNAL"))) { kill_signal = atoi(ptr); } if (pid1 > 0) { kill(pid1, kill_signal); } if (pid2 > 0) { kill(pid2, kill_signal); } @@ -103,13 +98,14 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\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-%u ms)\n" - " -m megs - memory limit for child process (%u MB, 0 = no limit)\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 " @@ -125,7 +121,9 @@ static void usage(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_level - set the complexity/intensivity of CmpLog.\n" + " Values: 1 (default), 2 (intensive) and 3 (heavy)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " @@ -337,7 +335,6 @@ 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); @@ -358,7 +355,8 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:Z")) > 0) { + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:P:RQs:S:t:T:UV:Wx:Z")) > + 0) { switch (opt) { @@ -787,6 +785,26 @@ int main(int argc, char **argv_orig, char **envp) { } break; + case 'l': { + + afl->cmplog_lvl = atoi(optarg); + if (afl->cmplog_lvl < 1 || afl->cmplog_lvl > CMPLOG_LVL_MAX) { + + FATAL( + "Bad complog level value, accepted values are 1 (default), 2 and " + "%u.", + CMPLOG_LVL_MAX); + + } + + 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"); } @@ -1635,6 +1653,14 @@ int main(int argc, char **argv_orig, char **envp) { 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: @@ -1652,6 +1678,7 @@ int main(int argc, char **argv_orig, char **envp) { } afl->expand_havoc = 2; + if (afl->cmplog_lvl < 2) afl->cmplog_lvl = 2; break; case 2: // if (!have_p) afl->schedule = EXPLOIT; @@ -1665,11 +1692,14 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 4; break; case 4: - // if not in sync mode, enable deterministic mode? - // if (!afl->sync_id) afl->skip_deterministic = 0; afl->expand_havoc = 5; + if (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; -- cgit 1.4.1 From a8b06291630d5e4b71ab1eb2fee856c1c576bf4b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 18 Jan 2021 09:26:16 +0100 Subject: introspection, favor extint over long double --- src/afl-cc.c | 3 +- src/afl-fuzz-redqueen.c | 202 +++++++++--------------------------------------- 2 files changed, 38 insertions(+), 167 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 02c9c7c5..d44f069a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -683,6 +683,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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 (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) continue; @@ -703,7 +704,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "-shared")) shared_linking = 1; if (!strncmp(cur, "-O", 2)) have_o = 1; - if (!strncmp(cur, "-f", 2) && strstr(cur, "unroll-loop")) have_unroll = 1; + if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1; cc_params[cc_par_cnt++] = cur; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 56246d6e..3cfe05bc 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -31,6 +31,7 @@ #define _DEBUG #define COMBINE #define CMPLOG_INTROSPECTION +#define ARITHMETIC_LESSER_GREATER ///// Colorization @@ -375,6 +376,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, #if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) FILE *f = stderr; + #ifndef _DEBUG if (afl->not_on_tty) { char fn[4096]; @@ -383,6 +385,8 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, } + #endif + if (f) { fprintf( @@ -393,7 +397,9 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, afl->queue_cur->colorized, afl->stage_cur, new_hit_cnt - orig_hit_cnt, positions); + #ifndef _DEBUG if (afl->not_on_tty) { fclose(f); } + #endif } @@ -758,11 +764,15 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // here we add and subract 1 from the value, but only if it is not an // == or != comparison - // Bits: 1 = Equal, 2 = Greater, 3 = Lesser, 4 = Float + // Bits: 1 = Equal, 2 = Greater, 4 = Lesser, 8 = Float + // 16 = modified float, 32 = modified integer (modified = wont match + // in original buffer) +#ifdef ARITHMETIC_LESSER_GREATER if (lvl < 4) { return 0; } - if (attr >= 8 && attr < 16) { // lesser/greater integer comparison + // lesser/greater FP comparison + if (!(attr & 1) && (attr & 6) && (attr >= 8 && attr < 16)) { u64 repl_new; if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { @@ -836,11 +846,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, double *f = (double *)&repl; float g = (float)*f; repl_new = 0; -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) memcpy((char *)&repl_new, (char *)&g, 4); -#else + #else memcpy(((char *)&repl_new) + 4, (char *)&g, 4); -#endif + #endif changed_val = repl_new; h->shape = 3; // modify shape @@ -850,7 +860,7 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - h->shape = 7; + h->shape = 7; // recover shape return 1; } @@ -859,7 +869,9 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } - } else if (attr > 1 && attr < 8) { // lesser/greater integer comparison + } else if (!(attr & 1) && (attr & 6) && attr < 8) { + + // lesser/greater integer comparison u64 repl_new; @@ -885,6 +897,8 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, } +#endif /* ARITHMETIC_LESSER_GREATER */ + return 0; } @@ -1057,89 +1071,6 @@ static u8 cmp_extend_encodingN(afl_state_t *afl, struct cmp_header *h, } -// uh a pointer read from (long double*) reads 12 bytes, not 10 ... -// so lets make this complicated. -static u8 cmp_extend_encoding_ld(afl_state_t *afl, struct cmp_header *h, - u8 *pattern, u8 *repl, u8 *o_pattern, - u8 *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 *buf_ld = &buf[idx], *o_buf_ld = &orig_buf[idx], backup[10]; - u32 its_len = MIN(len - idx, taint_len); - - if (its_len >= 10) { - -#ifdef _DEBUG - fprintf(stderr, "TestUld: %u>=10 (len=%u idx=%u attr=%u) (%u)\n", its_len, - len, idx, attr, do_reverse); - fprintf(stderr, "TestUld: "); - u32 i; - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", pattern[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", buf_ld[i]); - fprintf(stderr, " "); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", o_pattern[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", o_buf_ld[i]); - fprintf(stderr, " <= "); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", repl[i]); - fprintf(stderr, "=="); - for (i = 0; i < 10; i++) - fprintf(stderr, "%02x", changed_val[i]); - fprintf(stderr, "\n"); -#endif - - if (!memcmp(pattern, buf_ld, 10) && !memcmp(o_pattern, o_buf_ld, 10)) { - - // 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 - - memcpy(backup, buf_ld, 10); - memcpy(buf_ld, repl, 10); - if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } -#ifdef COMBINE - if (*status == 1) { memcpy(cbuf + idx, repl, 10); } -#endif - memcpy(buf_ld, backup, 10); - -#ifdef _DEBUG - fprintf(stderr, "Status=%u\n", *status); -#endif - - } - - } - - // reverse encoding - if (do_reverse && *status != 1) { - - u8 sp[10], sr[10], osp[10], osr[10]; - SWAPNN(sp, pattern, 10); - SWAPNN(sr, repl, 10); - SWAPNN(osp, o_pattern, 10); - SWAPNN(osr, changed_val, 10); - - if (unlikely(cmp_extend_encoding_ld(afl, h, sp, sr, osp, osr, attr, idx, - taint_len, orig_buf, buf, cbuf, len, 0, - lvl, status))) { - - return 1; - - } - - } - - return 0; - -} - static void try_to_add_to_dict(afl_state_t *afl, u64 v, u8 shape) { u8 *b = (u8 *)&v; @@ -1273,7 +1204,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, struct cmp_header *h = &afl->shm.cmp_map->headers[key]; struct tainted * t; u32 i, j, idx, taint_len; - u32 have_taint = 1, is_128 = 0, is_n = 0, is_ld = 0; + u32 have_taint = 1, is_128 = 0, is_n = 0; u32 loggeds = h->hits; if (h->hits > CMP_MAP_H) { loggeds = CMP_MAP_H; } @@ -1281,12 +1212,11 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, u8 found_one = 0; /* loop cmps are useless, detect and ignore them */ - u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; - long double ld0, ld1, o_ld0, o_ld1; - 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; + u128 s128_v0 = 0, s128_v1 = 0, orig_s128_v0 = 0, orig_s128_v1 = 0; + 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; switch (SHAPE_BYTES(h->shape)) { @@ -1298,9 +1228,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, case 16: is_128 = 1; break; - case 10: - if (h->attribute & 8) { is_ld = 1; } - // fall through default: is_n = 1; @@ -1376,24 +1303,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, 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); - if (is_ld) { - -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - memcpy((char *)&ld0, (char *)&s128_v0, sizeof(long double)); - memcpy((char *)&ld1, (char *)&s128_v1, sizeof(long double)); - memcpy((char *)&o_ld0, (char *)&orig_s128_v0, sizeof(long double)); - memcpy((char *)&o_ld1, (char *)&orig_s128_v1, sizeof(long double)); -#else - memcpy((char *)&ld0, (char *)(&s128_v0) + 6, sizeof(long double)); - memcpy((char *)&ld1, (char *)(&s128_v1) + 6, sizeof(long double)); - memcpy((char *)&o_ld0, (char *)(&orig_s128_v0) + 6, - sizeof(long double)); - memcpy((char *)&o_ld1, (char *)(&orig_s128_v1) + 6, - sizeof(long double)); -#endif - - } - } for (idx = 0; idx < len; ++idx) { @@ -1420,51 +1329,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, status = 0; - if (is_ld) { // long double special case - - if (ld0 != o_ld0 && o_ld1 != o_ld0) { - - if (unlikely(cmp_extend_encoding_ld( - afl, h, (u8 *)&ld0, (u8 *)&ld1, (u8 *)&o_ld0, (u8 *)&o_ld1, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - - if (ld1 != o_ld1 && o_ld0 != o_ld1) { - - if (unlikely(cmp_extend_encoding_ld( - afl, h, (u8 *)&ld1, (u8 *)&ld0, (u8 *)&o_ld1, (u8 *)&o_ld0, - h->attribute, idx, taint_len, orig_buf, buf, cbuf, len, 1, - lvl, &status))) { - - return 1; - - } - - } - - if (status == 1) { - - found_one = 1; - break; - - } - - } else - - if (is_n) { // _ExtInt special case + if (is_n) { // _ExtInt special case if (s128_v0 != orig_s128_v0 && orig_s128_v0 != orig_s128_v1) { @@ -1552,7 +1417,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, } - // even for u128 and long double do cmp_extend_encoding() because + // 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. @@ -1995,7 +1860,7 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { } - } else { + } else if (lvl & 1) { if (unlikely(rtn_fuzz(afl, k, orig_buf, buf, cbuf, len, taint))) { @@ -2064,7 +1929,7 @@ exit_its: #else u32 *v = (u64 *)afl->virgin_bits; u32 *s = (u64 *)virgin_save; - u32 i; + u32 i; for (i = 0; i < (afl->shm.map_size >> 2); i++) { v[i] &= s[i]; @@ -2094,6 +1959,7 @@ exit_its: #if defined(_DEBUG) || defined(CMPLOG_INTROSPECTION) FILE *f = stderr; + #ifndef _DEBUG if (afl->not_on_tty) { char fn[4096]; @@ -2102,6 +1968,8 @@ exit_its: } + #endif + if (f) { fprintf(f, @@ -2109,7 +1977,9 @@ exit_its: 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 } -- cgit 1.4.1 From 0367f6c72339ba655956d7e17b0b27c92b22d781 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 19 Jan 2021 14:03:10 +0100 Subject: cleanup and rename _DISCARD->_SKIP --- instrumentation/README.instrument_list.md | 2 +- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-cc.c | 6 +++--- src/afl-fuzz-bitmap.c | 1 - src/afl-fuzz.c | 1 - src/afl-ld-lto.c | 1 - 6 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 83197954..b47b50f6 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -41,7 +41,7 @@ in any function where you want: * `__AFL_COVERAGE_ON();` - enable coverage from this point onwards * `__AFL_COVERAGE_OFF();` - disable coverage from this point onwards * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point - * `__AFL_COVERAGE_ABORT();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. + * `__AFL_COVERAGE_SKIP();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. ## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index b735d8df..e31bff16 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1429,7 +1429,7 @@ void __afl_coverage_discard() { } // discard the testcase -void __afl_coverage_abort() { +void __afl_coverage_skip() { __afl_coverage_discard(); diff --git a/src/afl-cc.c b/src/afl-cc.c index 8fb42718..1379488e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -835,7 +835,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" "extern \"C\" void __afl_coverage_discard();" - "extern \"C\" void __afl_coverage_abort();" + "extern \"C\" void __afl_coverage_skip();" "extern \"C\" void __afl_coverage_on();" "extern \"C\" void __afl_coverage_off();"; @@ -844,7 +844,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" "void __afl_coverage_discard();" - "void __afl_coverage_abort();" + "void __afl_coverage_skip();" "void __afl_coverage_on();" "void __afl_coverage_off();"; @@ -857,7 +857,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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_ABORT()=__afl_coverage_abort()"; + 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)"; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index ed8c2510..586f3990 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -703,7 +703,6 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (!classified) { classify_counts(&afl->fsrv); - // classified = 1; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e6317f43..7facf261 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -337,7 +337,6 @@ 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); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 7a4d9132..0671d1c4 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -253,7 +253,6 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { s32 pid, i, status; - // u8 * ptr; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { -- cgit 1.4.1 From ac1117ffaeec0bd3c593063a05d8aa000d162d47 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Tue, 19 Jan 2021 09:44:59 +0800 Subject: android: Fix runtime for mutator --- Android.bp | 4 ++-- src/afl-cc.c | 14 ++++++++++---- src/afl-fuzz-mutators.c | 5 ++++- 3 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/Android.bp b/Android.bp index bd23d1d1..5d6f0433 100644 --- a/Android.bp +++ b/Android.bp @@ -135,6 +135,8 @@ cc_binary_host { "-DCLANGPP_BIN=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/clang++\"", "-DAFL_REAL_LD=\"prebuilts/clang/host/linux-x86/clang-r383902b/bin/ld.lld\"", "-DLLVM_LTO=1", + "-DLLVM_MAJOR=11", + "-DLLVM_MINOR=2", ], srcs: [ @@ -145,8 +147,6 @@ cc_binary_host { symlinks: [ "afl-clang-fast", "afl-clang-fast++", - "afl-clang-lto", - "afl-clang-lto++", ], } diff --git a/src/afl-cc.c b/src/afl-cc.c index 1379488e..f3dfd49f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -586,6 +586,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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) @@ -605,6 +608,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } +#endif #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -1034,6 +1038,10 @@ int main(int argc, char **argv, char **envp) { #endif +#ifdef __ANDROID__ + have_llvm = 1; +#endif + if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { have_gcc_plugin = 1; @@ -1807,11 +1815,8 @@ int main(int argc, char **argv, char **envp) { if (!be_quiet && cmplog_mode) printf("CmpLog mode by \n"); -#ifdef __ANDROID__ - ptr = find_object("afl-compiler-rt.so", argv[0]); -#else +#ifndef __ANDROID__ ptr = find_object("afl-compiler-rt.o", argv[0]); -#endif if (!ptr) { @@ -1824,6 +1829,7 @@ int main(int argc, char **argv, char **envp) { if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } ck_free(ptr); +#endif edit_params(argc, argv, envp); diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 089707b9..80df6d08 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -141,7 +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; - mutator->name_short = strrchr(fn, '/') + 1; + 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); -- cgit 1.4.1 From 0d472adef0cbe68cec128b7b15e508f0bb05455d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 12:02:50 +0100 Subject: temp conflict resolve --- src/afl-cc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index d44f069a..2ce986c7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -856,8 +856,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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_ABORT()=__afl_coverage_abort()"; - + 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)"; -- cgit 1.4.1 From b850951c726258053c5635d6597704cf346fe3c4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 17:15:14 +0100 Subject: code format and not setting sanitizers if debug and settings present --- src/afl-cc.c | 8 ++++---- src/afl-forkserver.c | 53 +++++++++++++++++++++++++++------------------------ src/afl-fuzz-bitmap.c | 6 +----- src/afl-fuzz-stats.c | 2 ++ src/afl-fuzz.c | 12 ++++++++---- src/afl-ld-lto.c | 2 +- 6 files changed, 44 insertions(+), 39 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index b0b11f48..8e7af0f9 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -586,9 +586,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) -#ifdef __ANDROID__ + #ifdef __ANDROID__ cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; -#else + #else if (have_instr_list) { if (!be_quiet) @@ -608,7 +608,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#endif + #endif #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -1036,7 +1036,7 @@ int main(int argc, char **argv, char **envp) { #endif #ifdef __ANDROID__ - have_llvm = 1; + have_llvm = 1; #endif if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 50e4139b..d4484de7 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -484,38 +484,41 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* 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 (!afl->debug || !getenv("ASAN_OPTIONS")) + 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); /* 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 (!afl->debug || !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", + 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 (!afl->debug || !getenv("MSAN_OPTIONS")) + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" "abort_on_error=1:" diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 586f3990..0c4a114e 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -700,11 +700,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (likely(!afl->non_instrumented_mode)) { - if (!classified) { - - classify_counts(&afl->fsrv); - - } + if (!classified) { classify_counts(&afl->fsrv); } simplify_trace(afl, afl->fsrv.trace_bits); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index e67bace9..82da8176 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -60,8 +60,10 @@ void write_setup_file(afl_state_t *afl, u32 argc, char **argv) { if (i) fprintf(f, " "); #ifdef __ANDROID__ if (memchr(argv[i], '\'', sizeof(argv[i]))) { + #else if (index(argv[i], '\'')) { + #endif fprintf(f, "'"); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9a8159bd..2a59bbe4 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,7 +123,8 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (integer+string), 2 (+FP) and 3 (+transform)\n\n" + " Values: 1 (integer+string), 2 (+FP) and 3 " + "(+transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " @@ -584,7 +585,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->timeout_given) { FATAL("Multiple -t options not supported"); } - if (!optarg || 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"); @@ -766,7 +768,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'V': { afl->most_time_key = 1; - if (!optarg || 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"); @@ -777,7 +780,8 @@ int main(int argc, char **argv_orig, char **envp) { case 'E': { afl->most_execs_key = 1; - if (!optarg || 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"); diff --git a/src/afl-ld-lto.c b/src/afl-ld-lto.c index 0671d1c4..49c04e4a 100644 --- a/src/afl-ld-lto.c +++ b/src/afl-ld-lto.c @@ -252,7 +252,7 @@ static void edit_params(int argc, char **argv) { int main(int argc, char **argv) { - s32 pid, i, status; + s32 pid, i, status; char thecwd[PATH_MAX]; if (getenv("AFL_LD_CALLER") != NULL) { -- cgit 1.4.1 From 2a9fcd2a87bf11bc019c8d6be2b6eda77b772ee2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 28 Jan 2021 18:01:27 +0100 Subject: warn on afl-gcc/afl-clang instrumentation --- src/afl-cc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 8e7af0f9..ff0f3c07 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1788,6 +1788,15 @@ int main(int argc, char **argv, char **envp) { } + 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()); -- cgit 1.4.1 From ce673ccab3f2ca438f879c1d2e63988eb9737a35 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 28 Jan 2021 19:19:57 +0100 Subject: remove snapshot reference --- src/afl-cc.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index ff0f3c07..b5dcb632 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1438,34 +1438,34 @@ int main(int argc, char **argv, char **envp) { " CC=afl-cc CXX=afl-c++ meson\n\n"); SAYF( - " |---------------- FEATURES " - "---------------|\n" - "MODES: NCC PERSIST SNAP DICT LAF " + " |------------- FEATURES " + "-------------|\n" + "MODES: NCC PERSIST DICT LAF " "CMPLOG SELECT\n" " [LTO] llvm LTO: %s%s\n" - " PCGUARD DEFAULT yes yes yes yes yes yes " + " PCGUARD DEFAULT yes yes yes yes yes " " yes\n" - " CLASSIC yes yes yes yes yes yes " + " CLASSIC yes yes yes yes yes " " yes\n" " [LLVM] llvm: %s%s\n" - " PCGUARD %s yes yes yes module yes yes " + " PCGUARD %s yes yes module yes yes " "extern\n" - " CLASSIC %s no yes yes module yes yes " + " CLASSIC %s no yes module yes yes " "yes\n" " - NORMAL\n" " - CTX\n" " - NGRAM-{2-16}\n" - " INSTRIM no yes yes module yes yes " + " INSTRIM no yes module yes yes " " yes\n" " - NORMAL\n" " - CTX\n" " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" - " CLASSIC DEFAULT no yes yes no no no " - " yes\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 " - " no\n\n", + " CLASSIC DEFAULT no no no no no " + "no\n\n", have_lto ? "AVAILABLE" : "unavailable!", compiler_mode == LTO ? " [SELECTED]" : "", have_llvm ? "AVAILABLE" : "unavailable!", @@ -1520,9 +1520,6 @@ int main(int argc, char **argv, char **envp) { " (instrumentation/README.lto.md)\n" " PERSIST: persistent mode support [code] (huge speed increase!)\n" " (instrumentation/README.persistent_mode.md)\n" - " SNAP: linux lkm snapshot module support [automatic] (speed " - "increase)\n" - " (https://github.com/AFLplusplus/AFL-Snapshot-LKM/)\n" " DICT: dictionary in the target [yes=automatic or llvm module " "pass]\n" " (instrumentation/README.lto.md + " -- cgit 1.4.1 From 7a861498c27997cd7be01a5650d54cff3b87a02e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 15:04:40 +0100 Subject: added support for __afl_coverage_interesting --- docs/Changelog.md | 3 ++ instrumentation/README.instrument_list.md | 7 +++++ instrumentation/SanitizerCoverageLTO.so.cc | 20 ++++++++++++++ instrumentation/SanitizerCoveragePCGUARD.so.cc | 38 +++++++++++++++++++++++--- src/afl-cc.c | 16 ++++++----- src/afl-fuzz.c | 5 ++-- 6 files changed, 76 insertions(+), 13 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 6e59961b..99bc8b47 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -36,6 +36,9 @@ sending a mail to . - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - cmplog/redqueen can now process basic libc++ and libstdc++ std::string comparisons (though no position or length type variants) + - added support for __afl_coverage_interesting() for LTO and + and our own PCGUARD (llvm 10.0.1+), read more about this function + and selective coverage in instrumentation/README.instrument_list.md - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index b47b50f6..b7dfb40c 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -43,6 +43,13 @@ in any function where you want: * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point * `__AFL_COVERAGE_SKIP();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. +A special function is `__afl_coverage_interesting`. +To use this, you must define `void __afl_coverage_interesting(u8 val, u32 id);`. +Then you can use this function globally, where the `val` parameter can be set +by you, the `id` parameter is for afl-fuzz and will be overwritten. +Note that useful parameters are for `val` are: 1, 2, 3, 4, 8, 16, 32, 64, 128. +A value of e.g. 33 will be seen as 32 for coverage purposes. + ## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST This feature is equivalent to llvm 12 sancov feature and allows to specify diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 016ac71f..e3490847 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -1237,6 +1237,25 @@ void ModuleSanitizerCoverage::instrumentFunction( for (auto &BB : F) { + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + StringRef FuncName = Callee->getName(); + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + Value *val = ConstantInt::get(Int32Ty, ++afl_global_id); + callInst->setOperand(1, val); + + } + + } + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) BlocksToInstrument.push_back(&BB); for (auto &Inst : BB) { @@ -1338,6 +1357,7 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, if (AllBlocks.empty()) return false; CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { // afl++ START diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index ecd6bc9b..5b274770 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -311,7 +311,8 @@ class ModuleSanitizerCoverage { Function &F, Type *Ty, const char *Section); GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); - void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks, + uint32_t special); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); Function *CreateInitCallsForSections(Module &M, const char *CtorName, @@ -970,11 +971,11 @@ GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( } void ModuleSanitizerCoverage::CreateFunctionLocalArrays( - Function &F, ArrayRef AllBlocks) { + Function &F, ArrayRef AllBlocks, uint32_t special) { if (Options.TracePCGuard) FunctionGuardArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName); if (Options.Inline8bitCounters) Function8bitCounterArray = CreateFunctionLocalArrayInSection( @@ -993,9 +994,38 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, bool IsLeafFunc) { if (AllBlocks.empty()) return false; - CreateFunctionLocalArrays(F, AllBlocks); + + uint32_t special = 0; + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + StringRef FuncName = Callee->getName(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + uint32_t id = 1 + instr + (uint32_t)AllBlocks.size() + special++; + Value * val = ConstantInt::get(Int32Ty, id); + callInst->setOperand(1, val); + + } + + } + + } + + CreateFunctionLocalArrays(F, AllBlocks, special); for (size_t i = 0, N = AllBlocks.size(); i < N; i++) InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + + instr += special; + return true; } diff --git a/src/afl-cc.c b/src/afl-cc.c index b5dcb632..f513764a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -572,7 +572,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - if (instrument_mode == INSTRUMENT_CFG) + 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 @@ -1670,15 +1671,16 @@ int main(int argc, char **argv, char **envp) { if (compiler_mode == LTO) { if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || - instrument_mode == INSTRUMENT_CFG) { + instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) { lto_mode = 1; - if (!instrument_mode) { + // force CFG + // if (!instrument_mode) { - instrument_mode = INSTRUMENT_CFG; - // ptr = instrument_mode_string[instrument_mode]; - - } + instrument_mode = INSTRUMENT_PCGUARD; + // ptr = instrument_mode_string[instrument_mode]; + // } } else if (instrument_mode == INSTRUMENT_LTO || diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 647a665e..82eff61f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -145,7 +145,8 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D, -Z (use -d to disable -D) and no trimming\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" @@ -502,7 +503,7 @@ int main(int argc, char **argv_orig, char **envp) { 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 + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { -- cgit 1.4.1 From b9f469e12fde797e301845caa4b0fd44315318bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 20:00:29 +0100 Subject: make some really weird targets compile --- src/afl-cc.c | 1 + src/afl-fuzz.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index f513764a..7db3c9a0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -689,6 +689,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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")) continue; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 4a23d99d..3a7343ae 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1596,7 +1596,6 @@ int main(int argc, char **argv_orig, char **envp) { u32 new_map_size = afl_fsrv_get_mapsize(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); -printf("NEW MAP SIZE2 %u (is %u)\n", new_map_size, map_size); if (new_map_size && new_map_size != 4194304) { -- cgit 1.4.1 From 654f389e73c9fd5b7e141b33ea28ab0fdda3178f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 23:36:27 +0100 Subject: try to remove warnings during compilation --- src/afl-cc.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 7db3c9a0..f272f0b5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -313,7 +313,8 @@ static u8 *find_object(u8 *obj, u8 *argv0) { 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; + preprocessor_only = 0, have_unroll = 0, have_o = 0, have_pic = 0, + have_c = 0; u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); @@ -461,7 +462,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { // laf if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); @@ -481,7 +482,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); @@ -501,7 +502,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); @@ -524,7 +525,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { - if (lto_mode) { + if (lto_mode && !have_c) { cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); @@ -560,7 +561,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (lto_mode) { + if (lto_mode && !have_c) { u8 *ld_path = strdup(AFL_REAL_LD); if (!*ld_path) ld_path = "ld.lld"; @@ -708,6 +709,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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; @@ -800,7 +802,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) && !defined(__HAIKU__) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) + cc_params[cc_par_cnt++] = "-lrt"; #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -967,15 +970,19 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + if (!shared_linking && !have_c) 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"; + if (!have_c) + cc_params[cc_par_cnt++] = "-lrt"; #endif + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + } #endif -- cgit 1.4.1 From 1f71b85426f837ebcae8381897d44a3a67c73a4f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 2 Feb 2021 10:05:10 +0100 Subject: automagically fix sanitize fuzzer+coverage --- src/afl-cc.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index f272f0b5..cba435bd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -693,6 +693,38 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined")) 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 errornous '-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; @@ -802,8 +834,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; #endif cc_params[cc_par_cnt++] = "-D__AFL_HAVE_MANUAL_CONTROL=1"; @@ -976,12 +1007,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { #endif #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) - cc_params[cc_par_cnt++] = "-lrt"; + if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; } -- cgit 1.4.1 From 58a5372bf0c55ead2a04ed4a4a5b651d68e69292 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Wed, 3 Feb 2021 14:18:35 +0100 Subject: typo --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index cba435bd..cf10d9a7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -707,7 +707,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!be_quiet) WARNF( - "Found errornous '-fsanitize=fuzzer', trying to replace with " + "Found erroneous '-fsanitize=fuzzer', trying to replace with " "libAFLDriver.a"); if (!afllib) { -- cgit 1.4.1 From d920104248a6c5387267561382636de404938675 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 6 Feb 2021 12:26:35 +0100 Subject: remove compiler warnings --- instrumentation/afl-compiler-rt.o.c | 20 ++++++++++---------- src/afl-cc.c | 4 ++++ utils/aflpp_driver/aflpp_driver.c | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 905051c6..ceae15d1 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -368,8 +368,8 @@ static void __afl_map_shm(void) { if (__afl_map_size && __afl_map_size > MAP_SIZE) { - u8 *map_env = getenv("AFL_MAP_SIZE"); - if (!map_env || atoi(map_env) < MAP_SIZE) { + u8 *map_env = (u8 *)getenv("AFL_MAP_SIZE"); + if (!map_env || atoi((char *)map_env) < MAP_SIZE) { send_forkserver_error(FS_ERROR_MAP_SIZE); _exit(1); @@ -378,7 +378,7 @@ static void __afl_map_shm(void) { } - __afl_area_ptr = shmat(shm_id, (void *)__afl_map_addr, 0); + __afl_area_ptr = (u8 *)shmat(shm_id, (void *)__afl_map_addr, 0); /* Whooooops. */ @@ -405,9 +405,9 @@ static void __afl_map_shm(void) { __afl_map_addr) { - __afl_area_ptr = - mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_area_ptr = (u8 *)mmap( + (void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (__afl_area_ptr == MAP_FAILED) { @@ -425,7 +425,7 @@ static void __afl_map_shm(void) { if (__afl_final_loc > MAP_INITIAL_SIZE) { - __afl_area_ptr = malloc(__afl_final_loc); + __afl_area_ptr = (u8 *)malloc(__afl_final_loc); } @@ -439,7 +439,7 @@ static void __afl_map_shm(void) { if (__afl_map_size > MAP_INITIAL_SIZE) { - __afl_area_ptr_dummy = malloc(__afl_map_size); + __afl_area_ptr_dummy = (u8 *)malloc(__afl_map_size); if (__afl_area_ptr_dummy) { @@ -505,7 +505,7 @@ static void __afl_map_shm(void) { #else u32 shm_id = atoi(id_str); - __afl_cmp_map = shmat(shm_id, NULL, 0); + __afl_cmp_map = (struct cmp_map *)shmat(shm_id, NULL, 0); #endif __afl_cmp_map_backup = __afl_cmp_map; @@ -1528,7 +1528,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { // to avoid to call it on .text addresses static int area_is_mapped(void *ptr, size_t len) { - char *p = ptr; + char *p = (char *)ptr; char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1)); int r = msync(page, (p - page) + len, MS_ASYNC); diff --git a/src/afl-cc.c b/src/afl-cc.c index cf10d9a7..76f4a437 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1840,6 +1840,8 @@ int main(int argc, char **argv, char **envp) { for (i = 0; i < argc; i++) SAYF(" '%s'", argv[i]); SAYF("\n"); + fflush(stdout); + fflush(stderr); } @@ -1880,6 +1882,8 @@ int main(int argc, char **argv, char **envp) { for (i = 0; i < (s32)cc_par_cnt; i++) SAYF(" '%s'", cc_params[i]); SAYF("\n"); + fflush(stdout); + fflush(stderr); } diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c index 7bb929b2..6af79e14 100644 --- a/utils/aflpp_driver/aflpp_driver.c +++ b/utils/aflpp_driver/aflpp_driver.c @@ -173,7 +173,7 @@ size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { // Execute any files provided as parameters. static int ExecuteFilesOnyByOne(int argc, char **argv) { - unsigned char *buf = malloc(MAX_FILE); + unsigned char *buf = (unsigned char *)malloc(MAX_FILE); for (int i = 1; i < argc; i++) { int fd = open(argv[i], O_RDONLY); -- cgit 1.4.1 From 0ad56167c53ae660d40ccc6cdedb39f0a52eefcd Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 7 Feb 2021 07:51:29 +0100 Subject: fix scan-build issues --- instrumentation/LLVMInsTrim.so.cc | 2 +- instrumentation/SanitizerCoverageLTO.so.cc | 2 +- instrumentation/afl-llvm-common.cc | 2 +- instrumentation/afl-llvm-dict2file.so.cc | 1 - instrumentation/afl-llvm-lto-instrumentation.so.cc | 2 +- instrumentation/afl-llvm-pass.so.cc | 1 + instrumentation/compare-transform-pass.so.cc | 2 +- instrumentation/split-compares-pass.so.cc | 37 +++++++++------------- src/afl-cc.c | 7 ---- src/afl-fuzz-init.c | 2 +- src/afl-fuzz-redqueen.c | 36 ++++++++++++++++++--- 11 files changed, 53 insertions(+), 41 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 235ee30f..948f8f3a 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -459,7 +459,7 @@ struct InsTrim : public ModulePass { BasicBlock *PBB = *PI; auto It = PredMap.insert({PBB, genLabel()}); unsigned Label = It.first->second; - cur_loc = Label; + // cur_loc = Label; PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); } diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index e3490847..3026abc8 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -760,7 +760,7 @@ bool ModuleSanitizerCoverage::instrumentModule( if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; + // addedNull = true; } diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index a27c4069..aa54f4f7 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -351,7 +351,7 @@ static std::string getSourceName(llvm::Function *F) { if (cDILoc) { instFilename = cDILoc->getFilename(); } - if (instFilename.str().empty()) { + if (instFilename.str().empty() && cDILoc) { /* If the original location is empty, try using the inlined location */ diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index a4b33732..6f34ac5a 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -430,7 +430,6 @@ bool AFLdict2filePass::runOnModule(Module &M) { if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; } diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 841d52e5..f5c24e41 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -546,7 +546,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - addedNull = true; + // addedNull = true; } diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 57ff3b47..16fd9c94 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -538,6 +538,7 @@ bool AFLCoverage::runOnModule(Module &M) { Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), AFLPrevLoc); + Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index da5cf7e9..8b00d8d1 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -391,7 +391,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (val && !val->empty()) { Str2 = StringRef(*val); - HasStr2 = true; + // HasStr2 = true; } diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index b6d8c466..80cd90ba 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -407,6 +407,7 @@ bool SplitComparesTransform::simplifyIntSignedness(Module &M) { auto op1 = IcmpInst->getOperand(1); IntegerType *intTyOp0 = dyn_cast(op0->getType()); + if (!intTyOp0) { continue; } unsigned bitw = intTyOp0->getBitWidth(); IntegerType *IntType = IntegerType::get(C, bitw); @@ -606,10 +607,11 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { : sizeInBits == 64 ? 53 : sizeInBits == 128 ? 113 : sizeInBits == 16 ? 11 - /* sizeInBits == 80 */ - : 65; + : sizeInBits == 80 ? 65 + : sizeInBits - 8; - const unsigned shiftR_exponent = precision - 1; + const unsigned shiftR_exponent = precision - 1; + // BUG FIXME TODO: u64 does not work for > 64 bit ... e.g. 80 and 128 bit const unsigned long long mask_fraction = (1ULL << (shiftR_exponent - 1)) | ((1ULL << (shiftR_exponent - 1)) - 1); const unsigned long long mask_exponent = @@ -1300,12 +1302,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { case 64: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1313,12 +1312,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 32: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1326,13 +1322,10 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 16: count += splitIntCompares(M, bitw); - /* - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << - count - << " split\n"; - */ - bitw >>= 1; + if (debug) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << count + << " split\n"; + // bitw >>= 1; break; default: diff --git a/src/afl-cc.c b/src/afl-cc.c index 76f4a437..0ae401e7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -315,16 +315,9 @@ 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; - u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); - name = strrchr(argv[0], '/'); - if (!name) - name = argv[0]; - else - ++name; - if (lto_mode) { if (lto_flag[0] != '-') diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 40ba20c7..702e732d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -1242,7 +1242,7 @@ static void link_or_copy(u8 *old_path, u8 *new_path) { void pivot_inputs(afl_state_t *afl) { - struct queue_entry *q = afl->queue; + struct queue_entry *q; u32 id = 0, i; ACTF("Creating hard links for all input files..."); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index f619a6d3..002929c5 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1415,7 +1415,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 { @@ -1473,7 +1473,7 @@ static void try_to_add_to_dictN(afl_state_t *afl, u128 v, u8 size) { } else if (b[k] == 0xff) { - ++cons_0; + ++cons_ff; } else { @@ -2410,7 +2410,21 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { // 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))) { return 1; } + 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; + + } + if (unlikely(!afl->orig_cmp_map)) { afl->orig_cmp_map = ck_alloc_nozero(sizeof(struct cmp_map)); @@ -2419,7 +2433,20 @@ u8 input_to_state_stage(afl_state_t *afl, u8 *orig_buf, u8 *buf, u32 len) { 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))) { return 1; } + if (unlikely(common_fuzz_cmplog_stuff(afl, buf, len))) { + + afl->queue_cur->colorized = CMPLOG_LVL_MAX; + while (taint) { + + t = taint->next; + ck_free(taint); + taint = t; + + } + + return 1; + + } #ifdef _DEBUG dump("ORIG", orig_buf, len); @@ -2530,7 +2557,6 @@ exit_its: afl->queue_cur->colorized = CMPLOG_LVL_MAX; ck_free(afl->queue_cur->cmplog_colorinput); - t = taint; while (taint) { t = taint->next; -- cgit 1.4.1 From 1ba5d1008e749a12ac338f57adbac3b2b4cf9ea9 Mon Sep 17 00:00:00 2001 From: aflpp Date: Sat, 13 Feb 2021 10:53:40 +0100 Subject: fuck you llvm 13 --- src/afl-cc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 0ae401e7..9d88f262 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -554,6 +554,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { } +#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); -- cgit 1.4.1 From 9bd1e19d7f004b4da6a610b07e59f99d66bb7ec2 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 13 Feb 2021 22:43:56 +0100 Subject: added AFL_IGNORE_UNKNOWN_ENVS --- docs/Changelog.md | 3 ++- docs/env_variables.md | 5 ++++- include/envs.h | 1 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 2 +- src/afl-cc.c | 1 + src/afl-common.c | 4 ++-- src/afl-fuzz.c | 1 + src/afl-showmap.c | 5 +++-- 8 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 895ab845..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -61,7 +61,8 @@ sending a mail to . - Added a new example harness to compare python, c, and rust bindings - afl-cmin and afl-showmap now support the -f option - changed default: no memory limit for afl-cmin and afl-cmin.bash - - warn on any _AFL and __AFL env vars + - warn on any _AFL and __AFL env vars. + - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) diff --git a/docs/env_variables.md b/docs/env_variables.md index 886669ad..f7745247 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -5,6 +5,10 @@ users or for some types of custom fuzzing setups. See [README.md](README.md) for the general instruction manual. + Note that most tools will warn on any unknown AFL environment variables. + This is for warning on typos that can happen. If you want to disable this + check then set the `AFL_IGNORE_UNKNOWN_ENVS` environment variable. + ## 1) Settings for all compilers Starting with afl++ 3.0 there is only one compiler: afl-cc @@ -18,7 +22,6 @@ To select the different instrumentation modes this can be done by `MODE` can be one of `LTO` (afl-clang-lto*), `LLVM` (afl-clang-fast*), `GCC_PLUGIN` (afl-g*-fast) or `GCC` (afl-gcc/afl-g++). - Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: diff --git a/include/envs.h b/include/envs.h index 210b34a6..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -61,6 +61,7 @@ static char *afl_environment_variables[] = { "AFL_FORKSRV_INIT_TMOUT", "AFL_HARDEN", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", + "AFL_IGNORE_UNKNOWN_ENVS", "AFL_IMPORT_FIRST", "AFL_INST_LIBS", "AFL_INST_RATIO", diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 80c8f917..9b1351b0 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -1138,7 +1138,7 @@ void ModuleSanitizerCoverage::InjectTraceForGep( IRBuilder<> IRB(GEP); for (Use &Idx : GEP->indices()) if (!isa(Idx) && Idx->getType()->isIntegerTy()) - IRB.CreateCall(SanCovTraceGepFunction, + IRB.CreateCall(SanCovTraceGepFunction, {IRB.CreateIntCast(Idx, IntptrTy, true)}); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 9d88f262..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1587,6 +1587,7 @@ int main(int argc, char **argv, char **envp) { "libtokencap.so)\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" diff --git a/src/afl-common.c b/src/afl-common.c index 1cc7f462..589aac71 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -523,7 +523,7 @@ 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 || strncmp(env, "_ALF", 4) == 0 || @@ -582,7 +582,7 @@ void check_environment_vars(char **envp) { } - if (match == 0) { + if (match == 0 && !ignore) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e4139857..e0ac8840 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -198,6 +198,7 @@ static void usage(u8 *argv0, int more_help) { "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" diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0d2c5ceb..b40527d3 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1168,8 +1168,9 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = at_file ? strdup(at_file) : (char *) - 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; -- cgit 1.4.1 From e3a5c31307f323452dc4b5288e0d19a02b596a33 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Feb 2021 13:25:15 +0100 Subject: llvm bug workaround for lto extint --- docs/Changelog.md | 1 + include/envs.h | 1 + instrumentation/cmplog-instructions-pass.cc | 39 ++++++++++++++++++++++++++--- qemu_mode/libqasan/dlmalloc.c | 5 ++++ src/afl-cc.c | 2 ++ src/afl-fuzz-redqueen.c | 1 + 6 files changed, 45 insertions(+), 4 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 71ef4c2c..e2482f8f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,6 +49,7 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 + - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 4313e053..36667ebc 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,6 +16,7 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { + "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index b5cc1882..6b071b48 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -113,6 +113,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); + char *is_lto = getenv("_AFL_LTO_COMPILE"); + #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -265,10 +267,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16 || max_size % 8) { + if (!SI->getNumCases() || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { - // if (!be_quiet) errs() << "skip trivial switch..\n"; - continue; + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } } @@ -285,6 +297,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -301,6 +314,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -540,7 +554,22 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size % 8 || max_size < 16) { continue; } + if (!max_size || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + } if (max_size > 128) { @@ -552,6 +581,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -568,6 +598,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index bace0ff6..aff58ad5 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,6 +3917,7 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4128,6 +4129,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4144,6 +4146,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4156,12 +4159,14 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index d41f79a2..959c9a6f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,6 +1875,8 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); + if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } + if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 527feef5..2b01ecad 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1533,6 +1533,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } + #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 6caec2169cef890ba8a62715c2c26cc0608626e3 Mon Sep 17 00:00:00 2001 From: Michael Rodler Date: Mon, 15 Feb 2021 19:14:28 +0100 Subject: Revert "llvm bug workaround for lto extint" This reverts commit e3a5c31307f323452dc4b5288e0d19a02b596a33. --- docs/Changelog.md | 1 - include/envs.h | 1 - instrumentation/cmplog-instructions-pass.cc | 39 +++-------------------------- qemu_mode/libqasan/dlmalloc.c | 5 ---- src/afl-cc.c | 2 -- src/afl-fuzz-redqueen.c | 1 - 6 files changed, 4 insertions(+), 45 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index e2482f8f..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,7 +49,6 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 - - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 36667ebc..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,7 +16,6 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { - "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 9cd99f85..50ade9fd 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -114,8 +114,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); - char *is_lto = getenv("_AFL_LTO_COMPILE"); - #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -268,20 +266,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { + if (!SI->getNumCases() || max_size < 16 || max_size % 8) { - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } + // if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; } @@ -298,7 +286,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -315,7 +302,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -504,22 +490,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { - - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } - - } + if (!max_size || max_size % 8 || max_size < 16) { continue; } if (max_size > 128) { @@ -531,7 +502,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -548,7 +518,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index aff58ad5..bace0ff6 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,7 +3917,6 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ - \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4129,7 +4128,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4146,7 +4144,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4159,14 +4156,12 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index 959c9a6f..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,8 +1875,6 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); - if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } - if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index cf65d3c1..275af9c8 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1547,7 +1547,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } - #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From ac9cfd89dae0ce99df500793b3f5cbed2fd2e4f7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 23:27:07 +0100 Subject: how to compare afl++ --- README.md | 7 +++++++ src/afl-cc.c | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/README.md b/README.md index d37826b5..59b1c143 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ If you want to use afl++ for your academic work, check the [papers page](https://aflplus.plus/papers/) on the website. To cite our work, look at the [Cite](#cite) section. + For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` + with `AFL_LLVM_CMPLOG=1`. ## Major changes in afl++ 3.0 @@ -1170,6 +1172,11 @@ Thank you! If you use AFLplusplus in scientific work, consider citing [our paper](https://www.usenix.org/conference/woot20/presentation/fioraldi) presented at WOOT'20: +If you use AFLpluplus to compare to your work, please use either `afl-clang-lto` +or `afl-clang-fast` with `AFL_LLVM_CMPLOG=1` for building targets and +`afl-fuzz` with the command line option `-l 2` for fuzzing. +The most effective setup is the `aflplusplus` default fuzzer on Google's fuzzbench. + + Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. “AFL++: Combining incremental steps of fuzzing research”. In 14th USENIX Workshop on Offensive Technologies (WOOT 20). USENIX Association, Aug. 2020. Bibtex: diff --git a/src/afl-cc.c b/src/afl-cc.c index d41f79a2..a5e54691 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1699,7 +1699,9 @@ int main(int argc, char **argv, char **envp) { "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\n"); + "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); -- cgit 1.4.1 From 8ad78f5b65d09375dddfa679ccce76bec0862c28 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 21 Feb 2021 23:42:08 +0100 Subject: fix --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index a5e54691..2eb8c575 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1699,9 +1699,9 @@ int main(int argc, char **argv, char **envp) { "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"); + "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" + "AFL_LLVM_CMPLOG and AFL_LLVM_DICT2FILE.\n\n"); exit(1); -- cgit 1.4.1 From 70fe872940b9815698b4317bdde33da1dae27923 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 16:39:38 +0100 Subject: ensure a valid seed exists --- instrumentation/afl-compiler-rt.o.c | 2 ++ src/afl-cc.c | 3 ++- src/afl-fuzz.c | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 324d541d..e4aeadfa 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1675,6 +1675,7 @@ void __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring, u8 *cstring) { if (unlikely(!__afl_cmp_map)) return; if (!area_is_mapped(stdstring, 32) || !area_is_mapped(cstring, 32)) return; + __cmplog_rtn_hook(get_llvm_stdstring(stdstring), cstring); } @@ -1684,6 +1685,7 @@ void __cmplog_rtn_llvm_stdstring_stdstring(u8 *stdstring1, u8 *stdstring2) { if (unlikely(!__afl_cmp_map)) return; if (!area_is_mapped(stdstring1, 32) || !area_is_mapped(stdstring2, 32)) return; + __cmplog_rtn_hook(get_llvm_stdstring(stdstring1), get_llvm_stdstring(stdstring2)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 2eb8c575..5f471355 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1700,7 +1700,8 @@ int main(int argc, char **argv, char **envp) { "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" + "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); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 24d77cc9..9137dc23 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1707,7 +1707,12 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); - if (!afl->pending_not_fuzzed) { + // 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!"); -- cgit 1.4.1 From 7674dac1a16a9963e22eb628fdbd409befe19979 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 18:17:35 +0100 Subject: auto mode for CTX + NGRAM --- src/afl-cc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 5f471355..783cf23e 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1421,6 +1421,14 @@ int main(int argc, char **argv, char **envp) { } + 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 -- cgit 1.4.1 From 745bc083d144a31981b3686d68bc34777758b359 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 18:22:09 +0100 Subject: fix error msg --- src/afl-cc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 783cf23e..c3910e6d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1802,8 +1802,8 @@ int main(int argc, char **argv, char **envp) { if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && instrument_mode != INSTRUMENT_CFG) FATAL( - "CTX and NGRAM instrumentation options can only be used with CFG " - "(recommended) and CLASSIC instrumentation modes!"); + "CTX and NGRAM instrumentation options can only be used with LLVM and " + "CFG or CLASSIC instrumentation modes!"); if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) FATAL( -- cgit 1.4.1 From 1e76079e93f5b4a9729367dd982d632013669bc5 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 11:32:32 +0100 Subject: llvm mode CALLER mode --- instrumentation/README.ctx.md | 22 +++++++++++++++++++--- instrumentation/afl-llvm-pass.so.cc | 18 ++++++++++-------- src/afl-cc.c | 37 +++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 23 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/README.ctx.md b/instrumentation/README.ctx.md index caf2c09a..ffcce0a9 100644 --- a/instrumentation/README.ctx.md +++ b/instrumentation/README.ctx.md @@ -4,14 +4,19 @@ This is an LLVM-based implementation of the context sensitive branch coverage. -Basically every function gets its own ID and that ID is combined with the -edges of the called functions. +Basically every function gets its own ID and, every time that an edge is logged, +all the IDs in the callstack are hashed and combined with the edge transition +hash to augment the classic edge coverage with the information about the +calling context. So if both function A and function B call a function C, the coverage collected in C will be different. In math the coverage is collected as follows: -`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` +`map[current_location_ID ^ previous_location_ID >> 1 ^ hash_callstack_IDs] += 1` + +The callstack hash is produced XOR-ing the function IDs to avoid explosion with +recusrsive functions. ## Usage @@ -20,3 +25,14 @@ Set the `AFL_LLVM_INSTRUMENT=CTX` or `AFL_LLVM_CTX=1` environment variable. It is highly recommended to increase the MAP_SIZE_POW2 definition in config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. + +## Caller Branch Coverage + +If the context sensitive coverage introduces too may collisions becoming +decremental, the user can choose to augment edge coverage with just the +called function ID, instead of the entire callstack hash. + +In math the coverage is collected as follows: +`map[current_location_ID ^ previous_location_ID >> 1 ^ previous_callee_ID] += 1` + +Set the `AFL_LLVM_INSTRUMENT=CALLER` or `AFL_LLVM_CALLER=1` environment variable. diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 87267e35..d06d3201 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -84,7 +84,7 @@ class AFLCoverage : public ModulePass { uint32_t ngram_size = 0; uint32_t map_size = MAP_SIZE; uint32_t function_minimum_size = 1; - char * ctx_str = NULL, *skip_nozero = NULL; + char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL; }; @@ -187,6 +187,7 @@ bool AFLCoverage::runOnModule(Module &M) { char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); ctx_str = getenv("AFL_LLVM_CTX"); + caller_str = getenv("AFL_LLVM_CALLER"); #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ @@ -240,7 +241,7 @@ bool AFLCoverage::runOnModule(Module &M) { GlobalVariable *AFLPrevLoc; GlobalVariable *AFLContext = NULL; - if (ctx_str) + if (ctx_str || caller_str) #if defined(__ANDROID__) || defined(__HAIKU__) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); @@ -318,7 +319,7 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if ((ctx_str || caller_str) && &BB == &F.getEntryBlock()) { // load the context ID of the previous function and write to to a local // variable on the stack @@ -354,8 +355,9 @@ bool AFLCoverage::runOnModule(Module &M) { // if yes we store a context ID for this function in the global var if (has_calls) { - Value *NewCtx = IRB.CreateXor( - PrevCtx, ConstantInt::get(Int32Ty, AFL_R(map_size))); + Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); + if (ctx_str) + NewCtx = IRB.CreateXor(PrevCtx, NewCtx); StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); @@ -412,7 +414,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if (ctx_str && has_calls) { + if ((ctx_str || caller_str) && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { @@ -459,7 +461,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif PrevLocTrans = PrevLoc; - if (ctx_str) + if (ctx_str || caller_str) PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); else @@ -546,7 +548,7 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if (ctx_str && has_calls) { + if ((ctx_str || caller_str) && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { diff --git a/src/afl-cc.c b/src/afl-cc.c index c3910e6d..9cf02059 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -73,7 +73,8 @@ enum { INSTRUMENT_GCC = 6, INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, - INSTRUMENT_OPT_NGRAM = 16 + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, }; @@ -1273,7 +1274,8 @@ int main(int argc, char **argv, char **envp) { } 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; @@ -1387,6 +1389,13 @@ int main(int argc, char **argv, char **envp) { 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) { @@ -1420,6 +1429,11 @@ int main(int argc, char **argv, char **envp) { } } + + 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_mode == INSTRUMENT_DEFAULT && (compiler_mode == LLVM || compiler_mode == UNSET)) { @@ -1770,7 +1784,7 @@ int main(int argc, char **argv, char **envp) { } if (instrument_opt_mode && compiler_mode != LLVM) - FATAL("CTX and NGRAM can only be used in LLVM mode"); + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); if (!instrument_opt_mode) { @@ -1780,15 +1794,14 @@ int main(int argc, char **argv, char **envp) { } else { - if (instrument_opt_mode == INSTRUMENT_OPT_CTX) - - ptr = alloc_printf("%s + CTX", instrument_mode_string[instrument_mode]); - else if (instrument_opt_mode == INSTRUMENT_OPT_NGRAM) - ptr = alloc_printf("%s + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); - else - ptr = alloc_printf("%s + CTX + NGRAM-%u", - instrument_mode_string[instrument_mode], ngram_size); + char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); + ptr = alloc_printf("%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 : "" + ); + + ck_free(ptr2); } -- cgit 1.4.1 From 8f538e77ed75256466e8b97d43d0c32948cb9931 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 11:33:51 +0100 Subject: code format --- instrumentation/afl-llvm-pass.so.cc | 5 ++--- src/afl-cc.c | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index d06d3201..33898aec 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -356,9 +356,8 @@ bool AFLCoverage::runOnModule(Module &M) { if (has_calls) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); - if (ctx_str) - NewCtx = IRB.CreateXor(PrevCtx, NewCtx); - StoreInst * StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 9cf02059..0c689286 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1275,7 +1275,7 @@ int main(int argc, char **argv, char **envp) { 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; @@ -1389,7 +1389,7 @@ int main(int argc, char **argv, char **envp) { setenv("AFL_LLVM_CTX", "1", 1); } - + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CALLER; @@ -1429,10 +1429,12 @@ int main(int argc, char **argv, char **envp) { } } - + 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_mode == INSTRUMENT_DEFAULT && @@ -1795,12 +1797,12 @@ int main(int argc, char **argv, char **envp) { } else { char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); - ptr = alloc_printf("%s%s%s%s", instrument_mode_string[instrument_mode], + ptr = alloc_printf( + "%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_NGRAM) ? ptr2 : ""); + ck_free(ptr2); } -- cgit 1.4.1 From 96c526cb78512737a980726dd32c95593edb8cd1 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 14:04:40 +0100 Subject: fix caller/ctx change, support dlopen in afl-compiler-rt --- docs/Changelog.md | 10 ++++ instrumentation/LLVMInsTrim.so.cc | 29 ++++----- instrumentation/afl-compiler-rt.o.c | 114 +++++++++++++++++++++++++++++++----- src/afl-cc.c | 31 ++++++---- 4 files changed, 144 insertions(+), 40 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1be41267..6fe3517a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,16 @@ sending a mail to . - afl-cc - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) + - handle erroneous setups in which multiple afl-compiler-rt are + compiled into the target. This now also supports dlopen instrumented + libs loaded before the forkserver. + - Renamed CTX to CALLER, added correct/real CTX implemenation to CLASSIC + - qemu_mode + - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! + - if no new/updated checkout is wanted, build with: + NO_CHECKOUT=1 ./build_qemu_support.sh + - we no longer perform a "git drop" + ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 948f8f3a..f0de6536 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -135,7 +135,7 @@ struct InsTrim : public ModulePass { unsigned int PrevLocSize = 0; char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); - char *ctx_str = getenv("AFL_LLVM_CTX"); + char *caller_str = getenv("AFL_LLVM_CALLER"); #ifdef AFL_HAVE_VECTOR_INTRINSICS unsigned int ngram_size = 0; @@ -197,9 +197,9 @@ struct InsTrim : public ModulePass { GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; GlobalVariable *AFLContext = NULL; - LoadInst * PrevCtx = NULL; // for CTX sensitive coverage + LoadInst * PrevCaller = NULL; // for CALLER sensitive coverage - if (ctx_str) + if (caller_str) #if defined(__ANDROID__) || defined(__HAIKU__) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); @@ -398,11 +398,11 @@ struct InsTrim : public ModulePass { unsigned int cur_loc; // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if (caller_str && &BB == &F.getEntryBlock()) { - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); + PrevCaller = IRB.CreateLoad(AFLContext); + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); // does the function have calls? and is any of the calls larger than // one basic block? @@ -441,7 +441,7 @@ struct InsTrim : public ModulePass { } - } // END of ctx_str + } // END of caller_str if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } @@ -485,9 +485,9 @@ struct InsTrim : public ModulePass { #endif PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); - if (ctx_str) + if (caller_str) PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); + IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCaller), Int32Ty); /* Load SHM pointer */ LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); @@ -535,16 +535,17 @@ struct InsTrim : public ModulePass { IRB.CreateStore(Incr, MapPtrIdx) ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - if (ctx_str && has_calls) { + if (caller_str && has_calls) { - // in CTX mode we have to restore the original context for the + // in CALLER mode we have to restore the original context for the // caller - she might be calling other functions which need the - // correct CTX + // correct CALLER Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + StoreInst * RestoreCtx = + Post_IRB.CreateStore(PrevCaller, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c9577a55..e3aa787f 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -123,6 +123,17 @@ static u8 is_persistent; static u8 _is_sancov; +/* Debug? */ + +static u32 __afl_debug; + +/* Already initialized markers */ + +static u32 __afl_already_initialized_shm; +static u32 __afl_already_initialized_forkserver; +static u32 __afl_already_initialized_first; +static u32 __afl_already_initialized_second; + /* Dummy pipe for area_is_valid() */ static int __afl_dummy_fd[2] = {2, 2}; @@ -176,7 +187,7 @@ static void __afl_map_shm_fuzz() { char *id_str = getenv(SHM_FUZZ_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none"); @@ -222,7 +233,7 @@ static void __afl_map_shm_fuzz() { __afl_fuzz_len = (u32 *)map; __afl_fuzz_ptr = map + sizeof(u32); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n"); @@ -242,7 +253,6 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { - static u32 __afl_already_initialized_shm = 0; if (__afl_already_initialized_shm) return; __afl_already_initialized_shm = 1; @@ -303,7 +313,7 @@ static void __afl_map_shm(void) { early-stage __afl_area_initial region that is needed to allow some really hacky .init code to work correctly in projects such as OpenSSL. */ - if (getenv("AFL_DEBUG")) + if (__afl_debug) fprintf(stderr, "DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " "__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, " @@ -359,17 +369,18 @@ static void __afl_map_shm(void) { } + close(shm_fd); + if (shm_base == MAP_FAILED) { - close(shm_fd); shm_fd = -1; - fprintf(stderr, "mmap() failed\n"); + perror("mmap for map"); + if (__afl_map_addr) send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_MMAP); - perror("mmap for map"); exit(2); @@ -476,7 +487,7 @@ static void __afl_map_shm(void) { id_str = getenv(CMPLOG_SHM_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: cmplog id_str %s\n", id_str == NULL ? "" : id_str); @@ -541,6 +552,58 @@ static void __afl_map_shm(void) { } +/* unmap SHM. */ + +static void __afl_unmap_shm(void) { + + if (!__afl_already_initialized_shm) return; + + char *id_str = getenv(SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_area_ptr, __afl_map_size); + +#else + + shmdt((void *)__afl_area_ptr); + +#endif + + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { + + munmap((void *)__afl_map_addr, __afl_map_size); + + } + + __afl_area_ptr = __afl_area_ptr_dummy; + + id_str = getenv(CMPLOG_SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_cmp_map, __afl_map_size); + +#else + + shmdt((void *)__afl_cmp_map); + +#endif + + __afl_cmp_map = NULL; + + } + + __afl_already_initialized_shm = 0; + +} + #ifdef __linux__ static void __afl_start_snapshots(void) { @@ -569,7 +632,7 @@ static void __afl_start_snapshots(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); } - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -746,7 +809,6 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { - static u32 __afl_already_initialized_forkserver = 0; if (__afl_already_initialized_forkserver) return; __afl_already_initialized_forkserver = 1; @@ -800,7 +862,7 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -1035,7 +1097,7 @@ void __afl_manual_init(void) { __afl_sharedmem_fuzzing = 0; if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_ptr_dummy; - if (getenv("AFL_DEBUG")) + if (__afl_debug) fprintf(stderr, "DEBUG: disabled instrumentation because of " "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); @@ -1079,10 +1141,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { __attribute__((constructor(1))) void __afl_auto_second(void) { - static u32 __afl_already_initialized_second = 0; if (__afl_already_initialized_second) return; __afl_already_initialized_second = 1; + if (getenv("AFL_DEBUG")) { __afl_debug = 1; } + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; u8 *ptr; @@ -1114,7 +1177,6 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { __attribute__((constructor(0))) void __afl_auto_first(void) { - static u32 __afl_already_initialized_first = 0; if (__afl_already_initialized_first) return; __afl_already_initialized_first = 1; @@ -1198,7 +1260,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { _is_sancov = 1; - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", @@ -1235,6 +1297,28 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + if (__afl_debug) { + + fprintf(stderr, + "Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n", + __afl_final_loc); + + } + + if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) { + + if (__afl_debug) { + + fprintf(stderr, "Reinit shm necessary (+%u)\n", + __afl_final_loc - __afl_map_size); + + } + + __afl_unmap_shm(); + __afl_map_shm(); + + } + } ///// CmpLog instrumentation diff --git a/src/afl-cc.c b/src/afl-cc.c index 0c689286..ab794877 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -89,7 +89,7 @@ char instrument_mode_string[18][18] = { "GCC", "CLANG", "CTX", - "", + "CALLER", "", "", "", @@ -1514,12 +1514,13 @@ int main(int argc, char **argv, char **envp) { " 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" - " - CTX\n" + " - CALLER\n" " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes no no no " @@ -1566,7 +1567,10 @@ int main(int argc, char **argv, char **envp) { NATIVE_MSG " CLASSIC: decision target instrumentation (README.llvm.md)\n" - " CTX: CLASSIC + callee context (instrumentation/README.ctx.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) " @@ -1660,15 +1664,17 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " - "NGRAM-16\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_CTX: use context sensitive coverage (for CLASSIC and " - "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"); @@ -1814,11 +1820,14 @@ int main(int argc, char **argv, char **envp) { "(requires LLVM 11 or higher)"); #endif - if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && - instrument_mode != INSTRUMENT_CFG) + 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( - "CTX and NGRAM instrumentation options can only be used with LLVM and " - "CFG or CLASSIC instrumentation modes!"); + "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( -- cgit 1.4.1 From 0aa93afeb82c610fd39b5dd4a3dd7482b9f86b1e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 14:50:26 +0100 Subject: vectorial top-k CTX first implementation --- instrumentation/LLVMInsTrim.so.cc | 2 +- instrumentation/afl-compiler-rt.o.c | 4 +- instrumentation/afl-llvm-pass.so.cc | 154 ++++++++++++++++++++++++---- instrumentation/llvm-alternative-coverage.h | 21 ++++ instrumentation/llvm-ngram-coverage.h | 18 ---- src/afl-cc.c | 2 +- 6 files changed, 162 insertions(+), 39 deletions(-) create mode 100644 instrumentation/llvm-alternative-coverage.h delete mode 100644 instrumentation/llvm-ngram-coverage.h (limited to 'src/afl-cc.c') diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 948f8f3a..83f11411 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -38,7 +38,7 @@ typedef long double max_align_t; #include "MarkNodes.h" #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include "config.h" #include "debug.h" diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c9577a55..d583e246 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -20,7 +20,7 @@ #include "config.h" #include "types.h" #include "cmplog.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include #include @@ -97,10 +97,12 @@ int __afl_selective_coverage_temp = 1; #if defined(__ANDROID__) || defined(__HAIKU__) PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; u32 __afl_prev_ctx; u32 __afl_cmp_counter; #else __thread PREV_LOC_T __afl_prev_loc[NGRAM_SIZE_MAX]; +__thread PREV_LOC_T __afl_prev_caller[CTX_MAX_K]; __thread u32 __afl_prev_ctx; __thread u32 __afl_cmp_counter; #endif diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 33898aec..c58e9d95 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -62,7 +62,7 @@ typedef long double max_align_t; #endif #include "afl-llvm-common.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" using namespace llvm; @@ -82,6 +82,7 @@ class AFLCoverage : public ModulePass { protected: uint32_t ngram_size = 0; + uint32_t ctx_k = 0; uint32_t map_size = MAP_SIZE; uint32_t function_minimum_size = 1; char * ctx_str = NULL, *caller_str = NULL, *skip_nozero = NULL; @@ -183,12 +184,17 @@ bool AFLCoverage::runOnModule(Module &M) { skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); unsigned PrevLocSize = 0; + unsigned PrevCallerSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); + char *ctx_k_str = getenv("AFL_LLVM_CTX_K"); + if (!ctx_k_str) ctx_k_str = getenv("AFL_CTX_K"); ctx_str = getenv("AFL_LLVM_CTX"); caller_str = getenv("AFL_LLVM_CALLER"); + bool instrument_ctx = ctx_str || caller_str; + #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Decide previous location vector size (must be a power of two) */ VectorType *PrevLocTy = NULL; @@ -205,6 +211,25 @@ bool AFLCoverage::runOnModule(Module &M) { if (ngram_size) PrevLocSize = ngram_size - 1; else + PrevLocSize = 1; + + /* Decide K-ctx vector size (must be a power of two) */ + VectorType *PrevCallerTy = NULL; + + if (ctx_k_str) + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || + ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", CTX_MAX_K); + + if (ctx_k == 1) { + ctx_k = 0; + instrument_ctx = true; + caller_str = ctx_k_str; // Enable CALLER instead + } + if (ctx_k) { + PrevCallerSize = ctx_k; + instrument_ctx = true; + } #else if (ngram_size_str) #ifndef LLVM_VERSION_PATCH @@ -218,8 +243,20 @@ bool AFLCoverage::runOnModule(Module &M) { "%d.%d.%d!", LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); #endif + if (ctx_k_str) + #ifndef LLVM_VERSION_PATCH + FATAL( + "Sorry, K-CTX branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); + #else + FATAL( + "Sorry, K-CTX branch coverage is not supported with llvm version " + "%d.%d.%d!", + LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); + #endif + PrevLocSize = 1; #endif - PrevLocSize = 1; #ifdef AFL_HAVE_VECTOR_INTRINSICS int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); @@ -232,6 +269,17 @@ bool AFLCoverage::runOnModule(Module &M) { ); #endif +#ifdef AFL_HAVE_VECTOR_INTRINSICS + int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize); + if (ctx_k) + PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); +#endif + /* Get globals for the SHM region and the previous location. Note that __afl_prev_loc is thread-local. */ @@ -239,6 +287,7 @@ bool AFLCoverage::runOnModule(Module &M) { new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; + GlobalVariable *AFLPrevCaller; GlobalVariable *AFLContext = NULL; if (ctx_str || caller_str) @@ -275,6 +324,30 @@ bool AFLCoverage::runOnModule(Module &M) { GlobalVariable::GeneralDynamicTLSModel, 0, false); #endif +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + #if defined(__ANDROID__) || defined(__HAIKU__) + AFLPrevCaller = new GlobalVariable( + M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_caller"); + #else + AFLPrevCaller = new GlobalVariable( + M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, + /* Initializer */ nullptr, "__afl_prev_caller", + /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, + /* AddressSpace */ 0, /* IsExternallyInitialized */ false); + #endif + else +#endif +#if defined(__ANDROID__) || defined(__HAIKU__) + AFLPrevCaller = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller"); +#else + AFLPrevCaller = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", 0, + GlobalVariable::GeneralDynamicTLSModel, 0, false); +#endif + #ifdef AFL_HAVE_VECTOR_INTRINSICS /* Create the vector shuffle mask for updating the previous block history. Note that the first element of the vector will store cur_loc, so just set @@ -289,13 +362,24 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); + + SmallVector PrevCallerShuffle = {UndefValue::get(Int32Ty)}; + + for (unsigned I = 0; I < PrevCallerSize - 1; ++I) + PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I)); + + for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I) + PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize)); + + Constant *PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle); #endif // other constants we need ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - LoadInst *PrevCtx = NULL; // CTX sensitive coverage + Value *PrevCtx = NULL; // CTX sensitive coverage + LoadInst *PrevCaller = NULL; // K-CTX coverage /* Instrument all the things! */ @@ -319,12 +403,21 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> IRB(&(*IP)); // Context sensitive coverage - if ((ctx_str || caller_str) && &BB == &F.getEntryBlock()) { + if (instrument_ctx && &BB == &F.getEntryBlock()) { - // load the context ID of the previous function and write to to a local - // variable on the stack - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + PrevCaller = IRB.CreateLoad(AFLPrevCaller); + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + PrevCtx = IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); + } else +#endif + { + // load the context ID of the previous function and write to to a local variable on the stack + LoadInst* PrevCtxLoad = IRB.CreateLoad(AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + PrevCtx = PrevCtxLoad; + } // does the function have calls? and is any of the calls larger than one // basic block? @@ -356,10 +449,22 @@ bool AFLCoverage::runOnModule(Module &M) { if (has_calls) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); - if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); - StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) { + Value *ShuffledPrevCaller = IRB.CreateShuffleVector( + PrevCaller, UndefValue::get(PrevCallerTy), PrevCallerShuffleMask); + Value *UpdatedPrevCaller = IRB.CreateInsertElement(ShuffledPrevCaller, NewCtx, (uint64_t)0); + + StoreInst * Store = IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); + Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } else +#endif + { + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + } } @@ -413,16 +518,22 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX - if ((ctx_str || caller_str) && has_calls) { + if (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst * RestoreCtx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else +#endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } } @@ -460,7 +571,7 @@ bool AFLCoverage::runOnModule(Module &M) { #endif PrevLocTrans = PrevLoc; - if (ctx_str || caller_str) + if (instrument_ctx) PrevLocTrans = IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); else @@ -547,13 +658,20 @@ bool AFLCoverage::runOnModule(Module &M) { // in CTX mode we have to restore the original context for the caller - // she might be calling other functions which need the correct CTX. // Currently this is only needed for the Ubuntu clang-6.0 bug - if ((ctx_str || caller_str) && has_calls) { + if (instrument_ctx && has_calls) { Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + + StoreInst * RestoreCtx; +#ifdef AFL_HAVE_VECTOR_INTRINSICS + if (ctx_k) + RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); + else +#endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/instrumentation/llvm-alternative-coverage.h b/instrumentation/llvm-alternative-coverage.h new file mode 100644 index 00000000..0d7b3957 --- /dev/null +++ b/instrumentation/llvm-alternative-coverage.h @@ -0,0 +1,21 @@ +#ifndef AFL_NGRAM_CONFIG_H +#define AFL_NGRAM_CONFIG_H + +#include "types.h" + +#if (MAP_SIZE_POW2 <= 16) +typedef u16 PREV_LOC_T; +#elif (MAP_SIZE_POW2 <= 32) +typedef u32 PREV_LOC_T; +#else +typedef u64 PREV_LOC_T; +#endif + +/* Maximum ngram size */ +#define NGRAM_SIZE_MAX 16U + +/* Maximum K for top-K context sensitivity */ +#define CTX_MAX_K 32U + +#endif + diff --git a/instrumentation/llvm-ngram-coverage.h b/instrumentation/llvm-ngram-coverage.h deleted file mode 100644 index 666839c8..00000000 --- a/instrumentation/llvm-ngram-coverage.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AFL_NGRAM_CONFIG_H -#define AFL_NGRAM_CONFIG_H - -#include "types.h" - -#if (MAP_SIZE_POW2 <= 16) -typedef u16 PREV_LOC_T; -#elif (MAP_SIZE_POW2 <= 32) -typedef u32 PREV_LOC_T; -#else -typedef u64 PREV_LOC_T; -#endif - -/* Maximum ngram size */ -#define NGRAM_SIZE_MAX 16U - -#endif - diff --git a/src/afl-cc.c b/src/afl-cc.c index 0c689286..3c96beac 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -22,7 +22,7 @@ #include "types.h" #include "debug.h" #include "alloc-inl.h" -#include "llvm-ngram-coverage.h" +#include "llvm-alternative-coverage.h" #include #include -- cgit 1.4.1 From af9aeb89d43919fb5e538778e5df9bf3ffaba0aa Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Mar 2021 15:26:15 +0100 Subject: afl-cc code for k-ctx --- src/afl-cc.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 3c96beac..97f32b2b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -50,7 +50,7 @@ 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, lto_mode; +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; @@ -75,6 +75,7 @@ enum { INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16, INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, }; @@ -1282,11 +1283,21 @@ int main(int argc, char **argv, char **envp) { 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 " + "K-CTX instrumentation mode must be between 2 and NGRAM_SIZE_MAX " "(%u)", NGRAM_SIZE_MAX); } + + if (getenv("AFL_LLVM_CTX_K")) { + + instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL( + "NGRAM instrumentation mode must be between 1 and CTX_MAX_K (%u)", CTX_MAX_K); + + } if (getenv("AFL_LLVM_INSTRUMENT")) { @@ -1382,6 +1393,32 @@ int main(int argc, char **argv, char **envp) { 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); + 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) { @@ -1437,6 +1474,20 @@ int main(int argc, char **argv, char **envp) { } + 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)) { @@ -1797,13 +1848,18 @@ int main(int argc, char **argv, char **envp) { } 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", instrument_mode_string[instrument_mode], + "%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_NGRAM) ? ptr2 : "", + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : "" + ); ck_free(ptr2); + ck_free(ptr3); } -- cgit 1.4.1 From c429021de10cf01878bd2150cc68c6b403db9335 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 5 Mar 2021 15:27:10 +0100 Subject: fix typos and format --- instrumentation/afl-llvm-pass.so.cc | 99 ++++++++++++++++++++++++------------- src/afl-cc.c | 16 +++--- 2 files changed, 72 insertions(+), 43 deletions(-) (limited to 'src/afl-cc.c') diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index fbf55f81..f4717345 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -212,24 +212,30 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocSize = ngram_size - 1; else PrevLocSize = 1; - + /* Decide K-ctx vector size (must be a power of two) */ VectorType *PrevCallerTy = NULL; if (ctx_k_str) - if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || - ctx_k > CTX_MAX_K) - FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", CTX_MAX_K); + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", + CTX_MAX_K); if (ctx_k == 1) { + ctx_k = 0; instrument_ctx = true; - caller_str = ctx_k_str; // Enable CALLER instead + caller_str = ctx_k_str; // Enable CALLER instead + } + if (ctx_k) { + PrevCallerSize = ctx_k; instrument_ctx = true; + } + #else if (ngram_size_str) #ifndef LLVM_VERSION_PATCH @@ -274,8 +280,8 @@ bool AFLCoverage::runOnModule(Module &M) { if (ctx_k) PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize #if LLVM_VERSION_MAJOR >= 12 - , - false + , + false #endif ); #endif @@ -340,12 +346,13 @@ bool AFLCoverage::runOnModule(Module &M) { else #endif #if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevCaller = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller"); + AFLPrevCaller = + new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, + "__afl_prev_caller"); #else AFLPrevCaller = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", + 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); #endif #ifdef AFL_HAVE_VECTOR_INTRINSICS @@ -362,11 +369,12 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); - - Constant *PrevCallerShuffleMask = NULL; + + Constant * PrevCallerShuffleMask = NULL; SmallVector PrevCallerShuffle = {UndefValue::get(Int32Ty)}; if (ctx_k) { + for (unsigned I = 0; I < PrevCallerSize - 1; ++I) PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I)); @@ -374,15 +382,17 @@ bool AFLCoverage::runOnModule(Module &M) { PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize)); PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle); + } + #endif // other constants we need ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - Value *PrevCtx = NULL; // CTX sensitive coverage - LoadInst *PrevCaller = NULL; // K-CTX coverage + Value * PrevCtx = NULL; // CTX sensitive coverage + LoadInst *PrevCaller = NULL; // K-CTX coverage /* Instrument all the things! */ @@ -410,16 +420,25 @@ bool AFLCoverage::runOnModule(Module &M) { #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) { + PrevCaller = IRB.CreateLoad(AFLPrevCaller); - PrevCaller->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - PrevCtx = IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); - } else + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + PrevCtx = + IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); + + } else + #endif { - // load the context ID of the previous function and write to to a local variable on the stack - LoadInst* PrevCtxLoad = IRB.CreateLoad(AFLContext); - PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + + // load the context ID of the previous function and write to to a + // local variable on the stack + LoadInst *PrevCtxLoad = IRB.CreateLoad(AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); PrevCtx = PrevCtxLoad; + } // does the function have calls? and is any of the calls larger than one @@ -454,19 +473,28 @@ bool AFLCoverage::runOnModule(Module &M) { Value *NewCtx = ConstantInt::get(Int32Ty, AFL_R(map_size)); #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) { + Value *ShuffledPrevCaller = IRB.CreateShuffleVector( - PrevCaller, UndefValue::get(PrevCallerTy), PrevCallerShuffleMask); - Value *UpdatedPrevCaller = IRB.CreateInsertElement(ShuffledPrevCaller, NewCtx, (uint64_t)0); + PrevCaller, UndefValue::get(PrevCallerTy), + PrevCallerShuffleMask); + Value *UpdatedPrevCaller = IRB.CreateInsertElement( + ShuffledPrevCaller, NewCtx, (uint64_t)0); + + StoreInst *Store = + IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); + Store->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + + } else - StoreInst * Store = IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); - Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } else #endif { + if (ctx_str) NewCtx = IRB.CreateXor(PrevCtx, NewCtx); StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); StoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } } @@ -528,15 +556,16 @@ bool AFLCoverage::runOnModule(Module &M) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx; -#ifdef AFL_HAVE_VECTOR_INTRINSICS + StoreInst *RestoreCtx; + #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else -#endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + else + #endif + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); + } } @@ -667,14 +696,14 @@ bool AFLCoverage::runOnModule(Module &M) { if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - - StoreInst * RestoreCtx; + + StoreInst *RestoreCtx; #ifdef AFL_HAVE_VECTOR_INTRINSICS if (ctx_k) RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else + else #endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/src/afl-cc.c b/src/afl-cc.c index 97f32b2b..e4ea66e4 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1283,19 +1283,19 @@ int main(int argc, char **argv, char **envp) { ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) FATAL( - "K-CTX instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " "(%u)", NGRAM_SIZE_MAX); } - + if (getenv("AFL_LLVM_CTX_K")) { instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); if (ctx_k < 1 || ctx_k > CTX_MAX_K) - FATAL( - "NGRAM instrumentation mode must be between 1 and CTX_MAX_K (%u)", CTX_MAX_K); + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); } @@ -1393,7 +1393,7 @@ int main(int argc, char **argv, char **envp) { compiler_mode = CLANG; } - + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0) { u8 *ptr3 = ptr2 + strlen("ctx-"); @@ -1412,7 +1412,8 @@ int main(int argc, char **argv, char **envp) { 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)", + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", CTX_MAX_K); instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); u8 *ptr4 = alloc_printf("%u", ctx_k); @@ -1855,8 +1856,7 @@ int main(int argc, char **argv, char **envp) { (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 : "" - ); + (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); ck_free(ptr2); ck_free(ptr3); -- cgit 1.4.1 From 47f2650a32470172a32c4ebd446003cb8a4b80e8 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 16:53:56 +0100 Subject: add AFL_NOOPT --- README.md | 17 +++++++++++++---- docs/env_variables.md | 11 +++++++++++ include/envs.h | 4 +++- src/afl-cc.c | 21 +++++++++++++++++++-- src/afl-common.c | 1 + 5 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/README.md b/README.md index 6b11fee4..d4e39566 100644 --- a/README.md +++ b/README.md @@ -399,10 +399,19 @@ How to do this is described below. Then build the target. (Usually with `make`) -**NOTE**: sometimes configure and build systems are fickle and do not like -stderr output (and think this means a test failure) - which is something -afl++ like to do to show statistics. It is recommended to disable them via -`export AFL_QUIET=1`. +**NOTES** + +1. sometimes configure and build systems are fickle and do not like + stderr output (and think this means a test failure) - which is something + afl++ like to do to show statistics. It is recommended to disable them via + `export AFL_QUIET=1`. + +2. sometimes configure and build systems error on warnings - these should be + disabled (e.g. `--disable-werror` for some configure scripts` + +3. in case the configure/build system complains about afl++'s compiler and + aborts then set `export AFL_NOOPT=1` which will then just behave like the + real compiler. This option has to be unset again before building the target! ##### configure diff --git a/docs/env_variables.md b/docs/env_variables.md index f6ed12d0..a20f1e42 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -26,6 +26,17 @@ Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: + - Some build/configure scripts break with afl++ compilers. To be able to + pass them, do: +``` + export CC=afl-cc + export CXX=afl-c++ + export AFL_NOOPT=1 + ./configure --disable-shared --disabler-werror + unset AFL_NOOPT + make +``` + - Most afl tools do not print any output if stdout/stderr are redirected. If you want to get the output into a file then set the `AFL_DEBUG` environment variable. diff --git a/include/envs.h b/include/envs.h index e8595ef7..37748a56 100644 --- a/include/envs.h +++ b/include/envs.h @@ -119,10 +119,12 @@ static char *afl_environment_variables[] = { "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", - "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_MAX_DET_EXTRAS", + "AFL_NO_X86", // not really an env but we dont want to warn on it + "AFL_NOOPT", + "AFL_PASSTHROUGH", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", diff --git a/src/afl-cc.c b/src/afl-cc.c index bb136fb9..8f9bb397 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1025,7 +1025,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) { - int i; + int i, passthrough = 0; char *callname = argv[0], *ptr = NULL; if (getenv("AFL_DEBUG")) { @@ -1045,6 +1045,13 @@ int main(int argc, char **argv, char **envp) { } + 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); @@ -1665,6 +1672,7 @@ int main(int argc, char **argv, char **envp) { " 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 lik 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" @@ -1977,7 +1985,16 @@ int main(int argc, char **argv, char **envp) { } - execvp(cc_params[0], (char **)cc_params); + 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]); diff --git a/src/afl-common.c b/src/afl-common.c index a306fe5e..68f82a5e 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -682,6 +682,7 @@ void check_environment_vars(char **envp) { env[strlen(afl_environment_variables[i])] == '=') { match = 1; + if ((val = getenv(afl_environment_variables[i])) && !*val) { WARNF( -- cgit 1.4.1 From 0868ea8348ebc682c370d763324b49536ee4c1f7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 17:15:19 +0100 Subject: fix compiler rt on -c --- docs/Changelog.md | 8 +++++++- src/afl-cc.c | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index b47b03ba..c475911d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,9 @@ sending a mail to . - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - afl-cc: + - added AFL_NOOPT that will just pass everything to the normal + gcc/clang compiler without any changes - to pass weird configure + scripts - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) - fixed a crash in LAF transform for empty strings @@ -21,7 +24,10 @@ sending a mail to . compiled into the target. This now also supports dlopen() instrumented libs loaded before the forkserver and even after the forkserver is started (then with collisions though) - - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC + - the compiler rt was added also in object building (-c) which + should have been fixed years ago but somewhere got lost :( + - Renamed CTX to CALLER, added correct/real CTX implementation to + CLASSIC - qemu_mode: - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! - if no new/updated checkout is wanted, build with: diff --git a/src/afl-cc.c b/src/afl-cc.c index 8f9bb397..a517124f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -940,7 +940,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only) { + // 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. @@ -1001,18 +1004,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking && !have_c) + 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__) - if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; + cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; - } #endif -- cgit 1.4.1 From 791c5c171d9e4e7391a9c3760a4a8eb4ce2b4058 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 18:44:42 +0100 Subject: fix ctx-1 --- include/envs.h | 1 + instrumentation/afl-llvm-pass.so.cc | 4 ++-- src/afl-cc.c | 29 +++++++++++++++++++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/envs.h b/include/envs.h index 37748a56..4d4d6b0e 100644 --- a/include/envs.h +++ b/include/envs.h @@ -80,6 +80,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", + "AFL_LLVM_CALLER", "AFL_LLVM_CTX", "AFL_LLVM_CTX_K", "AFL_LLVM_DICT2FILE", diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index f4717345..0f773aba 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -217,8 +217,8 @@ bool AFLCoverage::runOnModule(Module &M) { VectorType *PrevCallerTy = NULL; if (ctx_k_str) - if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 2 || ctx_k > CTX_MAX_K) - FATAL("Bad value of AFL_CTX_K (must be between 2 and CTX_MAX_K (%u))", + if (sscanf(ctx_k_str, "%u", &ctx_k) != 1 || ctx_k < 1 || ctx_k > CTX_MAX_K) + FATAL("Bad value of AFL_CTX_K (must be between 1 and CTX_MAX_K (%u))", CTX_MAX_K); if (ctx_k == 1) { diff --git a/src/afl-cc.c b/src/afl-cc.c index a517124f..b9e0c101 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1298,11 +1298,21 @@ int main(int argc, char **argv, char **envp) { if (getenv("AFL_LLVM_CTX_K")) { - instrument_opt_mode |= INSTRUMENT_OPT_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; + + } } @@ -1422,9 +1432,20 @@ int main(int argc, char **argv, char **envp) { "K-CTX instrumentation option must be between 1 and CTX_MAX_K " "(%u)", CTX_MAX_K); - instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); - u8 *ptr4 = alloc_printf("%u", ctx_k); - setenv("AFL_LLVM_CTX_K", ptr4, 1); + + 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); + + } } -- cgit 1.4.1 From f21a5c42c150fa48099144b937d5ec48fdc7048b Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Mar 2021 00:55:26 +0100 Subject: tiny typo fixed --- src/afl-cc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index b9e0c101..49de08e7 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1693,7 +1693,7 @@ int main(int argc, char **argv, char **envp) { " 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 lik a normal compiler (to pass configure tests)\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" -- cgit 1.4.1 From 851231c846ab4c9fe121f78a5677fa8820e843f3 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Mar 2021 01:15:38 +0100 Subject: fixed scan-build warnings --- src/afl-cc.c | 3 ++- src/afl-fuzz-init.c | 13 +++++++------ src/afl-fuzz.c | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 49de08e7..44654de0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1693,7 +1693,8 @@ int main(int argc, char **argv, char **envp) { " 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_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" diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 2d5f32a7..ca2f75f1 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -828,7 +828,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (unlikely(q->disabled)) { continue; } + if (unlikely(!q || q->disabled)) { continue; } u8 res; s32 fd; @@ -1069,7 +1069,7 @@ void perform_dry_run(afl_state_t *afl) { } afl->max_depth = 0; - for (i = 0; i < afl->queued_paths; i++) { + 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) @@ -1136,10 +1136,11 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { q = afl->queue_buf[idx]; - if (q->disabled || q->cal_failed || !q->exec_cksum) { continue; } + if (!q || q->disabled || q->cal_failed || !q->exec_cksum) { continue; } u32 done = 0; - for (i = idx + 1; i < afl->queued_paths && !done; i++) { + 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; } @@ -1191,7 +1192,7 @@ void perform_dry_run(afl_state_t *afl) { for (idx = 0; idx < afl->queued_paths; idx++) { - if (!afl->queue_buf[idx]->disabled && + 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; @@ -1247,7 +1248,7 @@ void pivot_inputs(afl_state_t *afl) { ACTF("Creating hard links for all input files..."); - for (i = 0; i < afl->queued_paths; i++) { + for (i = 0; i < afl->queued_paths && likely(afl->queue_buf[i]); i++) { q = afl->queue_buf[i]; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9c822d43..065010fa 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1409,6 +1409,7 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("general thread priority settings failed"); } + #endif init_count_class16(); -- cgit 1.4.1 From 7c2436c7114e21f029b06346421a73910deac578 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Mar 2021 19:34:34 +0100 Subject: no runtime for shared linking --- docs/Changelog.md | 4 +++- src/afl-cc.c | 59 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 29 deletions(-) (limited to 'src/afl-cc.c') diff --git a/docs/Changelog.md b/docs/Changelog.md index 17d68b0a..bf04c58e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,9 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.12a (dev) - - fix cmplog rtn (rare crash and not being able to gather ptr data) + - afl-cc: + - fix cmplog rtn (rare crash and not being able to gather ptr data) + - link runtime not to shared libs - qemu_mode (thanks @realmadsci): - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace - add AFL_QEMU_CUSTOM_BIN diff --git a/src/afl-cc.c b/src/afl-cc.c index 44654de0..18401d0d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,56 +959,59 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - switch (bit_mode) { + if (!shared_linking) { - case 0: - 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; + switch (bit_mode) { - case 32: - 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) { + case 0: + 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: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + 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: - 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) { + break; + case 64: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + 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; + } - } + 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 -- cgit 1.4.1 From 413807db01b642a2ad4c70e94517d74c11ace91c Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:25:21 +0100 Subject: lto test --- src/afl-cc.c | 6 ++++-- src/afl-fuzz.c | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 18401d0d..4a724c12 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,11 +959,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - if (!shared_linking) { - 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) @@ -972,6 +971,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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)) @@ -988,6 +988,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { 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)) @@ -1006,6 +1007,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #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 diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 12642557..6c170632 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1633,6 +1633,7 @@ int main(int argc, char **argv_orig, char **envp) { } + 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); -- cgit 1.4.1 From c6f1c56c15801662eef7b0b19e42287ddb3b97a7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 01:27:01 +0100 Subject: fix --- src/afl-cc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index 4a724c12..206066fd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1018,8 +1018,6 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-lrt"; #endif - } - #endif cc_params[cc_par_cnt] = NULL; -- cgit 1.4.1 From 5e2a5f1110e29c36f1c41fb4677ab698c5d571c0 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 10:26:02 +0100 Subject: shmem map size in config.h --- include/config.h | 9 ++++++ instrumentation/afl-compiler-rt.o.c | 6 ++-- src/afl-cc.c | 62 ++++++++++++++++++------------------- src/afl-common.c | 2 +- src/afl-fuzz.c | 24 +++++++++----- 5 files changed, 60 insertions(+), 43 deletions(-) (limited to 'src/afl-cc.c') diff --git a/include/config.h b/include/config.h index b049fee0..29225f6b 100644 --- a/include/config.h +++ b/include/config.h @@ -34,6 +34,15 @@ * * ******************************************************/ +/* Default shared memory map size. Most targets just need a coverage map + between 20-250kb. Plus there is an auto-detection feature in afl-fuzz. + However if a target has problematic constructors and init arrays then + this can fail. Hence afl-fuzz deploys a larger default map. The largest + map seen so far is the xlsx fuzzer for libreoffice which is 5MB. + At runtime this value can be overriden via AFL_MAP_SIZE. + Default: 8MB (defined in bytes) */ +#define DEFAULT_SHMEM_SIZE (8 * 1024 * 1024) + /* CMPLOG/REDQUEEN TUNING * * Here you can modify tuning and solving options for CMPLOG. diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 70148b78..c635ae63 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1774,14 +1774,14 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { */ if (unlikely(!__afl_cmp_map)) return; - //fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); + // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); int l1, l2; if ((l1 = area_is_valid(ptr1, 32)) <= 0 || (l2 = area_is_valid(ptr2, 32)) <= 0) return; int len = MIN(l1, l2); - //fprintf(stderr, "RTN2 %u\n", len); + // fprintf(stderr, "RTN2 %u\n", len); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); k &= CMP_MAP_W - 1; @@ -1812,7 +1812,7 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { ptr1, len); __builtin_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2, len); - //fprintf(stderr, "RTN3\n"); + // fprintf(stderr, "RTN3\n"); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 206066fd..ebbd390c 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,63 +959,63 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - switch (bit_mode) { + switch (bit_mode) { - case 0: - if (!shared_linking) + 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; + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; - case 32: - if (!shared_linking) + 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"); - 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; + break; - case 64: - if (!shared_linking) + 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"); - 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; + break; - } + } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + 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"; + cc_params[cc_par_cnt++] = "-lrt"; #endif #endif diff --git a/src/afl-common.c b/src/afl-common.c index 27b63434..7e56ce3f 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -1072,7 +1072,7 @@ 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 = 8000000; // a very large default map + uint32_t map_size = DEFAULT_SHMEM_SIZE; char * ptr; if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 2fde561c..8318a92e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1527,11 +1527,13 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - if (map_size <= 8000000 && !afl->non_instrumented_mode && + if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); + 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); } @@ -1582,11 +1584,13 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - if (map_size <= 8000000 && !afl->non_instrumented_mode && + if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && !afl->unicorn_mode) { - afl->cmplog_fsrv.map_size = 8000000; // dummy temporary value - setenv("AFL_MAP_SIZE", "8000000", 1); + 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); } @@ -1634,8 +1638,12 @@ int main(int argc, char **argv_orig, char **envp) { } 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); + + 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); -- cgit 1.4.1 From cdd30c766b7102dcd5413efd8f009b4388b40425 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 11:25:14 +0100 Subject: fix m32/m64 --- src/afl-cc.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/afl-cc.c') diff --git a/src/afl-cc.c b/src/afl-cc.c index ebbd390c..c4f8f3fc 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -971,11 +971,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 32: - if (!shared_linking) + 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 (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++] = @@ -988,11 +992,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { break; case 64: - if (!shared_linking) + 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 (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++] = -- cgit 1.4.1 From 513bd703845283c4f94ae2760c48ca8cc38a0076 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 12:44:07 +0100 Subject: shared linking fix --- dynamic_list.txt | 16 ++++++++++++++++ src/afl-cc.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src/afl-cc.c') diff --git a/dynamic_list.txt b/dynamic_list.txt index 3c0b054f..985ec799 100644 --- a/dynamic_list.txt +++ b/dynamic_list.txt @@ -5,6 +5,22 @@ "__afl_auto_init"; "__afl_area_initial"; "__afl_prev_loc"; + "__afl_prev_caller"; + "__afl_prev_ctx"; + "__afl_final_loc"; + "__afl_map_addr"; + "__afl_dictionary"; + "__afl_dictionary_len"; + "__afl_selective_coverage"; + "__afl_selective_coverage_start_off"; + "__afl_selective_coverage_temp;" + "__afl_coverage_discard"; + "__afl_coverage_skip"; + "__afl_coverage_on"; + "__afl_coverage_off"; + "__afl_coverage_interesting"; + "__afl_fuzz_len"; + "__afl_fuzz_ptr"; "__sanitizer_cov_trace_pc_guard"; "__sanitizer_cov_trace_pc_guard_init"; }; diff --git a/src/afl-cc.c b/src/afl-cc.c index c4f8f3fc..e13f285d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -682,17 +682,42 @@ static void edit_params(u32 argc, char **argv, char **envp) { /* 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")) + 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"))) { @@ -1015,7 +1040,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) + if (shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif -- cgit 1.4.1