├── LICENSE ├── afl-as-AFL_INST_RATIO.diff ├── afl-sort-all_uniq-fix.diff ├── llvm_versions_gt_4.diff ├── afl-llvm-fix.diff ├── afl-llvm-fix2.diff ├── afl-qemu-optimize-entrypoint.diff ├── afl-qemu-optimize-map.diff ├── afl-cmin-reduce-dataset.diff ├── afl-qemu-ppc64.diff ├── afl-llvm-optimize.diff ├── afl-fuzz-tmpdir.diff ├── afl-fuzz-fileextensionopt.diff ├── README.md ├── afl-qemu-optimize-logconditional.diff ├── afl-fuzz-context_sensitive.diff ├── afl-fuzz-79x24.diff ├── afl-tmpfs.diff ├── afl-qemu-speed.diff └── laf-intel.diff /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Basically the patches in https://github.com/vanhauser-thc/afl-patches have no license. 3 | They come from various authors and are all very small, hence I am not even sure that copyright could be applied. 4 | As all are destined to be applied to AFL, you should assume the same license as AFL. 5 | So have fun! 6 | 7 | -------------------------------------------------------------------------------- /afl-as-AFL_INST_RATIO.diff: -------------------------------------------------------------------------------- 1 | --- afl-as.c.old 2018-02-11 22:05:48.636563804 +0100 2 | +++ afl-as.c 2018-02-11 22:07:04.035919083 +0100 3 | @@ -525,7 +525,8 @@ int main(int argc, char** argv) { 4 | 5 | if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { 6 | sanitizer = 1; 7 | - inst_ratio /= 3; 8 | + if (!getenv("AFL_INST_RATIO")) 9 | + inst_ratio /= 3; 10 | } 11 | 12 | if (!just_version) add_instrumentation(); 13 | -------------------------------------------------------------------------------- /afl-sort-all_uniq-fix.diff: -------------------------------------------------------------------------------- 1 | --- afl-cmin.old 2018-04-02 10:41:58.642001053 +0200 2 | +++ afl-cmin 2018-04-02 10:42:07.892098892 +0200 3 | @@ -352,7 +352,7 @@ echo 4 | echo "[*] Sorting trace sets (this may take a while)..." 5 | 6 | ls "$IN_DIR" | sed "s#^#$TRACE_DIR/#" | tr '\n' '\0' | xargs -0 -n 1 cat | \ 7 | - sort | uniq -c | sort -n >"$TRACE_DIR/.all_uniq" 8 | + sort | uniq -c | sort -k 1,1 -n >"$TRACE_DIR/.all_uniq" 9 | 10 | TUPLE_COUNT=$((`grep -c . "$TRACE_DIR/.all_uniq"`)) 11 | 12 | -------------------------------------------------------------------------------- /llvm_versions_gt_4.diff: -------------------------------------------------------------------------------- 1 | --- ./llvm_mode/Makefile 2016-06-24 04:38:49.000000000 +0200 2 | +++ ./llvm_mode/Makefile 2019-06-10 21:59:57.584265630 +0200 3 | @@ -36,7 +36,7 @@ 4 | CXXFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign \ 5 | -DVERSION=\"$(VERSION)\" -Wno-variadic-macros 6 | 7 | -CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -fno-rtti -fpic $(CXXFLAGS) 8 | +CLANG_CFL = `$(LLVM_CONFIG) --cxxflags` -Wl,-znodelete -fno-rtti -fpic $(CXXFLAGS) 9 | CLANG_LFL = `$(LLVM_CONFIG) --ldflags` $(LDFLAGS) 10 | 11 | # User teor2345 reports that this is required to make things work on MacOS X. 12 | -------------------------------------------------------------------------------- /afl-llvm-fix.diff: -------------------------------------------------------------------------------- 1 | --- llvm_mode/afl-llvm-rt.o.c.orig 2018-04-16 13:46:15.409800802 +0000 2 | +++ llvm_mode/afl-llvm-rt.o.c 2018-04-16 13:47:04.845566296 +0000 3 | @@ -98,6 +98,8 @@ 4 | s32 child_pid; 5 | 6 | u8 child_stopped = 0; 7 | + 8 | + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); 9 | 10 | /* Phone home and tell the parent that we're OK. If parent isn't there, 11 | assume we're not running in forkserver mode and just execute program. */ 12 | @@ -132,6 +134,7 @@ 13 | /* In child process: close fds, resume execution. */ 14 | 15 | if (!child_pid) { 16 | + signal(SIGCHLD, old_sigchld_handler); 17 | 18 | close(FORKSRV_FD); 19 | close(FORKSRV_FD + 1); 20 | -------------------------------------------------------------------------------- /afl-llvm-fix2.diff: -------------------------------------------------------------------------------- 1 | --- llvm_mode/Makefile.orig 2018-08-16 14:49:37.150145700 +0200 2 | +++ llvm_mode/Makefile 2018-08-16 14:50:27.850146000 +0200 3 | @@ -51,7 +52,7 @@ 4 | 5 | ifeq "$(origin CC)" "default" 6 | CC = clang 7 | - CXX = clang++ 8 | + CXX = g++ 9 | endif 10 | 11 | ifndef AFL_TRACE_PC 12 | --- llvm_mode/afl-clang-fast.c.orig 2018-08-16 14:49:43.093745700 +0200 13 | +++ llvm_mode/afl-clang-fast.c 2018-08-16 14:49:52.781345800 +0200 14 | @@ -208,7 +208,7 @@ 15 | if (!getenv("AFL_DONT_OPTIMIZE")) { 16 | 17 | cc_params[cc_par_cnt++] = "-g"; 18 | - cc_params[cc_par_cnt++] = "-O3"; 19 | + //cc_params[cc_par_cnt++] = "-O3"; 20 | cc_params[cc_par_cnt++] = "-funroll-loops"; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /afl-qemu-optimize-entrypoint.diff: -------------------------------------------------------------------------------- 1 | --- qemu-2.10.0/linux-user/elfload.c.orig 2018-03-16 11:43:21.000000000 +0100 2 | +++ qemu-2.10.0/linux-user/elfload.c 2018-04-04 05:25:47.535020053 +0200 3 | @@ -2086,8 +2086,21 @@ 4 | info->end_data = 0; 5 | info->brk = 0; 6 | info->elf_flags = ehdr->e_flags; 7 | - 8 | - if (!afl_entry_point) afl_entry_point = info->entry; 9 | + 10 | + if (!afl_entry_point) { 11 | + char *ptr; 12 | + if ((ptr = getenv("AFL_ENTRYPOINT")) != NULL) { 13 | + afl_entry_point = strtoul(ptr, NULL, 16); 14 | + } else { 15 | + if (!afl_entry_point) afl_entry_point = info->entry; 16 | + } 17 | +#ifdef TARGET_ARM 18 | + /* The least significant bit indicates Thumb mode. */ 19 | + afl_entry_point = afl_entry_point & ~(target_ulong)1; 20 | +#endif 21 | + if (getenv("AFL_DEBUG") != NULL) 22 | + fprintf(stderr, "AFL forkserver entrypoint: %p\n", (void*)afl_entry_point); 23 | + } while(0); 24 | 25 | for (i = 0; i < ehdr->e_phnum; i++) { 26 | struct elf_phdr *eppnt = phdr + i; 27 | -------------------------------------------------------------------------------- /afl-qemu-optimize-map.diff: -------------------------------------------------------------------------------- 1 | --- ./patches/afl-qemu-cpu-inl.h.orig 2018-04-03 13:15:47.239442416 +0200 2 | +++ ./patches/afl-qemu-cpu-inl.h 2018-04-03 13:42:05.663936006 +0200 3 | @@ -61,7 +63,8 @@ 4 | 5 | /* This is equivalent to afl-as.h: */ 6 | 7 | -static unsigned char *afl_area_ptr; 8 | +static unsigned char dummy[65536]; 9 | +static unsigned char *afl_area_ptr = dummy; 10 | 11 | /* Exported variables populated by the code patched into elfload.c: */ 12 | 13 | @@ -158,12 +161,14 @@ 14 | 15 | 16 | /* Fork server logic, invoked once we hit _start. */ 17 | - 18 | +static int forkserver_installed = 0; 19 | static void afl_forkserver(CPUState *cpu) { 20 | - 21 | static unsigned char tmp[4]; 22 | 23 | - if (!afl_area_ptr) return; 24 | + if (forkserver_installed == 1) 25 | + return; 26 | + forkserver_installed = 1; 27 | + //if (!afl_area_ptr) return; 28 | 29 | /* Tell the parent that we're alive. If the parent doesn't want 30 | to talk, assume that we're not running in forkserver mode. */ 31 | @@ -233,7 +238,7 @@ 32 | /* Optimize for cur_loc > afl_end_code, which is the most likely case on 33 | Linux systems. */ 34 | 35 | - if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr) 36 | + if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) 37 | return; 38 | 39 | /* Looks like QEMU always maps to fixed locations, so ASAN is not a 40 | -------------------------------------------------------------------------------- /afl-cmin-reduce-dataset.diff: -------------------------------------------------------------------------------- 1 | --- afl-cmin 2018-04-02 10:44:34.273922652 +0200 2 | +++ afl-cmin 2018-04-02 11:25:13.310101247 +0200 3 | @@ -363,10 +363,7 @@ echo "[+] Found $TUPLE_COUNT unique tupl 4 | ##################################### 5 | 6 | # The next step is to find the best candidate for each tuple. The "best" 7 | -# part is understood simply as the smallest input that includes a particular 8 | -# tuple in its trace. Empirical evidence suggests that this produces smaller 9 | -# datasets than more involved algorithms that could be still pulled off in 10 | -# a shell script. 11 | +# part is understood simply as the input with the biggest bitmap. 12 | 13 | echo "[*] Finding best candidates for each tuple..." 14 | 15 | @@ -379,7 +376,7 @@ while read -r fn; do 16 | 17 | sed "s#\$# $fn#" "$TRACE_DIR/$fn" >>"$TRACE_DIR/.candidate_list" 18 | 19 | -done < <(ls -rS "$IN_DIR") 20 | +done < <(ls -S "$TRACE_DIR") 21 | 22 | echo 23 | 24 | @@ -387,10 +384,10 @@ echo 25 | # STEP 4: LOADING CANDIDATES # 26 | ############################## 27 | 28 | -# At this point, we have a file of tuple-file pairs, sorted by file size 29 | -# in ascending order (as a consequence of ls -rS). By doing sort keyed 30 | -# only by tuple (-k 1,1) and configured to output only the first line for 31 | -# every key (-s -u), we end up with the smallest file for each tuple. 32 | +# At this point, we have a file of tuple-file pairs, sorted by biggest test case 33 | +# By doing sort keyed only by tuple (-k 1,1) and configured to output only the 34 | +# first line for every key (-s -u), we end up with the file with largest 35 | +# bitmap for each tuple. To the dataset will be small in number of files. 36 | 37 | echo "[*] Sorting candidate list (be patient)..." 38 | 39 | -------------------------------------------------------------------------------- /afl-qemu-ppc64.diff: -------------------------------------------------------------------------------- 1 | --- qemu_mode/patches/elfload.diff.orig 2018-10-12 17:26:36.259207135 +0200 2 | +++ qemu_mode/patches/elfload.diff 2018-10-23 12:49:16.061010377 +0200 3 | @@ -1,5 +1,5 @@ 4 | ---- qemu-2.10.0-rc3-clean/linux-user/elfload.c 2017-08-15 11:39:41.000000000 -0700 5 | -+++ qemu-2.10.0-rc3/linux-user/elfload.c 2017-08-22 14:33:57.397127516 -0700 6 | +--- qemu-2.10.0.orig/linux-user/elfload.c 2017-08-30 18:50:41.000000000 +0200 7 | ++++ qemu-2.10.0/linux-user/elfload.c 2018-10-23 12:48:16.421879765 +0200 8 | @@ -20,6 +20,8 @@ 9 | 10 | #define ELF_OSABI ELFOSABI_SYSV 11 | @@ -30,3 +30,26 @@ 12 | } 13 | } 14 | if (elf_prot & PROT_WRITE) { 15 | +@@ -2443,6 +2449,22 @@ 16 | + info, (elf_interpreter ? &interp_info : NULL)); 17 | + info->start_stack = bprm->p; 18 | + 19 | ++#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) 20 | ++ // On PowerPC64 the entry point is the _function descriptor_ 21 | ++ // of the entry function. For AFL to properly initialize, 22 | ++ // afl_entry_point needs to be set to the actual first instruction 23 | ++ // as opposed executed by the target program. This as opposed to 24 | ++ // where the function's descriptor sits in memory. 25 | ++ 26 | ++ // Shameless copy of PPC init_thread 27 | ++ info_report("Adjusting afl_entry_point"); 28 | ++ if (afl_entry_point && (get_ppc64_abi(info) < 2)) { 29 | ++ uint64_t val; 30 | ++ get_user_u64(val, afl_entry_point); 31 | ++ afl_entry_point = val + info->load_bias; 32 | ++ } 33 | ++#endif 34 | ++ 35 | + /* If we have an interpreter, set that as the program's entry point. 36 | + Copy the load_bias as well, to help PPC64 interpret the entry 37 | + point as a function descriptor. Do this after creating elf tables 38 | -------------------------------------------------------------------------------- /afl-llvm-optimize.diff: -------------------------------------------------------------------------------- 1 | --- llvm_mode/afl-llvm-pass.so.cc.orig 2019-01-03 19:37:23.641280087 +0100 2 | +++ llvm_mode/afl-llvm-pass.so.cc 2019-01-04 10:31:02.999576722 +0100 3 | @@ -31,12 +31,22 @@ 4 | #include 5 | #include 6 | 7 | +#include "llvm/IR/BasicBlock.h" 8 | #include "llvm/ADT/Statistic.h" 9 | #include "llvm/IR/IRBuilder.h" 10 | #include "llvm/IR/LegacyPassManager.h" 11 | #include "llvm/IR/Module.h" 12 | #include "llvm/Support/Debug.h" 13 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 14 | +#include "llvm/IR/BasicBlock.h" 15 | +#include "llvm/ADT/STLExtras.h" 16 | +#include "llvm/IR/Constants.h" 17 | +#include "llvm/IR/Instructions.h" 18 | +#include "llvm/IR/IntrinsicInst.h" 19 | +#include "llvm/IR/LLVMContext.h" 20 | +#include "llvm/IR/Type.h" 21 | +#include "llvm/IR/CFG.h" 22 | +#include 23 | 24 | using namespace llvm; 25 | 26 | @@ -69,6 +79,7 @@ 27 | 28 | IntegerType *Int8Ty = IntegerType::getInt8Ty(C); 29 | IntegerType *Int32Ty = IntegerType::getInt32Ty(C); 30 | + unsigned int cur_loc = 0; 31 | 32 | /* Show a banner */ 33 | 34 | @@ -118,7 +129,32 @@ 35 | 36 | /* Make up cur_loc */ 37 | 38 | - unsigned int cur_loc = AFL_R(MAP_SIZE); 39 | + //cur_loc++; 40 | + cur_loc = AFL_R(MAP_SIZE); 41 | + 42 | + // only instrument if this basic block is the destination of a previous 43 | + // basic block that has multiple successors 44 | + // this gets rid of ~5-10% of instrumentations that are unnecessary 45 | + // result: a little more speed and less map pollution 46 | + int more_than_one = -1; 47 | + //fprintf(stderr, "BB %u: ", cur_loc); 48 | + for (BasicBlock *Pred : predecessors(&BB)) { 49 | + int count = 0; 50 | + if (more_than_one == -1) 51 | + more_than_one = 0; 52 | + //fprintf(stderr, " %p=>", Pred); 53 | + for (BasicBlock *Succ : successors(Pred)) { 54 | + //if (count > 0) 55 | + // fprintf(stderr, "|"); 56 | + if (Succ != NULL) count++; 57 | + //fprintf(stderr, "%p", Succ); 58 | + } 59 | + if (count > 1) 60 | + more_than_one = 1; 61 | + } 62 | + //fprintf(stderr, " == %d\n", more_than_one); 63 | + if (more_than_one != 1) 64 | + continue; 65 | 66 | ConstantInt *CurLoc = ConstantInt::get(Int32Ty, cur_loc); 67 | 68 | -------------------------------------------------------------------------------- /afl-fuzz-tmpdir.diff: -------------------------------------------------------------------------------- 1 | --- docs/env_variables.txt.orig 2018-02-20 09:36:08.498883127 +0100 2 | +++ docs/env_variables.txt 2018-02-20 09:35:54.292533925 +0100 3 | @@ -130,6 +130,11 @@ 4 | by some users for unorthodox parallelized fuzzing setups, but not 5 | advisable otherwise. 6 | 7 | + - AFL_TMPDIR is used to write the .cur_input file to if exists, and in 8 | + the normal output directory otherwise. You would use this to point to 9 | + a ramdisk/tmpfs. This increases the speed by a very minimal value but 10 | + also reduces the stress on SSDs. 11 | + 12 | - When developing custom instrumentation on top of afl-fuzz, you can use 13 | AFL_SKIP_BIN_CHECK to inhibit the checks for non-instrumented binaries 14 | and shell scripts; and AFL_DUMB_FORKSRV in conjunction with the -n 15 | --- afl-fuzz.c.orig 2018-02-20 09:19:59.125737935 +0100 16 | +++ afl-fuzz.c 2018-02-20 09:33:06.858964160 +0100 17 | @@ -83,6 +83,7 @@ 18 | EXP_ST u8 *in_dir, /* Input directory with test cases */ 19 | *out_file, /* File to fuzz, if any */ 20 | *out_dir, /* Working & output directory */ 21 | + *tmp_dir , /* Temporary directory for input */ 22 | *sync_dir, /* Synchronization directory */ 23 | *sync_id, /* Fuzzer ID */ 24 | *use_banner, /* Display banner */ 25 | @@ -3821,7 +3822,7 @@ 26 | 27 | /* And now, for some finishing touches. */ 28 | 29 | - fn = alloc_printf("%s/.cur_input", out_dir); 30 | + fn = alloc_printf("%s/.cur_input", tmp_dir); 31 | if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; 32 | ck_free(fn); 33 | 34 | @@ -7204,7 +7205,7 @@ 35 | 36 | EXP_ST void setup_stdio_file(void) { 37 | 38 | - u8* fn = alloc_printf("%s/.cur_input", out_dir); 39 | + u8* fn = alloc_printf("%s/.cur_input", tmp_dir); 40 | 41 | unlink(fn); /* Ignore errors */ 42 | 43 | @@ -7526,7 +7527,7 @@ 44 | /* If we don't have a file name chosen yet, use a safe default. */ 45 | 46 | if (!out_file) 47 | - out_file = alloc_printf("%s/.cur_input", out_dir); 48 | + out_file = alloc_printf("%s/.cur_input", tmp_dir); 49 | 50 | /* Be sure that we're always using fully-qualified paths. */ 51 | 52 | @@ -7907,6 +7908,14 @@ 53 | if (!strcmp(in_dir, out_dir)) 54 | FATAL("Input and output directories can't be the same"); 55 | 56 | + if ((tmp_dir = getenv("AFL_TMPDIR")) != NULL) { 57 | + char tmpfile[strlen(tmp_dir + 16)]; 58 | + sprintf(tmpfile, "%s/%s", tmp_dir, ".cur_input"); 59 | + if (access(tmpfile, F_OK) != -1) // there is still a race condition here, but well ... 60 | + FATAL("TMP_DIR already has an existing temporary input file: %s", tmpfile); 61 | + } else 62 | + tmp_dir = out_dir; 63 | + 64 | if (dumb_mode) { 65 | 66 | if (crash_mode) FATAL("-C and -n are mutually exclusive"); 67 | -------------------------------------------------------------------------------- /afl-fuzz-fileextensionopt.diff: -------------------------------------------------------------------------------- 1 | diff --git ./afl-fuzz.c ./afl-fuzz.c 2 | index 112180f..4108f8a 100644 3 | --- ./afl-fuzz.c 4 | +++ ./afl-fuzz.c 5 | @@ -92,6 +92,7 @@ EXP_ST u8 *in_dir, /* Input directory with test cases */ 6 | *doc_path, /* Path to documentation dir */ 7 | *target_path, /* Path to target binary */ 8 | *orig_cmdline, /* Original command line */ 9 | + *file_extension, /* File extension */ 10 | *input_model_file; /* Input model file */ 11 | 12 | EXP_ST u32 exec_tmout = EXEC_TIMEOUT; /* Configurable exec timeout (ms) */ 13 | @@ -3976,8 +3977,11 @@ static void maybe_delete_out_dir(void) { 14 | ck_free(fn); 15 | 16 | /* And now, for some finishing touches. */ 17 | - 18 | - fn = alloc_printf("%s/.cur_input", out_dir); 19 | + if(file_extension) { 20 | + fn = alloc_printf("%s/.cur_input.%s", out_dir, file_extension); 21 | + } else { 22 | + fn = alloc_printf("%s/.cur_input", out_dir); 23 | + } 24 | if (unlink(fn) && errno != ENOENT) goto dir_cleanup_failed; 25 | ck_free(fn); 26 | 27 | @@ -8094,6 +8098,7 @@ static void usage(u8* argv0) { 28 | " -h - mix higher-order mutations with other mutations\n" 29 | " -l - log input model mutations in log/.log of the output directory\n" 30 | " -H number - Apply a maximum on the number of higher-order mutations\n\n" 31 | + " -e ext - File extension for the temporarily generated test case\n\n" 32 | 33 | "For additional tips, please consult %s/README.\n\n", 34 | 35 | @@ -8237,7 +8242,12 @@ EXP_ST void setup_dirs_fds(void) { 36 | 37 | EXP_ST void setup_stdio_file(void) { 38 | 39 | - u8* fn = alloc_printf("%s/.cur_input", out_dir); 40 | + u8* fn; 41 | + if (file_extension) { 42 | + fn = alloc_printf("%s/.cur_input.%s", out_dir, file_extension); 43 | + } else { 44 | + fn = alloc_printf("%s/.cur_input", out_dir); 45 | + } 46 | 47 | unlink(fn); /* Ignore errors */ 48 | 49 | @@ -8558,8 +8568,13 @@ EXP_ST void detect_file_args(char** argv) { 50 | 51 | /* If we don't have a file name chosen yet, use a safe default. */ 52 | 53 | - if (!out_file) 54 | - out_file = alloc_printf("%s/.cur_input", out_dir); 55 | + if (!out_file) { 56 | + if (file_extension) { 57 | + out_file = alloc_printf("%s/.cur_input.%s", out_dir, file_extension); 58 | + } else { 59 | + out_file = alloc_printf("%s/.cur_input", out_dir); 60 | + } 61 | + } 62 | 63 | /* Be sure that we're always using fully-qualified paths. */ 64 | 65 | @@ -8754,7 +8769,7 @@ int main(int argc, char **argv) { 66 | gettimeofday(&tv, &tz); 67 | srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); 68 | 69 | - while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Qw:g:lhH:")) > 0) 70 | + while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Qw:g:lhH:e:")) > 0) 71 | 72 | switch (opt) { 73 | 74 | @@ -8972,6 +8987,13 @@ int main(int argc, char **argv) { 75 | if (errno) FATAL("Numeric format error of -H option"); 76 | 77 | break; 78 | + 79 | + case 'e': 80 | + if (file_extension) FATAL("Multiple -e options not supported"); 81 | + 82 | + file_extension = optarg; 83 | + 84 | + break; 85 | default: 86 | 87 | usage(argv[0]); 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # afl-patches 2 | Patches to afl to fix bugs or add enhancements 3 | 4 | 5 | ## ARCHIVE 6 | 7 | This github repository is archived as it serves no real purpose anymore as we have now afl+ü 8 | which has all these patches plus many more great features: 9 | 10 | **https://github.com/vanhauser-thc/AFLplusplus** 11 | 12 | 13 | 14 | 15 | ## Introduction 16 | 17 | All patches are for the current version afl-2.52b and can be applied in the extracted afl directory (patch -p0 < patch.diff). 18 | 19 | NOTE: all patches are stand-alone. Hence several conflict with each other. 20 | To ease the pain, there is now AFLplusplus which is afl-2.52b with most of the patches here incorporated: 21 | 22 | **https://github.com/vanhauser-thc/AFLplusplus** 23 | 24 | Additionally it has been upgraded to use qemu 3.1 and supports llvm 3.8 to 8. Enjoy! 25 | 26 | 27 | ## Patches 28 | 29 | ### Fixes 30 | 31 | **afl-llvm-fix.diff** - afl-clang: fix to afl llvm for SIGCHLD in the forkserver (by kcwu(at)csie(dot)org) 32 | 33 | **afl-llvm-fix2.diff** - afl-clang: fix to afl llvm to remove target binary optimisation and use g++ for compiling (needed for LLVM 5.0+) (by mh(at)mh-sec(dot)de) 34 | 35 | **afl-sort-all_uniq-fix.diff** - afl-cmin: fix sort (by legarrec(dot)vincent(at)gmail(dot)com) 36 | 37 | **llvm_versions_gt_4.diff** - llvm_mode: fix crash when clang (with llvm version > 4.x) loads compiler pass (by heiko(dot)eissfeldt(at)hexco(dot).de) 38 | 39 | 40 | ### Enhancements / Features 41 | 42 | **afl-fuzz-context_sensitive.diff** - afl-fuzz: patch that reimplements Angora Fuzzer's context sensitive branch coverage extension. (by heiko(dot)eissfeldt(at)hexco(dot)de) 43 | 44 | **laf-intel.diff** - afl-clang-fast/afl-clang-fast++: implements laf-intel (rewriting memcmp/strcmp for easier solving) (by heiko(dot)eissfeldt(at)hexco(dot)de) 45 | 46 | **afl-llvm-optimize.diff** - afl-clang-fast/afl-clang-fast++: only instrument blocks that are relevant, ~5-10%% less blocks to instrument equals more speed and less map pollution. (by mh(at)mh-sec(dot)de) 47 | 48 | **afl-fuzz-tmpdir.diff** - afl-fuzz: patch that adds AFL_TMPDIR where the .cur_input file will be written to. If you do not want your -o folder on a ramdisk this is what you want. (by mh(at)mh-sec(dot)de) 49 | 50 | **afl-tmpfs.diff** - afl-fuzz: patch that uses tmpfs for the .cur_input file, basically the same idea that my patch uses, but maybe easier. (by jjudin(at)iki(dot)fi) 51 | 52 | **afl-fuzz-79x24.diff** - afl-fuzz: lower the terminal requirements to 79x24 to display the status screen. (by heiko(dot)eissfeldt(at)hexco(dot)de) 53 | 54 | **afl-fuzz-fileextensionopt.diff** - afl-fuzz: cmdline option to force the input file to have a specific extension 55 | 56 | **afl-qemu-optimize-entrypoint.diff** - afl-qemu: fixes entrypoint detection for ARM thumb (by markh(dot)sj(at)gmail(dot)com plus adds AFL_ENTRYPOINT that lets you specify any point you want for the forkserver (gives more speed) (by mh(at)mh-sec(dot)de) 57 | 58 | **afl-qemu-optimize-logconditional.diff** - afl-qemu: only log destinations of calls and conditional jumps. patch is only for INTEL and ARM. makes it a bit slower but helpful for large targets that fill up the map otherwise (by mh(at)mh-sec(dot)de) 59 | 60 | **afl-qemu-optimize-map.diff** - afl-qemu: removes 2 instructions from afl_log at a cost of 64kb. (by mh(at)mh-sec(dot)de) 61 | 62 | **afl-qemu-speed.diff** - afl-qemu: fixes afl/qemu to allow caching, x3 speed improvement. (by abiondo on github) 63 | 64 | **afl-qemu-ppc64.diff** - afl-qemu: afl's patch is broken for PPC, william(dot)barsse(at)airbus(dot)com fixed it. 65 | 66 | **afl-as-AFL_INST_RATIO.diff** - afl-as: do not divide by 3 with sanitizer if AFL_INST_RATIO is manually set. (by legarrec(dot)vincent(at)gmail(dot)com) 67 | 68 | **afl-cmin-reduce-dataset.diff** - afl-cmin: rather small dataset of testcase instead of small testcase. (by legarrec(dot)vincent(at)gmail(dot)com) 69 | -------------------------------------------------------------------------------- /afl-qemu-optimize-logconditional.diff: -------------------------------------------------------------------------------- 1 | --- ./patches/afl-qemu-cpu-inl.h.orig 2018-04-03 13:15:47.239442416 +0200 2 | +++ ./patches/afl-qemu-cpu-inl.h 2018-04-03 13:42:05.663936006 +0200 3 | @@ -46,12 +46,14 @@ 4 | _start and does the usual forkserver stuff, not very different from 5 | regular instrumentation injected via afl-as.h. */ 6 | 7 | +unsigned int afl_do_log_next = 0; 8 | #define AFL_QEMU_CPU_SNIPPET2 do { \ 9 | if(itb->pc == afl_entry_point) { \ 10 | afl_setup(); \ 11 | afl_forkserver(cpu); \ 12 | } \ 13 | - afl_maybe_log(itb->pc); \ 14 | + if (afl_do_log_next == 1) { afl_maybe_log(itb->pc); afl_do_log_next = 0 ; } \ 15 | + if (itb->log_next == 1) afl_do_log_next = 1; \ 16 | } while (0) 17 | 18 | /* We use one additional file descriptor to relay "needs translation" 19 | --- ./qemu-2.10.0/target/arm/translate.h.orig 2018-04-03 13:34:29.559618756 +0200 20 | +++ ./qemu-2.10.0/target/arm/translate.h 2018-04-03 13:34:43.815874490 +0200 21 | @@ -67,6 +67,7 @@ 22 | #define TMP_A64_MAX 16 23 | int tmp_a64_count; 24 | TCGv_i64 tmp_a64[TMP_A64_MAX]; 25 | + int has_link; 26 | } DisasContext; 27 | 28 | typedef struct DisasCompare { 29 | --- ./qemu-2.10.0/target/arm/translate.c.orig 2018-04-03 13:22:42.906930362 +0200 30 | +++ ./qemu-2.10.0/target/arm/translate.c 2018-04-03 13:35:36.076811919 +0200 31 | @@ -8239,6 +8239,7 @@ 32 | val += 4; 33 | /* protected by ARCH(5); above, near the start of uncond block */ 34 | gen_bx_im(s, val); 35 | + s->has_link = 1; 36 | return; 37 | } else if ((insn & 0x0e000f00) == 0x0c000100) { 38 | if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { 39 | @@ -8408,6 +8409,7 @@ 40 | tcg_gen_movi_i32(tmp2, s->pc); 41 | store_reg(s, 14, tmp2); 42 | gen_bx(s, tmp); 43 | + s->has_link = 1; 44 | break; 45 | case 0x4: 46 | { 47 | @@ -9546,6 +9548,7 @@ 48 | offset = sextract32(insn << 2, 0, 26); 49 | val += offset + 4; 50 | gen_jmp(s, val); 51 | + s->has_link = 1; 52 | } 53 | break; 54 | case 0xc: 55 | @@ -10447,6 +10450,7 @@ 56 | if (insn & (1 << 14)) { 57 | /* Branch and link. */ 58 | tcg_gen_movi_i32(cpu_R[14], s->pc | 1); 59 | + s->has_link = 1; 60 | } 61 | 62 | offset += s->pc; 63 | @@ -11169,6 +11173,7 @@ 64 | tcg_gen_movi_i32(tmp2, val); 65 | store_reg(s, 14, tmp2); 66 | gen_bx(s, tmp); 67 | + s->has_link = 1; 68 | } else { 69 | /* Only BX works as exception-return, not BLX */ 70 | gen_bx_excret(s, tmp); 71 | @@ -12013,11 +12018,14 @@ 72 | disas_arm_insn(dc, insn); 73 | } 74 | 75 | + if (dc->condjmp || dc->has_link == 1) 76 | + tb->log_next = 1; 77 | + 78 | if (dc->condjmp && !dc->is_jmp) { 79 | gen_set_label(dc->condlabel); 80 | dc->condjmp = 0; 81 | } 82 | - 83 | + 84 | if (tcg_check_temp_count()) { 85 | fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n", 86 | dc->pc); 87 | --- ./qemu-2.10.0/target/i386/translate.c.orig 2018-04-03 12:59:07.877326731 +0200 88 | +++ ./qemu-2.10.0/target/i386/translate.c 2018-04-03 13:41:44.899555380 +0200 89 | @@ -138,6 +138,7 @@ 90 | int cpuid_ext3_features; 91 | int cpuid_7_0_ebx_features; 92 | int cpuid_xsave_features; 93 | + int have_jcc; 94 | } DisasContext; 95 | 96 | static void gen_eob(DisasContext *s); 97 | @@ -4431,6 +4432,7 @@ 98 | s->pc_start = s->pc = pc_start; 99 | prefixes = 0; 100 | s->override = -1; 101 | + s->have_jcc = 0; 102 | rex_w = -1; 103 | rex_r = 0; 104 | #ifdef TARGET_X86_64 105 | @@ -5017,6 +5019,7 @@ 106 | } 107 | tcg_gen_ld_tl(cpu_tmp4, cpu_env, offsetof(CPUX86State, eip)); 108 | gen_jr(s, cpu_tmp4); 109 | + s->have_jcc = 1; 110 | break; 111 | case 4: /* jmp Ev */ 112 | if (dflag == MO_16) { 113 | @@ -6496,6 +6499,7 @@ 114 | gen_push_v(s, cpu_T0); 115 | gen_bnd_jmp(s); 116 | gen_jmp(s, tval); 117 | + s->have_jcc = 1; 118 | } 119 | break; 120 | case 0x9a: /* lcall im */ 121 | @@ -8491,6 +8495,8 @@ 122 | } 123 | 124 | pc_ptr = disas_insn(env, dc, pc_ptr); 125 | + if (dc->have_jcc) 126 | + tb->log_next = 1; 127 | /* stop translation if indicated */ 128 | if (dc->is_jmp) 129 | break; 130 | --- ./qemu-2.10.0/include/exec/exec-all.h.orig 2018-04-03 13:06:11.360979778 +0200 131 | +++ ./qemu-2.10.0/include/exec/exec-all.h 2018-04-03 13:05:58.996753068 +0200 132 | @@ -395,6 +395,7 @@ 133 | */ 134 | uintptr_t jmp_list_next[2]; 135 | uintptr_t jmp_list_first; 136 | + uint32_t log_next; 137 | }; 138 | 139 | void tb_free(TranslationBlock *tb); 140 | -------------------------------------------------------------------------------- /afl-fuzz-context_sensitive.diff: -------------------------------------------------------------------------------- 1 | diff -ur afl-2.52b_Original/config.h afl-2.52b_Context/config.h 2 | --- ./config.h 2017-11-05 03:24:47.000000000 +0100 3 | +++ ./config.h 2019-02-07 22:40:58.152498335 +0100 4 | @@ -21,7 +21,7 @@ 5 | 6 | /* Version string: */ 7 | 8 | -#define VERSION "2.52b" 9 | +#define VERSION "2.52c" 10 | 11 | /****************************************************** 12 | * * 13 | @@ -313,7 +313,7 @@ 14 | problems with complex programs). You need to recompile the target binary 15 | after changing this - otherwise, SEGVs may ensue. */ 16 | 17 | -#define MAP_SIZE_POW2 16 18 | +#define MAP_SIZE_POW2 18 19 | #define MAP_SIZE (1 << MAP_SIZE_POW2) 20 | 21 | /* Maximum allocator request size (keep well under INT_MAX): */ 22 | diff -ur afl-2.52b_Original/llvm_mode/afl-llvm-pass.so.cc afl-2.52b_Context/llvm_mode/afl-llvm-pass.so.cc 23 | --- ./llvm_mode/afl-llvm-pass.so.cc 2017-06-23 00:49:06.000000000 +0200 24 | +++ ./llvm_mode/afl-llvm-pass.so.cc 2019-06-10 21:51:21.248256045 +0200 25 | @@ -104,15 +104,35 @@ 26 | M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 27 | 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); 28 | 29 | + Constant *getCallingContext = M.getOrInsertFunction("__afl_getCallingContext", Int32Ty, Type::getVoidTy(C) 30 | +#if __LLVM_MAJOR_VERSION__ < 5 31 | + ,nullptr 32 | +#endif 33 | +); 34 | + 35 | /* Instrument all the things! */ 36 | 37 | int inst_blocks = 0; 38 | 39 | - for (auto &F : M) 40 | + for (auto &F : M) { 41 | + AllocaInst *CallingContext = nullptr; 42 | for (auto &BB : F) { 43 | 44 | BasicBlock::iterator IP = BB.getFirstInsertionPt(); 45 | IRBuilder<> IRB(&(*IP)); 46 | + if (&BB == &F.getEntryBlock()) { 47 | + auto *localVar = IRB.CreateAlloca( IntegerType::getInt32Ty(C), nullptr, "AFL_StackContext"); 48 | + localVar->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 49 | + CallInst *myCall = IRB.CreateCall(getCallingContext); 50 | + CallingContext = localVar; 51 | + Value *context = myCall; 52 | + StoreInst *storectx = IRB.CreateStore(context, localVar); 53 | + storectx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 54 | + myCall->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 55 | + } 56 | + if (CallingContext == nullptr) { 57 | + break; 58 | + } 59 | 60 | if (AFL_R(100) >= inst_ratio) continue; 61 | 62 | @@ -128,18 +148,32 @@ 63 | PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 64 | Value *PrevLocCasted = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); 65 | 66 | + LoadInst *getContext = IRB.CreateLoad(CallingContext); 67 | + getContext->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 68 | + Value *Context = getContext; 69 | + 70 | /* Load SHM pointer */ 71 | 72 | LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); 73 | MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 74 | + Value *x1 = IRB.CreateXor(PrevLocCasted, CurLoc); 75 | + dyn_cast(x1) 76 | + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 77 | + Value *x2 = IRB.CreateXor(x1, Context); 78 | + dyn_cast(x2) 79 | + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 80 | Value *MapPtrIdx = 81 | - IRB.CreateGEP(MapPtr, IRB.CreateXor(PrevLocCasted, CurLoc)); 82 | + IRB.CreateGEP(MapPtr, x2); 83 | + dyn_cast(MapPtrIdx) 84 | + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 85 | 86 | /* Update bitmap */ 87 | 88 | LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); 89 | Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 90 | Value *Incr = IRB.CreateAdd(Counter, ConstantInt::get(Int8Ty, 1)); 91 | + dyn_cast(Incr) 92 | + ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 93 | IRB.CreateStore(Incr, MapPtrIdx) 94 | ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); 95 | 96 | @@ -152,7 +186,7 @@ 97 | inst_blocks++; 98 | 99 | } 100 | - 101 | + } 102 | /* Say something nice. */ 103 | 104 | if (!be_quiet) { 105 | diff -ur afl-2.52b_Original/llvm_mode/afl-llvm-rt.o.c afl-2.52b_Context/llvm_mode/afl-llvm-rt.o.c 106 | --- ./llvm_mode/afl-llvm-rt.o.c 2017-02-01 02:59:41.000000000 +0100 107 | +++ ./llvm_mode/afl-llvm-rt.o.c 2019-02-07 22:40:58.152498335 +0100 108 | @@ -28,6 +28,7 @@ 109 | #include 110 | #include 111 | #include 112 | +#include 113 | 114 | #include 115 | #include 116 | @@ -304,3 +305,29 @@ 117 | } 118 | 119 | } 120 | + 121 | + /* Construct a stack trace dependent value in order 122 | + to assist edge coverage with context sensitivity, 123 | + see Angora paper for details. This will lead to more 124 | + entries in the bitmap, so it is advised to 125 | + use a greater bitmap size (256KB). */ 126 | + 127 | +int __afl_getCallingContext(void) { 128 | + static void *StackTrace[256]; 129 | + int depth = 0; 130 | + u32 result = 0; 131 | + 132 | + if (!depth) 133 | + depth = backtrace(StackTrace, sizeof(StackTrace)/sizeof(void *)); 134 | +#ifdef HAVE__UNWIND_BACKTRACE 135 | + if (!depth) 136 | + depth = unwindBacktrace(StackTrace, sizeof(StackTrace)/sizeof(void *)); 137 | +#endif 138 | + if (!depth) 139 | + return result; 140 | + 141 | + for (int i = 2; i < depth - 2; ++i) { 142 | + result ^= (u32)( StackTrace[i] ); 143 | + } 144 | + return result % MAP_SIZE; 145 | +} 146 | -------------------------------------------------------------------------------- /afl-fuzz-79x24.diff: -------------------------------------------------------------------------------- 1 | --- ./afl-fuzz.c 2017-11-05 03:25:56.000000000 +0100 2 | +++ ./afl-fuzz.c 2018-08-18 09:33:50.633610556 +0200 3 | @@ -3980,7 +3980,7 @@ 4 | if (term_too_small) { 5 | 6 | SAYF(cBRI "Your terminal is too small to display the UI.\n" 7 | - "Please resize terminal window to at least 80x25.\n" cRST); 8 | + "Please resize terminal window to at least 79x24.\n" cRST); 9 | 10 | return; 11 | 12 | @@ -3989,14 +3989,14 @@ 13 | /* Let's start by drawing a centered banner. */ 14 | 15 | banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner); 16 | - banner_pad = (80 - banner_len) / 2; 17 | + banner_pad = (79 - banner_len) / 2; 18 | memset(tmp, ' ', banner_pad); 19 | 20 | sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN 21 | " (%s)", crash_mode ? cPIN "peruvian were-rabbit" : 22 | cYEL "american fuzzy lop", use_banner); 23 | 24 | - SAYF("\n%s\n\n", tmp); 25 | + SAYF("\n%s\n", tmp); 26 | 27 | /* "Handy" shortcuts for drawing boxes... */ 28 | 29 | @@ -4013,7 +4013,7 @@ 30 | /* Lord, forgive me this. */ 31 | 32 | SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH2 bHB 33 | - bH bSTOP cCYA " overall results " bSTG bH5 bRT "\n"); 34 | + bH bSTOP cCYA " overall results " bSTG bH2 bH2 bRT "\n"); 35 | 36 | if (dumb_mode) { 37 | 38 | @@ -4039,7 +4039,7 @@ 39 | } 40 | 41 | SAYF(bV bSTOP " run time : " cRST "%-34s " bSTG bV bSTOP 42 | - " cycles done : %s%-5s " bSTG bV "\n", 43 | + " cycles done : %s%-5s " bSTG bV "\n", 44 | DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); 45 | 46 | /* We want to warn people about not seeing new paths after a full cycle, 47 | @@ -4065,7 +4065,7 @@ 48 | 49 | } 50 | 51 | - SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", 52 | + SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", 53 | DI(queued_paths)); 54 | 55 | /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH 56 | @@ -4075,7 +4075,7 @@ 57 | (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); 58 | 59 | SAYF(bV bSTOP " last uniq crash : " cRST "%-34s " bSTG bV bSTOP 60 | - " uniq crashes : %s%-6s " bSTG bV "\n", 61 | + " uniq crashes : %s%-6s" bSTG bV "\n", 62 | DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, 63 | tmp); 64 | 65 | @@ -4083,11 +4083,11 @@ 66 | (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); 67 | 68 | SAYF(bV bSTOP " last uniq hang : " cRST "%-34s " bSTG bV bSTOP 69 | - " uniq hangs : " cRST "%-6s " bSTG bV "\n", 70 | + " uniq hangs : " cRST "%-6s" bSTG bV "\n", 71 | DTD(cur_ms, last_hang_time), tmp); 72 | 73 | SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH20 bHB bH bSTOP cCYA 74 | - " map coverage " bSTG bH bHT bH20 bH2 bH bVL "\n"); 75 | + " map coverage " bSTG bH bHT bH20 bH2 bVL "\n"); 76 | 77 | /* This gets funny because we want to print several variable-length variables 78 | together, but then cram them into a fixed-width field - so we need to 79 | @@ -4102,7 +4102,7 @@ 80 | sprintf(tmp, "%0.02f%% / %0.02f%%", ((double)queue_cur->bitmap_size) * 81 | 100 / MAP_SIZE, t_byte_ratio); 82 | 83 | - SAYF(" map density : %s%-21s " bSTG bV "\n", t_byte_ratio > 70 ? cLRD : 84 | + SAYF(" map density : %s%-21s" bSTG bV "\n", t_byte_ratio > 70 ? cLRD : 85 | ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); 86 | 87 | sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths), 88 | @@ -4113,10 +4113,10 @@ 89 | sprintf(tmp, "%0.02f bits/tuple", 90 | t_bytes ? (((double)t_bits) / t_bytes) : 0); 91 | 92 | - SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp); 93 | + SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp); 94 | 95 | SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH20 bX bH bSTOP cCYA 96 | - " findings in depth " bSTG bH20 bVL "\n"); 97 | + " findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n"); 98 | 99 | sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored), 100 | ((double)queued_favored) * 100 / queued_paths); 101 | @@ -4124,7 +4124,7 @@ 102 | /* Yeah... it's still going on... halp? */ 103 | 104 | SAYF(bV bSTOP " now trying : " cRST "%-21s " bSTG bV bSTOP 105 | - " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp); 106 | + " favored paths : " cRST "%-22s" bSTG bV "\n", stage_name, tmp); 107 | 108 | if (!stage_max) { 109 | 110 | @@ -4142,7 +4142,7 @@ 111 | sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov), 112 | ((double)queued_with_cov) * 100 / queued_paths); 113 | 114 | - SAYF(" new edges on : " cRST "%-22s " bSTG bV "\n", tmp); 115 | + SAYF(" new edges on : " cRST "%-22s" bSTG bV "\n", tmp); 116 | 117 | sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes), 118 | (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); 119 | @@ -4150,13 +4150,13 @@ 120 | if (crash_mode) { 121 | 122 | SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP 123 | - " new crashes : %s%-22s " bSTG bV "\n", DI(total_execs), 124 | + " new crashes : %s%-22s" bSTG bV "\n", DI(total_execs), 125 | unique_crashes ? cLRD : cRST, tmp); 126 | 127 | } else { 128 | 129 | SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP 130 | - " total crashes : %s%-22s " bSTG bV "\n", DI(total_execs), 131 | + " total crashes : %s%-22s" bSTG bV "\n", DI(total_execs), 132 | unique_crashes ? cLRD : cRST, tmp); 133 | 134 | } 135 | @@ -4180,12 +4180,12 @@ 136 | sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts), 137 | (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); 138 | 139 | - SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s " bSTG bV "\n", tmp); 140 | + SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s" bSTG bV "\n", tmp); 141 | 142 | /* Aaaalmost there... hold on! */ 143 | 144 | SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bH bHT bH10 145 | - bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bH bVL "\n"); 146 | + bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bVL "\n"); 147 | 148 | if (skip_deterministic) { 149 | 150 | @@ -4201,7 +4201,7 @@ 151 | } 152 | 153 | SAYF(bV bSTOP " bit flips : " cRST "%-37s " bSTG bV bSTOP " levels : " 154 | - cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth)); 155 | + cRST "%-10s" bSTG bV "\n", tmp, DI(max_depth)); 156 | 157 | if (!skip_deterministic) 158 | sprintf(tmp, "%s/%s, %s/%s, %s/%s", 159 | @@ -4210,7 +4210,7 @@ 160 | DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); 161 | 162 | SAYF(bV bSTOP " byte flips : " cRST "%-37s " bSTG bV bSTOP " pending : " 163 | - cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); 164 | + cRST "%-10s" bSTG bV "\n", tmp, DI(pending_not_fuzzed)); 165 | 166 | if (!skip_deterministic) 167 | sprintf(tmp, "%s/%s, %s/%s, %s/%s", 168 | @@ -4219,7 +4219,7 @@ 169 | DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); 170 | 171 | SAYF(bV bSTOP " arithmetics : " cRST "%-37s " bSTG bV bSTOP " pend fav : " 172 | - cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); 173 | + cRST "%-10s" bSTG bV "\n", tmp, DI(pending_favored)); 174 | 175 | if (!skip_deterministic) 176 | sprintf(tmp, "%s/%s, %s/%s, %s/%s", 177 | @@ -4228,7 +4228,7 @@ 178 | DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); 179 | 180 | SAYF(bV bSTOP " known ints : " cRST "%-37s " bSTG bV bSTOP " own finds : " 181 | - cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); 182 | + cRST "%-10s" bSTG bV "\n", tmp, DI(queued_discovered)); 183 | 184 | if (!skip_deterministic) 185 | sprintf(tmp, "%s/%s, %s/%s, %s/%s", 186 | @@ -4237,7 +4237,7 @@ 187 | DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); 188 | 189 | SAYF(bV bSTOP " dictionary : " cRST "%-37s " bSTG bV bSTOP 190 | - " imported : " cRST "%-10s " bSTG bV "\n", tmp, 191 | + " imported : " cRST "%-10s" bSTG bV "\n", tmp, 192 | sync_id ? DI(queued_imported) : (u8*)"n/a"); 193 | 194 | sprintf(tmp, "%s/%s, %s/%s", 195 | @@ -4249,7 +4249,7 @@ 196 | if (t_bytes) sprintf(tmp, "%0.02f%%", stab_ratio); 197 | else strcpy(tmp, "n/a"); 198 | 199 | - SAYF(" stability : %s%-10s " bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) 200 | + SAYF(" stability : %s%-10s" bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) 201 | ? cLRD : ((queued_variable && (!persistent_mode || var_byte_count > 20)) 202 | ? cMGN : cRST), tmp); 203 | 204 | @@ -4284,7 +4284,7 @@ 205 | 206 | } 207 | 208 | - SAYF(bV bSTOP " trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" 209 | + SAYF(bV bSTOP " trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH bRB "\n" 210 | bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); 211 | 212 | /* Provide some CPU utilization stats. */ 213 | @@ -7032,7 +7032,7 @@ 214 | 215 | if (ioctl(1, TIOCGWINSZ, &ws)) return; 216 | 217 | - if (ws.ws_row < 25 || ws.ws_col < 80) term_too_small = 1; 218 | + if (ws.ws_row < 24 || ws.ws_col < 79) term_too_small = 1; 219 | 220 | } 221 | 222 | -------------------------------------------------------------------------------- /afl-tmpfs.diff: -------------------------------------------------------------------------------- 1 | --- afl-fuzz.c 2018-06-20 15:56:46.816337163 +0300 2 | +++ afl-fuzz.c 2018-06-20 17:21:57.846392733 +0300 3 | @@ -60,6 +60,23 @@ 4 | # include 5 | #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ 6 | 7 | +#if defined(__linux__) 8 | +# include 9 | +# include 10 | +/* Not all Linux distros have linux/magic.h include available where */ 11 | +/* TMPFS_MAGIC is defined. */ 12 | +# define TMPFS_MAGIC 0x01021994 13 | +#elif defined(__FreeBSD__) || defined(__OpenBSD__) 14 | +# include 15 | +# include 16 | +#elif defined(__NetBSD__) 17 | +# include 18 | +#elif defined(__sun) 19 | +# include 20 | +# include 21 | +# include 22 | +#endif /* __linux__ */ 23 | + 24 | /* For systems that have sched_setaffinity; right now just Linux, but one 25 | can hope... */ 26 | 27 | @@ -82,6 +99,7 @@ 28 | 29 | EXP_ST u8 *in_dir, /* Input directory with test cases */ 30 | *out_file, /* File to fuzz, if any */ 31 | + *out_file_dir, /* Directory to write out file */ 32 | *out_dir, /* Working & output directory */ 33 | *sync_dir, /* Synchronization directory */ 34 | *sync_id, /* Fuzzer ID */ 35 | @@ -142,7 +160,7 @@ 36 | 37 | static u8 var_bytes[MAP_SIZE]; /* Bytes that appear to be variable */ 38 | 39 | -static s32 shm_id; /* ID of the SHM region */ 40 | +static s32 shm_id = -1; /* ID of the SHM region */ 41 | 42 | static volatile u8 stop_soon, /* Ctrl-C pressed? */ 43 | clear_screen = 1, /* Window resized? */ 44 | @@ -223,6 +241,12 @@ 45 | 46 | static FILE* plot_file; /* Gnuplot output file */ 47 | 48 | +struct tmpfs_candidate { 49 | + uint64_t required_fs_free; /* Free tmpfs space in bytes */ 50 | + uint64_t fs_free; /* Best tmpfs candidate free space */ 51 | + char fs_path[512]; /* Best tmpfs candidate root */ 52 | +}; 53 | + 54 | struct queue_entry { 55 | 56 | u8* fname; /* File name for the test case */ 57 | @@ -1196,12 +1220,20 @@ 58 | #endif /* ^__x86_64__ */ 59 | 60 | 61 | -/* Get rid of shared memory (atexit handler). */ 62 | - 63 | -static void remove_shm(void) { 64 | - 65 | - shmctl(shm_id, IPC_RMID, NULL); 66 | +/* Get rid of shared memory and temporary directories (atexit handler). */ 67 | 68 | +static void cleanup_tmpallocs(void) { 69 | + if (out_file_dir != NULL && strcmp(out_file_dir, out_dir) != 0) { 70 | + /* On stdin case we do not have out_file */ 71 | + u8* fn = alloc_printf("%s/.cur_input", out_file_dir); 72 | + /* Ignore errors */ 73 | + unlink(fn); 74 | + rmdir(out_file_dir); 75 | + ck_free(fn); 76 | + } 77 | + if (shm_id >= 0) { 78 | + shmctl(shm_id, IPC_RMID, NULL); 79 | + } 80 | } 81 | 82 | 83 | @@ -1352,8 +1384,6 @@ 84 | 85 | if (shm_id < 0) PFATAL("shmget() failed"); 86 | 87 | - atexit(remove_shm); 88 | - 89 | shm_str = alloc_printf("%d", shm_id); 90 | 91 | /* If somebody is asking us to fuzz instrumented binaries in dumb mode, 92 | @@ -7199,12 +7229,296 @@ 93 | 94 | } 95 | 96 | +/* Functions to detect an usable memory based file system. */ 97 | + 98 | +static u8 try_create_directory(const u8* path) { 99 | + const u8* path_end; 100 | + u8* template_path_end; 101 | + const char suffix[] = "/.afl-fuzz.XXXXXX"; 102 | + u8 template[sizeof(((struct tmpfs_candidate*)0)->fs_path) + sizeof(suffix)] = {0}; 103 | + 104 | + /* Strip the last slash */ 105 | + path_end = path + strlen(path); 106 | + while (path < path_end && *(path_end - 1) == '/') { 107 | + path_end--; 108 | + } 109 | + if (path_end == path) { 110 | + return 0; 111 | + } 112 | + 113 | + if (sizeof(template) < (path_end - path) + sizeof(suffix)) { 114 | + return 0; 115 | + } 116 | + 117 | + memcpy(template, path, path_end - path); 118 | + template_path_end = template + (path_end - path); 119 | + memcpy(template_path_end, suffix, sizeof(suffix)); 120 | + 121 | + { 122 | + u8* result = mkdtemp(template); 123 | + if (result == NULL) { 124 | + return 0; 125 | + } 126 | + rmdir(result); 127 | + } 128 | + return 1; 129 | +} 130 | + 131 | +static u8 is_path_tmpfs(const u8* fs_path) { 132 | +#if defined(__linux__) 133 | + struct statfs stats = {0}; 134 | + if (statfs(fs_path, &stats) != 0) { 135 | + return 0; 136 | + } 137 | + if (stats.f_type == TMPFS_MAGIC) { 138 | + return 1; 139 | + } 140 | +#elif defined(__FreeBSD__) || defined(__OpenBSD__) 141 | + struct statfs stats = {0}; 142 | + if (statfs(fs_path, &stats) != 0) { 143 | + return 0; 144 | + } 145 | + if (strcmp(stats.f_fstypename, "tmpfs") == 0) { 146 | + return 1; 147 | + } 148 | +# ifdef __OpenBSD__ 149 | + if (strcmp(stats.f_fstypename, "mfs") == 0) { 150 | + return 1; 151 | + } 152 | +# endif /* __OpenBSD */ 153 | +#elif defined(__NetBSD__) 154 | + struct statvfs stats = {0}; 155 | + if (statvfs(fs_path, &stats) != 0) { 156 | + return 0; 157 | + } 158 | + if (strcmp(stats.f_fstypename, "tmpfs") == 0) { 159 | + return 1; 160 | + } 161 | +#elif defined(__sun) 162 | + struct statvfs stats = {0}; 163 | + if (statvfs(fs_path, &stats) != 0) { 164 | + return 0; 165 | + } 166 | + if (strcmp(stats.f_basetype, "tmpfs") == 0) { 167 | + return 1; 168 | + } 169 | +#endif 170 | + return 0; 171 | +} 172 | + 173 | +static u64 get_fs_size(const u8* fs_path) { 174 | +#if defined(__linux__) 175 | + struct statfs stats = {0}; 176 | + statfs(fs_path, &stats); 177 | + return (u64)stats.f_bavail * (u64)stats.f_frsize; 178 | +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 179 | + struct statvfs stats = {0}; 180 | + statvfs(fs_path, &stats); 181 | + return (u64)stats.f_bavail * (u64)stats.f_frsize; 182 | +#elif defined(__sun) 183 | + struct statvfs stats = {0}; 184 | + statvfs(fs_path, &stats); 185 | + return (u64)stats.f_bavail * (u64)stats.f_frsize; 186 | +#else 187 | + return 0; 188 | +#endif 189 | +} 190 | + 191 | +static u64 read_tmpfs_free(const u8* fs_path) { 192 | + if (!is_path_tmpfs(fs_path)) { 193 | + return 0; 194 | + } 195 | + return get_fs_size(fs_path); 196 | +} 197 | + 198 | +static u8 assign_if_freer_fs_path( 199 | + const u8* fs_path, struct tmpfs_candidate* largest_candidate) { 200 | + u64 fs_free = read_tmpfs_free(fs_path); 201 | + if (fs_free < largest_candidate->required_fs_free) { 202 | + return 0; 203 | + } 204 | + if (fs_free <= largest_candidate->fs_free) { 205 | + return 0; 206 | + } 207 | + if (!try_create_directory(fs_path)) { 208 | + return 0; 209 | + } 210 | + { 211 | + size_t fs_path_len = strlen(fs_path); 212 | + if (sizeof(largest_candidate->fs_path) <= fs_path_len) { 213 | + return 0; 214 | + } 215 | + largest_candidate->fs_free = fs_free; 216 | + memcpy(largest_candidate->fs_path, fs_path, fs_path_len); 217 | + largest_candidate->fs_path[fs_path_len] = '\0'; 218 | + } 219 | + return 1; 220 | +} 221 | + 222 | +static void iterate_proc_mounts(const u8* proc_mounts, struct tmpfs_candidate* largest_candidate) { 223 | + u8 mount_line[512] = ""; 224 | + 225 | + /* Try to figure out other tmpfs locations based on device 226 | + mountpoints. This is Linux specific thing: */ 227 | + FILE* mounts_fp = fopen(proc_mounts, "r"); 228 | + if (mounts_fp == NULL) { 229 | + return; 230 | + } 231 | + 232 | + while (fgets(mount_line, sizeof(mount_line), mounts_fp) != NULL) { 233 | + u8* path_end; 234 | + u8* fs_path = strchr(mount_line, ' '); 235 | + if (fs_path == NULL) { 236 | + continue; 237 | + } 238 | + fs_path++; 239 | + path_end = strchr(fs_path, ' '); 240 | + if (path_end == NULL) { 241 | + continue; 242 | + } 243 | + *path_end = '\0'; 244 | + assign_if_freer_fs_path(fs_path, largest_candidate); 245 | + } 246 | + 247 | + fclose(mounts_fp); 248 | +} 249 | + 250 | +static void iterate_getfsstat(struct tmpfs_candidate* largest_candidate) { 251 | +#if defined(__FreeBSD__) || defined(__OpenBSD__) 252 | + int i; 253 | + int mounted_fs = getfsstat(NULL, 0, MNT_NOWAIT); 254 | + int allocated_fs = mounted_fs + 1; 255 | + struct statfs* fs_list = calloc(allocated_fs, sizeof(struct statfs)); 256 | + if (fs_list == NULL) { 257 | + return; 258 | + } 259 | +# if defined(__FreeBSD__) || defined(__OpenBSD__) 260 | + int returned_fs = getfsstat(fs_list, sizeof(struct statfs) * allocated_fs, MNT_NOWAIT); 261 | + const struct statfs* current_fs = fs_list; 262 | +# elif defined(__NetBSD__) 263 | + int returned_fs = getvfsstat(fs_list, sizeof(struct statvfs) * allocated_fs, MNT_NOWAIT); 264 | + const struct statvfs* current_fs = fs_list; 265 | +# endif 266 | + if (returned_fs == -1) { 267 | + free(fs_list); 268 | + return; 269 | + } 270 | + for (i = 0; i < returned_fs; i++, current_fs++) { 271 | + assign_if_freer_fs_path(current_fs->f_mntonname, largest_candidate); 272 | + } 273 | + free(fs_list); 274 | +#else 275 | + (void)largest_candidate; 276 | +#endif 277 | +} 278 | + 279 | +static void iterate_getmntent(const u8* path, struct tmpfs_candidate* largest_candidate) { 280 | +#if defined(__linux__) 281 | + struct mntent* mountpoint = NULL; 282 | + FILE* mnttab = setmntent(path, "r"); 283 | + if (mnttab == NULL) { 284 | + return; 285 | + } 286 | + while ((mountpoint = getmntent(mnttab)) != NULL) { 287 | + assign_if_freer_fs_path(mountpoint->mnt_dir, largest_candidate); 288 | + } 289 | + endmntent(mnttab); 290 | +#elif defined(__sun) 291 | + struct mnttab mountpoint = {0}; 292 | + struct mnttab tmpfs_mountpoint = { .mnt_fstype = "tmpfs" }; 293 | + FILE* mnttab = fopen(path, "r"); 294 | + if (mnttab == NULL) { 295 | + return; 296 | + } 297 | + while (getmntany(mnttab, &mountpoint, &tmpfs_mountpoint) == 0 298 | + && mountpoint.mnt_mountp != NULL) { 299 | + assign_if_freer_fs_path(mountpoint.mnt_mountp, largest_candidate); 300 | + } 301 | + fclose(mnttab); 302 | +#else 303 | + (void)path; 304 | + (void)largest_candidate; 305 | +#endif 306 | +} 307 | + 308 | +static void iterate_getvfsent(const u8* path, struct tmpfs_candidate* largest_candidate) { 309 | +#if defined(__sun) 310 | + struct vfstab mountpoint = {0}; 311 | + struct vfstab tmpfs_mountpoint = { .vfs_fstype = "tmpfs" }; 312 | + FILE* vfstab = fopen(path, "r"); 313 | + if (vfstab == NULL) { 314 | + return; 315 | + } 316 | + while (getvfsany(vfstab, &mountpoint, &tmpfs_mountpoint) == 0 317 | + && mountpoint.vfs_mountp != NULL) { 318 | + assign_if_freer_fs_path(mountpoint.vfs_mountp, largest_candidate); 319 | + } 320 | + fclose(vfstab); 321 | +#else 322 | + (void)path; 323 | + (void)largest_candidate; 324 | +#endif 325 | +} 326 | + 327 | +EXP_ST u8* get_tmpfs_workdir(u64 minimum_free_bytes) { 328 | + size_t i; 329 | + struct tmpfs_candidate largest_candidate = {0}; 330 | + /* First let's guess couple of locations in case we are inside a */ 331 | + /* container or other faked file system without /proc/ access but */ 332 | + /* possibly with some tmpfs accesses: */ 333 | + const u8* const tmpfs_guesses[] = { 334 | + "/var/shm", 335 | + "/dev/shm", 336 | + "/run/shm", 337 | + "/tmp" 338 | + }; 339 | + largest_candidate.required_fs_free = minimum_free_bytes; 340 | + 341 | + for (i = 0; i < sizeof(tmpfs_guesses) / sizeof(*tmpfs_guesses); i++) { 342 | + const u8* fs_path = tmpfs_guesses[i]; 343 | + assign_if_freer_fs_path(fs_path, &largest_candidate); 344 | + } 345 | + 346 | + /* NetBSD and Linux */ 347 | + iterate_proc_mounts("/proc/mounts", &largest_candidate); 348 | + /* Just Linux */ 349 | + iterate_proc_mounts("/proc/self/mounts", &largest_candidate); 350 | + 351 | + iterate_getfsstat(&largest_candidate); 352 | + 353 | + /* Linux */ 354 | + iterate_getmntent("/etc/mtab", &largest_candidate); 355 | + iterate_getmntent("/etc/fstab", &largest_candidate); 356 | + /* SunOS */ 357 | + iterate_getmntent("/etc/mnttab", &largest_candidate); 358 | + 359 | + iterate_getvfsent("/etc/vfstab", &largest_candidate); 360 | + 361 | + if (largest_candidate.fs_free == 0) { 362 | + return NULL; 363 | + } 364 | + 365 | + { 366 | + u8* template = alloc_printf("%s/.afl-fuzz.XXXXXX", largest_candidate.fs_path); 367 | + u8* result = mkdtemp(template); 368 | + if (result == NULL) { 369 | + free(template); 370 | + } 371 | + return result; 372 | + } 373 | +} 374 | 375 | /* Setup the output file for fuzzed data, if not using -f. */ 376 | 377 | EXP_ST void setup_stdio_file(void) { 378 | - 379 | - u8* fn = alloc_printf("%s/.cur_input", out_dir); 380 | + u8* fn; 381 | + out_file_dir = get_tmpfs_workdir(1024 * 1024); 382 | + if (out_file_dir != NULL) { 383 | + fn = alloc_printf("%s/.cur_input", out_file_dir); 384 | + } else { 385 | + fn = alloc_printf("%s/.cur_input", out_dir); 386 | + out_file_dir = out_dir; 387 | + } 388 | 389 | unlink(fn); /* Ignore errors */ 390 | 391 | @@ -7505,7 +7819,6 @@ 392 | 393 | } 394 | 395 | - 396 | /* Detect @@ in args. */ 397 | 398 | EXP_ST void detect_file_args(char** argv) { 399 | @@ -7525,8 +7838,15 @@ 400 | 401 | /* If we don't have a file name chosen yet, use a safe default. */ 402 | 403 | - if (!out_file) 404 | - out_file = alloc_printf("%s/.cur_input", out_dir); 405 | + if (!out_file) { 406 | + out_file_dir = get_tmpfs_workdir(1024 * 1024); 407 | + if (out_file_dir != NULL) { 408 | + out_file = alloc_printf("%s/.cur_input", out_file_dir); 409 | + } else { 410 | + out_file = alloc_printf("%s/.cur_input", out_dir); 411 | + out_file_dir = out_dir; 412 | + } 413 | + } 414 | 415 | /* Be sure that we're always using fully-qualified paths. */ 416 | 417 | @@ -7952,6 +8272,9 @@ 418 | check_cpu_governor(); 419 | 420 | setup_post(); 421 | + 422 | + atexit(cleanup_tmpallocs); 423 | + 424 | setup_shm(); 425 | init_count_class16(); 426 | 427 | -------------------------------------------------------------------------------- /afl-qemu-speed.diff: -------------------------------------------------------------------------------- 1 | --- ./afl-analyze.c 2018-03-04 12:24:45.413737400 +0100 2 | +++ ./afl-analyze.c 2018-10-22 14:07:07.048608000 +0200 3 | @@ -882,10 +882,6 @@ 4 | char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); 5 | u8 *tmp, *cp, *rsl, *own_copy; 6 | 7 | - /* Workaround for a QEMU stability glitch. */ 8 | - 9 | - setenv("QEMU_LOG", "nochain", 1); 10 | - 11 | memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); 12 | 13 | /* Now we need to actually find qemu for argv[0]. */ 14 | --- ./afl-fuzz.c 2018-03-04 12:24:45.433737400 +0100 15 | +++ ./afl-fuzz.c 2018-10-22 14:07:07.048608000 +0200 16 | @@ -7605,10 +7605,6 @@ 17 | char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); 18 | u8 *tmp, *cp, *rsl, *own_copy; 19 | 20 | - /* Workaround for a QEMU stability glitch. */ 21 | - 22 | - setenv("QEMU_LOG", "nochain", 1); 23 | - 24 | memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); 25 | 26 | new_argv[2] = target_path; 27 | --- ./afl-showmap.c 2018-03-04 12:24:45.446237400 +0100 28 | +++ ./afl-showmap.c 2018-10-22 14:07:07.048608000 +0200 29 | @@ -559,10 +559,6 @@ 30 | char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); 31 | u8 *tmp, *cp, *rsl, *own_copy; 32 | 33 | - /* Workaround for a QEMU stability glitch. */ 34 | - 35 | - setenv("QEMU_LOG", "nochain", 1); 36 | - 37 | memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); 38 | 39 | new_argv[2] = target_path; 40 | --- ./afl-tmin.c 2018-03-04 12:24:45.451237400 +0100 41 | +++ ./afl-tmin.c 2018-10-22 14:07:07.048608000 +0200 42 | @@ -894,10 +894,6 @@ 43 | char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); 44 | u8 *tmp, *cp, *rsl, *own_copy; 45 | 46 | - /* Workaround for a QEMU stability glitch. */ 47 | - 48 | - setenv("QEMU_LOG", "nochain", 1); 49 | - 50 | memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); 51 | 52 | /* Now we need to actually find qemu for argv[0]. */ 53 | --- ./llvm_mode/afl-clang-fast.c 2018-08-16 14:49:52.781345800 +0200 54 | +++ ./llvm_mode/afl-clang-fast.c 2018-10-22 14:07:07.142208000 +0200 55 | @@ -208,7 +208,7 @@ 56 | if (!getenv("AFL_DONT_OPTIMIZE")) { 57 | 58 | cc_params[cc_par_cnt++] = "-g"; 59 | - //cc_params[cc_par_cnt++] = "-O3"; 60 | + cc_params[cc_par_cnt++] = "-O3"; 61 | cc_params[cc_par_cnt++] = "-funroll-loops"; 62 | 63 | } 64 | --- ./llvm_mode/Makefile 2018-08-16 14:50:27.850146000 +0200 65 | +++ ./llvm_mode/Makefile 2018-10-22 14:07:07.142208000 +0200 66 | @@ -52,7 +51,7 @@ 67 | 68 | ifeq "$(origin CC)" "default" 69 | CC = clang 70 | - CXX = g++ 71 | + CXX = clang++ 72 | endif 73 | 74 | ifndef AFL_TRACE_PC 75 | --- ./qemu_mode/build_qemu_support.sh 2018-03-04 12:24:46.013737400 +0100 76 | +++ ./qemu_mode/build_qemu_support.sh 2018-10-22 14:07:07.142208000 +0200 77 | @@ -131,6 +131,9 @@ 78 | patch -p1 <../patches/elfload.diff || exit 1 79 | patch -p1 <../patches/cpu-exec.diff || exit 1 80 | patch -p1 <../patches/syscall.diff || exit 1 81 | +patch -p1 <../patches/configure.diff || exit 1 82 | +patch -p1 <../patches/memfd.diff || exit 1 83 | +patch -p1 <../patches/translate-all.diff || exit 1 84 | 85 | echo "[+] Patching done." 86 | 87 | --- ./qemu_mode/patches/afl-qemu-cpu-inl.h 2018-03-04 12:24:46.021237400 +0100 88 | +++ ./qemu_mode/patches/afl-qemu-cpu-inl.h 2018-10-22 14:07:07.142208000 +0200 89 | @@ -7,6 +7,9 @@ 90 | 91 | Idea & design very much by Andrew Griffiths. 92 | 93 | + TCG instrumentation and block chaining support by Andrea Biondo 94 | + 95 | + 96 | Copyright 2015, 2016, 2017 Google Inc. All rights reserved. 97 | 98 | Licensed under the Apache License, Version 2.0 (the "License"); 99 | @@ -33,15 +36,6 @@ 100 | * VARIOUS AUXILIARY STUFF * 101 | ***************************/ 102 | 103 | -/* A snippet patched into tb_find_slow to inform the parent process that 104 | - we have hit a new block that hasn't been translated yet, and to tell 105 | - it to translate within its own context, too (this avoids translation 106 | - overhead in the next forked-off copy). */ 107 | - 108 | -#define AFL_QEMU_CPU_SNIPPET1 do { \ 109 | - afl_request_tsl(pc, cs_base, flags); \ 110 | - } while (0) 111 | - 112 | /* This snippet kicks in when the instruction pointer is positioned at 113 | _start and does the usual forkserver stuff, not very different from 114 | regular instrumentation injected via afl-as.h. */ 115 | @@ -51,7 +45,6 @@ 116 | afl_setup(); \ 117 | afl_forkserver(cpu); \ 118 | } \ 119 | - afl_maybe_log(itb->pc); \ 120 | } while (0) 121 | 122 | /* We use one additional file descriptor to relay "needs translation" 123 | @@ -61,7 +54,7 @@ 124 | 125 | /* This is equivalent to afl-as.h: */ 126 | 127 | -static unsigned char *afl_area_ptr; 128 | +unsigned char *afl_area_ptr; /* Exported for afl_gen_trace */ 129 | 130 | /* Exported variables populated by the code patched into elfload.c: */ 131 | 132 | @@ -76,23 +69,32 @@ 133 | 134 | /* Instrumentation ratio: */ 135 | 136 | -static unsigned int afl_inst_rms = MAP_SIZE; 137 | +unsigned int afl_inst_rms = MAP_SIZE; /* Exported for afl_gen_trace */ 138 | 139 | /* Function declarations. */ 140 | 141 | static void afl_setup(void); 142 | static void afl_forkserver(CPUState*); 143 | -static inline void afl_maybe_log(abi_ulong); 144 | 145 | static void afl_wait_tsl(CPUState*, int); 146 | -static void afl_request_tsl(target_ulong, target_ulong, uint64_t); 147 | +static void afl_request_tsl(target_ulong, target_ulong, uint32_t, TranslationBlock*, int); 148 | 149 | -/* Data structure passed around by the translate handlers: */ 150 | +/* Data structures passed around by the translate handlers: */ 151 | 152 | -struct afl_tsl { 153 | +struct afl_tb { 154 | target_ulong pc; 155 | target_ulong cs_base; 156 | - uint64_t flags; 157 | + uint32_t flags; 158 | +}; 159 | + 160 | +struct afl_tsl { 161 | + struct afl_tb tb; 162 | + char is_chain; 163 | +}; 164 | + 165 | +struct afl_chain { 166 | + struct afl_tb last_tb; 167 | + int tb_exit; 168 | }; 169 | 170 | /* Some forward decls: */ 171 | @@ -224,54 +226,37 @@ 172 | } 173 | 174 | 175 | -/* The equivalent of the tuple logging routine from afl-as.h. */ 176 | - 177 | -static inline void afl_maybe_log(abi_ulong cur_loc) { 178 | - 179 | - static __thread abi_ulong prev_loc; 180 | - 181 | - /* Optimize for cur_loc > afl_end_code, which is the most likely case on 182 | - Linux systems. */ 183 | - 184 | - if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr) 185 | - return; 186 | - 187 | - /* Looks like QEMU always maps to fixed locations, so ASAN is not a 188 | - concern. Phew. But instruction addresses may be aligned. Let's mangle 189 | - the value to get something quasi-uniform. */ 190 | - 191 | - cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 192 | - cur_loc &= MAP_SIZE - 1; 193 | - 194 | - /* Implement probabilistic instrumentation by looking at scrambled block 195 | - address. This keeps the instrumented locations stable across runs. */ 196 | - 197 | - if (cur_loc >= afl_inst_rms) return; 198 | - 199 | - afl_area_ptr[cur_loc ^ prev_loc]++; 200 | - prev_loc = cur_loc >> 1; 201 | - 202 | -} 203 | - 204 | - 205 | /* This code is invoked whenever QEMU decides that it doesn't have a 206 | - translation of a particular block and needs to compute it. When this happens, 207 | - we tell the parent to mirror the operation, so that the next fork() has a 208 | - cached copy. */ 209 | + translation of a particular block and needs to compute it, or when it 210 | + decides to chain two TBs together. When this happens, we tell the parent to 211 | + mirror the operation, so that the next fork() has a cached copy. */ 212 | 213 | -static void afl_request_tsl(target_ulong pc, target_ulong cb, uint64_t flags) { 214 | +static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags, 215 | + TranslationBlock *last_tb, int tb_exit) { 216 | 217 | struct afl_tsl t; 218 | + struct afl_chain c; 219 | 220 | if (!afl_fork_child) return; 221 | 222 | - t.pc = pc; 223 | - t.cs_base = cb; 224 | - t.flags = flags; 225 | + t.tb.pc = pc; 226 | + t.tb.cs_base = cb; 227 | + t.tb.flags = flags; 228 | + t.is_chain = (last_tb != NULL); 229 | 230 | if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) 231 | return; 232 | 233 | + if (t.is_chain) { 234 | + c.last_tb.pc = last_tb->pc; 235 | + c.last_tb.cs_base = last_tb->cs_base; 236 | + c.last_tb.flags = last_tb->flags; 237 | + c.tb_exit = tb_exit; 238 | + 239 | + if (write(TSL_FD, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain)) 240 | + return; 241 | + } 242 | + 243 | } 244 | 245 | /* This is the other side of the same channel. Since timeouts are handled by 246 | @@ -280,7 +265,8 @@ 247 | static void afl_wait_tsl(CPUState *cpu, int fd) { 248 | 249 | struct afl_tsl t; 250 | - TranslationBlock *tb; 251 | + struct afl_chain c; 252 | + TranslationBlock *tb, *last_tb; 253 | 254 | while (1) { 255 | 256 | @@ -289,16 +275,31 @@ 257 | if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) 258 | break; 259 | 260 | - tb = tb_htable_lookup(cpu, t.pc, t.cs_base, t.flags); 261 | + tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags); 262 | 263 | if(!tb) { 264 | mmap_lock(); 265 | tb_lock(); 266 | - tb_gen_code(cpu, t.pc, t.cs_base, t.flags, 0); 267 | + tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, 0); 268 | mmap_unlock(); 269 | tb_unlock(); 270 | } 271 | 272 | + if (t.is_chain) { 273 | + if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain)) 274 | + break; 275 | + 276 | + last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base, 277 | + c.last_tb.flags); 278 | + if (last_tb) { 279 | + tb_lock(); 280 | + if (!tb->invalid) { 281 | + tb_add_jump(last_tb, c.tb_exit, tb); 282 | + } 283 | + tb_unlock(); 284 | + } 285 | + } 286 | + 287 | } 288 | 289 | close(fd); 290 | --- ./qemu_mode/patches/afl-qemu-translate-inl.h 1970-01-01 01:00:00.000000000 +0100 291 | +++ ./qemu_mode/patches/afl-qemu-translate-inl.h 2018-10-22 14:07:07.142208000 +0200 292 | @@ -0,0 +1,82 @@ 293 | +/* 294 | + american fuzzy lop - high-performance binary-only instrumentation 295 | + ----------------------------------------------------------------- 296 | + 297 | + Written by Andrew Griffiths and 298 | + Michal Zalewski 299 | + 300 | + Idea & design very much by Andrew Griffiths. 301 | + 302 | + TCG instrumentation and block chaining support by Andrea Biondo 303 | + 304 | + 305 | + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. 306 | + 307 | + Licensed under the Apache License, Version 2.0 (the "License"); 308 | + you may not use this file except in compliance with the License. 309 | + You may obtain a copy of the License at: 310 | + 311 | + http://www.apache.org/licenses/LICENSE-2.0 312 | + 313 | + This code is a shim patched into the separately-distributed source 314 | + code of QEMU 2.10.0. It leverages the built-in QEMU tracing functionality 315 | + to implement AFL-style instrumentation and to take care of the remaining 316 | + parts of the AFL fork server logic. 317 | + 318 | + The resulting QEMU binary is essentially a standalone instrumentation 319 | + tool; for an example of how to leverage it for other purposes, you can 320 | + have a look at afl-showmap.c. 321 | + 322 | + */ 323 | + 324 | +#include "../../config.h" 325 | +#include "tcg-op.h" 326 | + 327 | +/* Declared in afl-qemu-cpu-inl.h */ 328 | +extern unsigned char *afl_area_ptr; 329 | +extern unsigned int afl_inst_rms; 330 | +extern abi_ulong afl_start_code, afl_end_code; 331 | + 332 | +/* Generates TCG code for AFL's tracing instrumentation. */ 333 | +static void afl_gen_trace(target_ulong cur_loc) 334 | +{ 335 | + static __thread target_ulong prev_loc; 336 | + TCGv index, count, new_prev_loc; 337 | + TCGv_ptr prev_loc_ptr, count_ptr; 338 | + 339 | + /* Optimize for cur_loc > afl_end_code, which is the most likely case on 340 | + Linux systems. */ 341 | + 342 | + if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr) 343 | + return; 344 | + 345 | + /* Looks like QEMU always maps to fixed locations, so ASAN is not a 346 | + concern. Phew. But instruction addresses may be aligned. Let's mangle 347 | + the value to get something quasi-uniform. */ 348 | + 349 | + cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); 350 | + cur_loc &= MAP_SIZE - 1; 351 | + 352 | + /* Implement probabilistic instrumentation by looking at scrambled block 353 | + address. This keeps the instrumented locations stable across runs. */ 354 | + 355 | + if (cur_loc >= afl_inst_rms) return; 356 | + 357 | + /* index = prev_loc ^ cur_loc */ 358 | + prev_loc_ptr = tcg_const_ptr(&prev_loc); 359 | + index = tcg_temp_new(); 360 | + tcg_gen_ld_tl(index, prev_loc_ptr, 0); 361 | + tcg_gen_xori_tl(index, index, cur_loc); 362 | + 363 | + /* afl_area_ptr[index]++ */ 364 | + count_ptr = tcg_const_ptr(afl_area_ptr); 365 | + tcg_gen_add_ptr(count_ptr, count_ptr, TCGV_NAT_TO_PTR(index)); 366 | + count = tcg_temp_new(); 367 | + tcg_gen_ld8u_tl(count, count_ptr, 0); 368 | + tcg_gen_addi_tl(count, count, 1); 369 | + tcg_gen_st8_tl(count, count_ptr, 0); 370 | + 371 | + /* prev_loc = cur_loc >> 1 */ 372 | + new_prev_loc = tcg_const_tl(cur_loc >> 1); 373 | + tcg_gen_st_tl(new_prev_loc, prev_loc_ptr, 0); 374 | +} 375 | --- ./qemu_mode/patches/configure.diff 1970-01-01 01:00:00.000000000 +0100 376 | +++ ./qemu_mode/patches/configure.diff 2018-10-22 14:07:07.157808000 +0200 377 | @@ -0,0 +1,11 @@ 378 | +--- a/configure 379 | ++++ b/configure 380 | +@@ -3855,7 +3855,7 @@ fi 381 | + # check if memfd is supported 382 | + memfd=no 383 | + cat > $TMPC << EOF 384 | +-#include 385 | ++#include 386 | + 387 | + int main(void) 388 | + { 389 | --- ./qemu_mode/patches/cpu-exec.diff 2018-03-04 12:24:46.023737400 +0100 390 | +++ ./qemu_mode/patches/cpu-exec.diff 2018-10-22 14:07:07.157808000 +0200 391 | @@ -1,5 +1,5 @@ 392 | ---- qemu-2.10.0-rc3-clean/accel/tcg/cpu-exec.c 2017-08-15 11:39:41.000000000 -0700 393 | -+++ qemu-2.10.0-rc3/accel/tcg/cpu-exec.c 2017-08-22 14:34:55.868730680 -0700 394 | +--- qemu-2.10.0-clean/accel/tcg/cpu-exec.c 2017-08-30 18:50:40.000000000 +0200 395 | ++++ qemu-2.10.0/accel/tcg/cpu-exec.c 2018-09-22 13:21:23.612068407 +0200 396 | @@ -36,6 +36,8 @@ 397 | #include "sysemu/cpus.h" 398 | #include "sysemu/replay.h" 399 | @@ -18,11 +18,37 @@ 400 | qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, 401 | "Trace %p [%d: " TARGET_FMT_lx "] %s\n", 402 | itb->tc_ptr, cpu->cpu_index, itb->pc, 403 | +@@ -337,7 +341,7 @@ 404 | + TranslationBlock *tb; 405 | + target_ulong cs_base, pc; 406 | + uint32_t flags; 407 | +- bool have_tb_lock = false; 408 | ++ bool have_tb_lock = false, was_translated = false, was_chained = false; 409 | + 410 | + /* we record a subset of the CPU state. It will 411 | + always be the same before a given translated block 412 | @@ -365,6 +369,7 @@ 413 | if (!tb) { 414 | /* if no translated code available, then translate it now */ 415 | tb = tb_gen_code(cpu, pc, cs_base, flags, 0); 416 | -+ AFL_QEMU_CPU_SNIPPET1; 417 | ++ was_translated = true; 418 | } 419 | 420 | mmap_unlock(); 421 | +@@ -390,11 +395,16 @@ 422 | + } 423 | + if (!tb->invalid) { 424 | + tb_add_jump(last_tb, tb_exit, tb); 425 | ++ was_chained = true; 426 | + } 427 | + } 428 | + if (have_tb_lock) { 429 | + tb_unlock(); 430 | + } 431 | ++ if (was_translated || was_chained) { 432 | ++ afl_request_tsl(pc, cs_base, flags, was_chained ? last_tb : NULL, 433 | ++ tb_exit); 434 | ++ } 435 | + return tb; 436 | + } 437 | + 438 | --- ./qemu_mode/patches/memfd.diff 1970-01-01 01:00:00.000000000 +0100 439 | +++ ./qemu_mode/patches/memfd.diff 2018-10-22 14:07:07.157808000 +0200 440 | @@ -0,0 +1,12 @@ 441 | +--- a/util/memfd.c 442 | ++++ b/util/memfd.c 443 | +@@ -31,9 +31,7 @@ 444 | + 445 | + #include "qemu/memfd.h" 446 | + 447 | +-#ifdef CONFIG_MEMFD 448 | +-#include 449 | +-#elif defined CONFIG_LINUX 450 | ++#if defined CONFIG_LINUX && !defined CONFIG_MEMFD 451 | + #include 452 | + #include 453 | --- ./qemu_mode/patches/translate-all.diff 1970-01-01 01:00:00.000000000 +0100 454 | +++ ./qemu_mode/patches/translate-all.diff 2018-10-22 14:07:07.157808000 +0200 455 | @@ -0,0 +1,19 @@ 456 | +--- a/accel/tcg/translate-all.c 2017-08-30 18:50:40.000000000 +0200 457 | ++++ b/accel/tcg/translate-all.c 2018-09-21 10:19:42.328766554 +0200 458 | +@@ -60,6 +60,8 @@ 459 | + #include "exec/log.h" 460 | + #include "sysemu/cpus.h" 461 | + 462 | ++#include "../patches/afl-qemu-translate-inl.h" 463 | ++ 464 | + /* #define DEBUG_TB_INVALIDATE */ 465 | + /* #define DEBUG_TB_FLUSH */ 466 | + /* make various TB consistency checks */ 467 | +@@ -1280,6 +1282,7 @@ 468 | + tcg_func_start(&tcg_ctx); 469 | + 470 | + tcg_ctx.cpu = ENV_GET_CPU(env); 471 | ++ afl_gen_trace(pc); 472 | + gen_intermediate_code(cpu, tb); 473 | + tcg_ctx.cpu = NULL; 474 | + 475 | -------------------------------------------------------------------------------- /laf-intel.diff: -------------------------------------------------------------------------------- 1 | diff -urN ./laf-intel.usage ./laf-intel.usage 2 | --- ./llvm_mode/README.laf-intel 1970-01-01 01:00:00.000000000 +0100 3 | +++ ./llvm_mode/README.laf-intel 2019-01-09 21:41:01.928365797 +0100 4 | @@ -0,0 +1,13 @@ 5 | +Usage 6 | +By default the passes will not run when you compile programs using 7 | +afl-clang-fast. Hence, you can use AFL as usual. To enable the passes you must 8 | +set environment variables before you compile the target project. 9 | + 10 | + 11 | +export LAF_SPLIT_SWITCHES=1 Enables the split-switches pass. 12 | + 13 | +export LAF_TRANSFORM_COMPARES=1 Enables the transform-compares pass (strcmp, memcmp, strncmp, strcasecmp, strncasecmp). 14 | + 15 | +export LAF_SPLIT_COMPARES=1 Enables the split-compares pass. By default it will split all compares with 16 | +a bit width <= 64 bits. You can change this behaviour by setting export LAF_SPLIT_COMPARES_BITW=. 17 | + 18 | diff -urN ./llvm_mode/afl-clang-fast.c ./llvm_mode/afl-clang-fast.c 19 | --- ./llvm_mode/afl-clang-fast.c 2017-01-25 03:51:26.000000000 +0100 20 | +++ ./llvm_mode/afl-clang-fast.c 2018-12-27 15:23:41.710341000 +0100 21 | @@ -119,6 +119,29 @@ 22 | 23 | http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards */ 24 | 25 | + // laf 26 | + if (getenv("LAF_SPLIT_SWITCHES")) { 27 | + cc_params[cc_par_cnt++] = "-Xclang"; 28 | + cc_params[cc_par_cnt++] = "-load"; 29 | + cc_params[cc_par_cnt++] = "-Xclang"; 30 | + cc_params[cc_par_cnt++] = alloc_printf("%s/split-switches-pass.so", obj_path); 31 | + } 32 | + 33 | + if (getenv("LAF_TRANSFORM_COMPARES")) { 34 | + cc_params[cc_par_cnt++] = "-Xclang"; 35 | + cc_params[cc_par_cnt++] = "-load"; 36 | + cc_params[cc_par_cnt++] = "-Xclang"; 37 | + cc_params[cc_par_cnt++] = alloc_printf("%s/compare-transform-pass.so", obj_path); 38 | + } 39 | + 40 | + if (getenv("LAF_SPLIT_COMPARES")) { 41 | + cc_params[cc_par_cnt++] = "-Xclang"; 42 | + cc_params[cc_par_cnt++] = "-load"; 43 | + cc_params[cc_par_cnt++] = "-Xclang"; 44 | + cc_params[cc_par_cnt++] = alloc_printf("%s/split-compares-pass.so", obj_path); 45 | + } 46 | + // /laf 47 | + 48 | #ifdef USE_TRACE_PC 49 | cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; 50 | cc_params[cc_par_cnt++] = "-mllvm"; 51 | diff -urN ./llvm_mode/compare-transform-pass.so.cc ./llvm_mode/compare-transform-pass.so.cc 52 | --- ./llvm_mode/compare-transform-pass.so.cc 1970-01-01 01:00:00.000000000 +0100 53 | +++ ./llvm_mode/compare-transform-pass.so.cc 2019-01-09 22:36:59.184886414 +0100 54 | @@ -0,0 +1,307 @@ 55 | +/* 56 | + * Copyright 2016 laf-intel 57 | + * 58 | + * Licensed under the Apache License, Version 2.0 (the "License"); 59 | + * you may not use this file except in compliance with the License. 60 | + * You may obtain a copy of the License at 61 | + * 62 | + * http://www.apache.org/licenses/LICENSE-2.0 63 | + * 64 | + * Unless required by applicable law or agreed to in writing, software 65 | + * distributed under the License is distributed on an "AS IS" BASIS, 66 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 67 | + * See the License for the specific language governing permissions and 68 | + * limitations under the License. 69 | + */ 70 | + 71 | +#include 72 | +#include 73 | +#include 74 | + 75 | +#include "llvm/ADT/Statistic.h" 76 | +#include "llvm/IR/IRBuilder.h" 77 | +#include "llvm/IR/LegacyPassManager.h" 78 | +#include "llvm/IR/Module.h" 79 | +#include "llvm/Support/Debug.h" 80 | +#include "llvm/Support/raw_ostream.h" 81 | +#include "llvm/Transforms/IPO/PassManagerBuilder.h" 82 | +#include "llvm/Transforms/Utils/BasicBlockUtils.h" 83 | +#include "llvm/IR/Verifier.h" 84 | +#include "llvm/Pass.h" 85 | +#include "llvm/Analysis/ValueTracking.h" 86 | + 87 | +#include 88 | + 89 | +using namespace llvm; 90 | + 91 | +namespace { 92 | + 93 | + class CompareTransform : public ModulePass { 94 | + 95 | + public: 96 | + static char ID; 97 | + CompareTransform() : ModulePass(ID) { 98 | + } 99 | + 100 | + bool runOnModule(Module &M) override; 101 | + 102 | +#if __clang_major__ < 4 103 | + const char * getPassName() const override { 104 | +#else 105 | + StringRef getPassName() const override { 106 | +#endif 107 | + return "transforms compare functions"; 108 | + } 109 | + private: 110 | + bool transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp 111 | + ,const bool processStrncmp, const bool processStrcasecmp, const bool processStrncasecmp); 112 | + }; 113 | +} 114 | + 115 | + 116 | +char CompareTransform::ID = 0; 117 | + 118 | +bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp 119 | + , const bool processStrncmp, const bool processStrcasecmp, const bool processStrncasecmp) { 120 | + 121 | + std::vector calls; 122 | + LLVMContext &C = M.getContext(); 123 | + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); 124 | + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); 125 | + IntegerType *Int64Ty = IntegerType::getInt64Ty(C); 126 | + Constant* c = M.getOrInsertFunction("tolower", 127 | + Int32Ty, 128 | + Int32Ty 129 | +#if __clang_major__ < 7 130 | + , nullptr 131 | +#endif 132 | + ); 133 | + Function* tolowerFn = cast(c); 134 | + 135 | + /* iterate over all functions, bbs and instruction and add suitable calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp */ 136 | + for (auto &F : M) { 137 | + for (auto &BB : F) { 138 | + for(auto &IN: BB) { 139 | + CallInst* callInst = nullptr; 140 | + 141 | + if ((callInst = dyn_cast(&IN))) { 142 | + 143 | + bool isStrcmp = processStrcmp; 144 | + bool isMemcmp = processMemcmp; 145 | + bool isStrncmp = processStrncmp; 146 | + bool isStrcasecmp = processStrcasecmp; 147 | + bool isStrncasecmp = processStrncasecmp; 148 | + 149 | + Function *Callee = callInst->getCalledFunction(); 150 | + if (!Callee) 151 | + continue; 152 | + if (callInst->getCallingConv() != llvm::CallingConv::C) 153 | + continue; 154 | + StringRef FuncName = Callee->getName(); 155 | + isStrcmp &= !FuncName.compare(StringRef("strcmp")); 156 | + isMemcmp &= !FuncName.compare(StringRef("memcmp")); 157 | + isStrncmp &= !FuncName.compare(StringRef("strncmp")); 158 | + isStrcasecmp &= !FuncName.compare(StringRef("strcasecmp")); 159 | + isStrncasecmp &= !FuncName.compare(StringRef("strncasecmp")); 160 | + 161 | + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp) 162 | + continue; 163 | + 164 | + /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function prototype */ 165 | + FunctionType *FT = Callee->getFunctionType(); 166 | + 167 | + 168 | + isStrcmp &= FT->getNumParams() == 2 && 169 | + FT->getReturnType()->isIntegerTy(32) && 170 | + FT->getParamType(0) == FT->getParamType(1) && 171 | + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); 172 | + isStrcasecmp &= FT->getNumParams() == 2 && 173 | + FT->getReturnType()->isIntegerTy(32) && 174 | + FT->getParamType(0) == FT->getParamType(1) && 175 | + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()); 176 | + isMemcmp &= FT->getNumParams() == 3 && 177 | + FT->getReturnType()->isIntegerTy(32) && 178 | + FT->getParamType(0)->isPointerTy() && 179 | + FT->getParamType(1)->isPointerTy() && 180 | + FT->getParamType(2)->isIntegerTy(); 181 | + isStrncmp &= FT->getNumParams() == 3 && 182 | + FT->getReturnType()->isIntegerTy(32) && 183 | + FT->getParamType(0) == FT->getParamType(1) && 184 | + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) && 185 | + FT->getParamType(2)->isIntegerTy(); 186 | + isStrncasecmp &= FT->getNumParams() == 3 && 187 | + FT->getReturnType()->isIntegerTy(32) && 188 | + FT->getParamType(0) == FT->getParamType(1) && 189 | + FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) && 190 | + FT->getParamType(2)->isIntegerTy(); 191 | + 192 | + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp) 193 | + continue; 194 | + 195 | + /* is a str{n,}{case,}cmp/memcmp, check is we have 196 | + * str{case,}cmp(x, "const") or str{case,}cmp("const", x) 197 | + * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, ..) 198 | + * memcmp(x, "const", ..) or memcmp("const", x, ..) */ 199 | + Value *Str1P = callInst->getArgOperand(0), *Str2P = callInst->getArgOperand(1); 200 | + StringRef Str1, Str2; 201 | + bool HasStr1 = getConstantStringInfo(Str1P, Str1); 202 | + bool HasStr2 = getConstantStringInfo(Str2P, Str2); 203 | + 204 | + /* handle cases of one string is const, one string is variable */ 205 | + if (!(HasStr1 ^ HasStr2)) 206 | + continue; 207 | + 208 | + if (isMemcmp || isStrncmp || isStrncasecmp) { 209 | + /* check if third operand is a constant integer 210 | + * strlen("constStr") and sizeof() are treated as constant */ 211 | + Value *op2 = callInst->getArgOperand(2); 212 | + ConstantInt* ilen = dyn_cast(op2); 213 | + if (!ilen) 214 | + continue; 215 | + /* final precaution: if size of compare is larger than constant string skip it*/ 216 | + uint64_t literalLength = HasStr1 ? GetStringLength(Str1P) : GetStringLength(Str2P); 217 | + if (literalLength < ilen->getZExtValue()) 218 | + continue; 219 | + } 220 | + 221 | + calls.push_back(callInst); 222 | + } 223 | + } 224 | + } 225 | + } 226 | + 227 | + if (!calls.size()) 228 | + return false; 229 | + errs() << "Replacing " << calls.size() << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n"; 230 | + 231 | + for (auto &callInst: calls) { 232 | + 233 | + Value *Str1P = callInst->getArgOperand(0), *Str2P = callInst->getArgOperand(1); 234 | + StringRef Str1, Str2, ConstStr; 235 | + Value *VarStr; 236 | + bool HasStr1 = getConstantStringInfo(Str1P, Str1); 237 | + getConstantStringInfo(Str2P, Str2); 238 | + uint64_t constLen, sizedLen; 239 | + bool isMemcmp = !callInst->getCalledFunction()->getName().compare(StringRef("memcmp")); 240 | + bool isSizedcmp = isMemcmp 241 | + || !callInst->getCalledFunction()->getName().compare(StringRef("strncmp")) 242 | + || !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp")); 243 | + bool isCaseInsensitive = !callInst->getCalledFunction()->getName().compare(StringRef("strcasecmp")) 244 | + || !callInst->getCalledFunction()->getName().compare(StringRef("strncasecmp")); 245 | + 246 | + if (isSizedcmp) { 247 | + Value *op2 = callInst->getArgOperand(2); 248 | + ConstantInt* ilen = dyn_cast(op2); 249 | + sizedLen = ilen->getZExtValue(); 250 | + } 251 | + 252 | + if (HasStr1) { 253 | + ConstStr = Str1; 254 | + VarStr = Str2P; 255 | + constLen = isMemcmp ? sizedLen : GetStringLength(Str1P); 256 | + } 257 | + else { 258 | + ConstStr = Str2; 259 | + VarStr = Str1P; 260 | + constLen = isMemcmp ? sizedLen : GetStringLength(Str2P); 261 | + } 262 | + if (isSizedcmp && constLen > sizedLen) { 263 | + constLen = sizedLen; 264 | + } 265 | + 266 | + errs() << callInst->getCalledFunction()->getName() << ": len " << constLen << ": " << ConstStr << "\n"; 267 | + 268 | + /* split before the call instruction */ 269 | + BasicBlock *bb = callInst->getParent(); 270 | + BasicBlock *end_bb = bb->splitBasicBlock(BasicBlock::iterator(callInst)); 271 | + BasicBlock *next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); 272 | + BranchInst::Create(end_bb, next_bb); 273 | + PHINode *PN = PHINode::Create(Int32Ty, constLen + 1, "cmp_phi"); 274 | + 275 | + TerminatorInst *term = bb->getTerminator(); 276 | + BranchInst::Create(next_bb, bb); 277 | + term->eraseFromParent(); 278 | + 279 | + for (uint64_t i = 0; i < constLen; i++) { 280 | + 281 | + BasicBlock *cur_bb = next_bb; 282 | + 283 | + char c = isCaseInsensitive ? tolower(ConstStr[i]) : ConstStr[i]; 284 | + 285 | + 286 | + BasicBlock::iterator IP = next_bb->getFirstInsertionPt(); 287 | + IRBuilder<> IRB(&*IP); 288 | + 289 | + Value* v = ConstantInt::get(Int64Ty, i); 290 | + Value *ele = IRB.CreateInBoundsGEP(VarStr, v, "empty"); 291 | + Value *load = IRB.CreateLoad(ele); 292 | + if (isCaseInsensitive) { 293 | + // load >= 'A' && load <= 'Z' ? load | 0x020 : load 294 | + std::vector args; 295 | + args.push_back(load); 296 | + load = IRB.CreateCall(tolowerFn, args, "tmp"); 297 | + } 298 | + Value *isub; 299 | + if (HasStr1) 300 | + isub = IRB.CreateSub(ConstantInt::get(Int8Ty, c), load); 301 | + else 302 | + isub = IRB.CreateSub(load, ConstantInt::get(Int8Ty, c)); 303 | + 304 | + Value *sext = IRB.CreateSExt(isub, Int32Ty); 305 | + PN->addIncoming(sext, cur_bb); 306 | + 307 | + 308 | + if (i < constLen - 1) { 309 | + next_bb = BasicBlock::Create(C, "cmp_added", end_bb->getParent(), end_bb); 310 | + BranchInst::Create(end_bb, next_bb); 311 | + 312 | + TerminatorInst *term = cur_bb->getTerminator(); 313 | + Value *icmp = IRB.CreateICmpEQ(isub, ConstantInt::get(Int8Ty, 0)); 314 | + IRB.CreateCondBr(icmp, next_bb, end_bb); 315 | + term->eraseFromParent(); 316 | + } else { 317 | + //IRB.CreateBr(end_bb); 318 | + } 319 | + 320 | + //add offset to varstr 321 | + //create load 322 | + //create signed isub 323 | + //create icmp 324 | + //create jcc 325 | + //create next_bb 326 | + } 327 | + 328 | + /* since the call is the first instruction of the bb it is safe to 329 | + * replace it with a phi instruction */ 330 | + BasicBlock::iterator ii(callInst); 331 | + ReplaceInstWithInst(callInst->getParent()->getInstList(), ii, PN); 332 | + } 333 | + 334 | + 335 | + return true; 336 | +} 337 | + 338 | +bool CompareTransform::runOnModule(Module &M) { 339 | + 340 | + llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, extended by heiko@hexco.de\n"; 341 | + transformCmps(M, true, true, true, true, true); 342 | + verifyModule(M); 343 | + 344 | + return true; 345 | +} 346 | + 347 | +static void registerCompTransPass(const PassManagerBuilder &, 348 | + legacy::PassManagerBase &PM) { 349 | + 350 | + auto p = new CompareTransform(); 351 | + PM.add(p); 352 | + 353 | +} 354 | + 355 | +static RegisterStandardPasses RegisterCompTransPass( 356 | + PassManagerBuilder::EP_OptimizerLast, registerCompTransPass); 357 | + 358 | +static RegisterStandardPasses RegisterCompTransPass0( 359 | + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); 360 | + 361 | diff -urN ./llvm_mode/Makefile ./llvm_mode/Makefile 362 | --- ./llvm_mode/Makefile 2016-06-24 04:38:49.000000000 +0200 363 | +++ ./llvm_mode/Makefile 2018-12-27 15:19:18.086346000 +0100 364 | @@ -55,9 +55,9 @@ 365 | endif 366 | 367 | ifndef AFL_TRACE_PC 368 | - PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o 369 | + PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so 370 | else 371 | - PROGS = ../afl-clang-fast ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o 372 | + PROGS = ../afl-clang-fast ../afl-llvm-rt.o ../afl-llvm-rt-32.o ../afl-llvm-rt-64.o ../compare-transform-pass.so ../split-compares-pass.so ../split-switches-pass.so 373 | endif 374 | 375 | all: test_deps $(PROGS) test_build all_done 376 | @@ -82,6 +82,16 @@ 377 | ../afl-llvm-pass.so: afl-llvm-pass.so.cc | test_deps 378 | $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) 379 | 380 | +# laf 381 | +../split-switches-pass.so: split-switches-pass.so.cc | test_deps 382 | + $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) 383 | +../compare-transform-pass.so: compare-transform-pass.so.cc | test_deps 384 | + $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) 385 | +../split-compares-pass.so: split-compares-pass.so.cc | test_deps 386 | + $(CXX) $(CLANG_CFL) -shared $< -o $@ $(CLANG_LFL) 387 | + 388 | +# /laf 389 | + 390 | ../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps 391 | $(CC) $(CFLAGS) -fPIC -c $< -o $@ 392 | 393 | diff -urN ./llvm_mode/split-compares-pass.so.cc ./llvm_mode/split-compares-pass.so.cc 394 | --- ./llvm_mode/split-compares-pass.so.cc 1970-01-01 01:00:00.000000000 +0100 395 | +++ ./llvm_mode/split-compares-pass.so.cc 2019-01-08 17:17:05.564960249 +0100 396 | @@ -0,0 +1,527 @@ 397 | +/* 398 | + * Copyright 2016 laf-intel 399 | + * 400 | + * Licensed under the Apache License, Version 2.0 (the "License"); 401 | + * you may not use this file except in compliance with the License. 402 | + * You may obtain a copy of the License at 403 | + * 404 | + * http://www.apache.org/licenses/LICENSE-2.0 405 | + * 406 | + * Unless required by applicable law or agreed to in writing, software 407 | + * distributed under the License is distributed on an "AS IS" BASIS, 408 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 409 | + * See the License for the specific language governing permissions and 410 | + * limitations under the License. 411 | + */ 412 | + 413 | +#include "llvm/Pass.h" 414 | +#include "llvm/Support/raw_ostream.h" 415 | +#include "llvm/IR/LegacyPassManager.h" 416 | +#include "llvm/Transforms/IPO/PassManagerBuilder.h" 417 | +#include "llvm/Transforms/Utils/BasicBlockUtils.h" 418 | +#include "llvm/IR/Verifier.h" 419 | +#include "llvm/IR/Module.h" 420 | + 421 | +#include "llvm/IR/IRBuilder.h" 422 | + 423 | +using namespace llvm; 424 | + 425 | +namespace { 426 | + class SplitComparesTransform : public ModulePass { 427 | + public: 428 | + static char ID; 429 | + SplitComparesTransform() : ModulePass(ID) {} 430 | + 431 | + bool runOnModule(Module &M) override; 432 | +#if __clang_major__ >= 4 433 | + StringRef getPassName() const override { 434 | +#else 435 | + const char * getPassName() const override { 436 | +#endif 437 | + return "simplifies and splits ICMP instructions"; 438 | + } 439 | + private: 440 | + bool splitCompares(Module &M, unsigned bitw); 441 | + bool simplifyCompares(Module &M); 442 | + bool simplifySignedness(Module &M); 443 | + 444 | + }; 445 | +} 446 | + 447 | +char SplitComparesTransform::ID = 0; 448 | + 449 | +/* This function splits ICMP instructions with xGE or xLE predicates into two 450 | + * ICMP instructions with predicate xGT or xLT and EQ */ 451 | +bool SplitComparesTransform::simplifyCompares(Module &M) { 452 | + LLVMContext &C = M.getContext(); 453 | + std::vector icomps; 454 | + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); 455 | + 456 | + /* iterate over all functions, bbs and instruction and add 457 | + * all integer comparisons with >= and <= predicates to the icomps vector */ 458 | + for (auto &F : M) { 459 | + for (auto &BB : F) { 460 | + for (auto &IN: BB) { 461 | + CmpInst* selectcmpInst = nullptr; 462 | + 463 | + if ((selectcmpInst = dyn_cast(&IN))) { 464 | + 465 | + if (selectcmpInst->getPredicate() != CmpInst::ICMP_UGE && 466 | + selectcmpInst->getPredicate() != CmpInst::ICMP_SGE && 467 | + selectcmpInst->getPredicate() != CmpInst::ICMP_ULE && 468 | + selectcmpInst->getPredicate() != CmpInst::ICMP_SLE ) { 469 | + continue; 470 | + } 471 | + 472 | + auto op0 = selectcmpInst->getOperand(0); 473 | + auto op1 = selectcmpInst->getOperand(1); 474 | + 475 | + IntegerType* intTyOp0 = dyn_cast(op0->getType()); 476 | + IntegerType* intTyOp1 = dyn_cast(op1->getType()); 477 | + 478 | + /* this is probably not needed but we do it anyway */ 479 | + if (!intTyOp0 || !intTyOp1) { 480 | + continue; 481 | + } 482 | + 483 | + icomps.push_back(selectcmpInst); 484 | + } 485 | + } 486 | + } 487 | + } 488 | + 489 | + if (!icomps.size()) { 490 | + return false; 491 | + } 492 | + 493 | + 494 | + for (auto &IcmpInst: icomps) { 495 | + BasicBlock* bb = IcmpInst->getParent(); 496 | + 497 | + auto op0 = IcmpInst->getOperand(0); 498 | + auto op1 = IcmpInst->getOperand(1); 499 | + 500 | + /* find out what the new predicate is going to be */ 501 | + auto pred = dyn_cast(IcmpInst)->getPredicate(); 502 | + CmpInst::Predicate new_pred; 503 | + switch(pred) { 504 | + case CmpInst::ICMP_UGE: 505 | + new_pred = CmpInst::ICMP_UGT; 506 | + break; 507 | + case CmpInst::ICMP_SGE: 508 | + new_pred = CmpInst::ICMP_SGT; 509 | + break; 510 | + case CmpInst::ICMP_ULE: 511 | + new_pred = CmpInst::ICMP_ULT; 512 | + break; 513 | + case CmpInst::ICMP_SLE: 514 | + new_pred = CmpInst::ICMP_SLT; 515 | + break; 516 | + default: // keep the compiler happy 517 | + continue; 518 | + } 519 | + 520 | + /* split before the icmp instruction */ 521 | + BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); 522 | + 523 | + /* the old bb now contains a unconditional jump to the new one (end_bb) 524 | + * we need to delete it later */ 525 | + 526 | + /* create the ICMP instruction with new_pred and add it to the old basic 527 | + * block bb it is now at the position where the old IcmpInst was */ 528 | + Instruction* icmp_np; 529 | + icmp_np = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); 530 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_np); 531 | + 532 | + /* create a new basic block which holds the new EQ icmp */ 533 | + Instruction *icmp_eq; 534 | + /* insert middle_bb before end_bb */ 535 | + BasicBlock* middle_bb = BasicBlock::Create(C, "injected", 536 | + end_bb->getParent(), end_bb); 537 | + icmp_eq = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, op0, op1); 538 | + middle_bb->getInstList().push_back(icmp_eq); 539 | + /* add an unconditional branch to the end of middle_bb with destination 540 | + * end_bb */ 541 | + BranchInst::Create(end_bb, middle_bb); 542 | + 543 | + /* replace the uncond branch with a conditional one, which depends on the 544 | + * new_pred icmp. True goes to end, false to the middle (injected) bb */ 545 | + auto term = bb->getTerminator(); 546 | + BranchInst::Create(end_bb, middle_bb, icmp_np, bb); 547 | + term->eraseFromParent(); 548 | + 549 | + 550 | + /* replace the old IcmpInst (which is the first inst in end_bb) with a PHI 551 | + * inst to wire up the loose ends */ 552 | + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); 553 | + /* the first result depends on the outcome of icmp_eq */ 554 | + PN->addIncoming(icmp_eq, middle_bb); 555 | + /* if the source was the original bb we know that the icmp_np yielded true 556 | + * hence we can hardcode this value */ 557 | + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); 558 | + /* replace the old IcmpInst with our new and shiny PHI inst */ 559 | + BasicBlock::iterator ii(IcmpInst); 560 | + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); 561 | + } 562 | + 563 | + return true; 564 | +} 565 | + 566 | +/* this function transforms signed compares to equivalent unsigned compares */ 567 | +bool SplitComparesTransform::simplifySignedness(Module &M) { 568 | + LLVMContext &C = M.getContext(); 569 | + std::vector icomps; 570 | + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); 571 | + 572 | + /* iterate over all functions, bbs and instruction and add 573 | + * all signed compares to icomps vector */ 574 | + for (auto &F : M) { 575 | + for (auto &BB : F) { 576 | + for(auto &IN: BB) { 577 | + CmpInst* selectcmpInst = nullptr; 578 | + 579 | + if ((selectcmpInst = dyn_cast(&IN))) { 580 | + 581 | + if (selectcmpInst->getPredicate() != CmpInst::ICMP_SGT && 582 | + selectcmpInst->getPredicate() != CmpInst::ICMP_SLT 583 | + ) { 584 | + continue; 585 | + } 586 | + 587 | + auto op0 = selectcmpInst->getOperand(0); 588 | + auto op1 = selectcmpInst->getOperand(1); 589 | + 590 | + IntegerType* intTyOp0 = dyn_cast(op0->getType()); 591 | + IntegerType* intTyOp1 = dyn_cast(op1->getType()); 592 | + 593 | + /* see above */ 594 | + if (!intTyOp0 || !intTyOp1) { 595 | + continue; 596 | + } 597 | + 598 | + /* i think this is not possible but to lazy to look it up */ 599 | + if (intTyOp0->getBitWidth() != intTyOp1->getBitWidth()) { 600 | + continue; 601 | + } 602 | + 603 | + icomps.push_back(selectcmpInst); 604 | + } 605 | + } 606 | + } 607 | + } 608 | + 609 | + if (!icomps.size()) { 610 | + return false; 611 | + } 612 | + 613 | + for (auto &IcmpInst: icomps) { 614 | + BasicBlock* bb = IcmpInst->getParent(); 615 | + 616 | + auto op0 = IcmpInst->getOperand(0); 617 | + auto op1 = IcmpInst->getOperand(1); 618 | + 619 | + IntegerType* intTyOp0 = dyn_cast(op0->getType()); 620 | + unsigned bitw = intTyOp0->getBitWidth(); 621 | + IntegerType *IntType = IntegerType::get(C, bitw); 622 | + 623 | + 624 | + /* get the new predicate */ 625 | + auto pred = dyn_cast(IcmpInst)->getPredicate(); 626 | + CmpInst::Predicate new_pred; 627 | + if (pred == CmpInst::ICMP_SGT) { 628 | + new_pred = CmpInst::ICMP_UGT; 629 | + } else { 630 | + new_pred = CmpInst::ICMP_ULT; 631 | + } 632 | + 633 | + BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); 634 | + 635 | + /* create a 1 bit compare for the sign bit. to do this shift and trunc 636 | + * the original operands so only the first bit remains.*/ 637 | + Instruction *s_op0, *t_op0, *s_op1, *t_op1, *icmp_sign_bit; 638 | + 639 | + s_op0 = BinaryOperator::Create(Instruction::LShr, op0, ConstantInt::get(IntType, bitw - 1)); 640 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0); 641 | + t_op0 = new TruncInst(s_op0, Int1Ty); 642 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op0); 643 | + 644 | + s_op1 = BinaryOperator::Create(Instruction::LShr, op1, ConstantInt::get(IntType, bitw - 1)); 645 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1); 646 | + t_op1 = new TruncInst(s_op1, Int1Ty); 647 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), t_op1); 648 | + 649 | + /* compare of the sign bits */ 650 | + icmp_sign_bit = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_EQ, t_op0, t_op1); 651 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_sign_bit); 652 | + 653 | + /* create a new basic block which is executed if the signedness bit is 654 | + * different */ 655 | + Instruction *icmp_inv_sig_cmp; 656 | + BasicBlock* sign_bb = BasicBlock::Create(C, "sign", end_bb->getParent(), end_bb); 657 | + if (pred == CmpInst::ICMP_SGT) { 658 | + /* if we check for > and the op0 positiv and op1 negative then the final 659 | + * result is true. if op0 negative and op1 pos, the cmp must result 660 | + * in false 661 | + */ 662 | + icmp_inv_sig_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, t_op0, t_op1); 663 | + } else { 664 | + /* just the inverse of the above statement */ 665 | + icmp_inv_sig_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, t_op0, t_op1); 666 | + } 667 | + sign_bb->getInstList().push_back(icmp_inv_sig_cmp); 668 | + BranchInst::Create(end_bb, sign_bb); 669 | + 670 | + /* create a new bb which is executed if signedness is equal */ 671 | + Instruction *icmp_usign_cmp; 672 | + BasicBlock* middle_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); 673 | + /* we can do a normal unsigned compare now */ 674 | + icmp_usign_cmp = CmpInst::Create(Instruction::ICmp, new_pred, op0, op1); 675 | + middle_bb->getInstList().push_back(icmp_usign_cmp); 676 | + BranchInst::Create(end_bb, middle_bb); 677 | + 678 | + auto term = bb->getTerminator(); 679 | + /* if the sign is eq do a normal unsigned cmp, else we have to check the 680 | + * signedness bit */ 681 | + BranchInst::Create(middle_bb, sign_bb, icmp_sign_bit, bb); 682 | + term->eraseFromParent(); 683 | + 684 | + 685 | + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); 686 | + 687 | + PN->addIncoming(icmp_usign_cmp, middle_bb); 688 | + PN->addIncoming(icmp_inv_sig_cmp, sign_bb); 689 | + 690 | + BasicBlock::iterator ii(IcmpInst); 691 | + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); 692 | + } 693 | + 694 | + return true; 695 | +} 696 | + 697 | +/* splits icmps of size bitw into two nested icmps with bitw/2 size each */ 698 | +bool SplitComparesTransform::splitCompares(Module &M, unsigned bitw) { 699 | + LLVMContext &C = M.getContext(); 700 | + 701 | + IntegerType *Int1Ty = IntegerType::getInt1Ty(C); 702 | + IntegerType *OldIntType = IntegerType::get(C, bitw); 703 | + IntegerType *NewIntType = IntegerType::get(C, bitw / 2); 704 | + 705 | + std::vector icomps; 706 | + 707 | + if (bitw % 2) { 708 | + return false; 709 | + } 710 | + 711 | + /* not supported yet */ 712 | + if (bitw > 64) { 713 | + return false; 714 | + } 715 | + 716 | + /* get all EQ, NE, UGT, and ULT icmps of width bitw. if the other two 717 | + * unctions were executed only these four predicates should exist */ 718 | + for (auto &F : M) { 719 | + for (auto &BB : F) { 720 | + for(auto &IN: BB) { 721 | + CmpInst* selectcmpInst = nullptr; 722 | + 723 | + if ((selectcmpInst = dyn_cast(&IN))) { 724 | + 725 | + if(selectcmpInst->getPredicate() != CmpInst::ICMP_EQ && 726 | + selectcmpInst->getPredicate() != CmpInst::ICMP_NE && 727 | + selectcmpInst->getPredicate() != CmpInst::ICMP_UGT && 728 | + selectcmpInst->getPredicate() != CmpInst::ICMP_ULT 729 | + ) { 730 | + continue; 731 | + } 732 | + 733 | + auto op0 = selectcmpInst->getOperand(0); 734 | + auto op1 = selectcmpInst->getOperand(1); 735 | + 736 | + IntegerType* intTyOp0 = dyn_cast(op0->getType()); 737 | + IntegerType* intTyOp1 = dyn_cast(op1->getType()); 738 | + 739 | + if (!intTyOp0 || !intTyOp1) { 740 | + continue; 741 | + } 742 | + 743 | + /* check if the bitwidths are the one we are looking for */ 744 | + if (intTyOp0->getBitWidth() != bitw || intTyOp1->getBitWidth() != bitw) { 745 | + continue; 746 | + } 747 | + 748 | + icomps.push_back(selectcmpInst); 749 | + } 750 | + } 751 | + } 752 | + } 753 | + 754 | + if (!icomps.size()) { 755 | + return false; 756 | + } 757 | + 758 | + for (auto &IcmpInst: icomps) { 759 | + BasicBlock* bb = IcmpInst->getParent(); 760 | + 761 | + auto op0 = IcmpInst->getOperand(0); 762 | + auto op1 = IcmpInst->getOperand(1); 763 | + 764 | + auto pred = dyn_cast(IcmpInst)->getPredicate(); 765 | + 766 | + BasicBlock* end_bb = bb->splitBasicBlock(BasicBlock::iterator(IcmpInst)); 767 | + 768 | + /* create the comparison of the top halfs of the original operands */ 769 | + Instruction *s_op0, *op0_high, *s_op1, *op1_high, *icmp_high; 770 | + 771 | + s_op0 = BinaryOperator::Create(Instruction::LShr, op0, ConstantInt::get(OldIntType, bitw / 2)); 772 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op0); 773 | + op0_high = new TruncInst(s_op0, NewIntType); 774 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), op0_high); 775 | + 776 | + s_op1 = BinaryOperator::Create(Instruction::LShr, op1, ConstantInt::get(OldIntType, bitw / 2)); 777 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), s_op1); 778 | + op1_high = new TruncInst(s_op1, NewIntType); 779 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), op1_high); 780 | + 781 | + icmp_high = CmpInst::Create(Instruction::ICmp, pred, op0_high, op1_high); 782 | + bb->getInstList().insert(bb->getTerminator()->getIterator(), icmp_high); 783 | + 784 | + /* now we have to destinguish between == != and > < */ 785 | + if (pred == CmpInst::ICMP_EQ || pred == CmpInst::ICMP_NE) { 786 | + /* transformation for == and != icmps */ 787 | + 788 | + /* create a compare for the lower half of the original operands */ 789 | + Instruction *op0_low, *op1_low, *icmp_low; 790 | + BasicBlock* cmp_low_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); 791 | + 792 | + op0_low = new TruncInst(op0, NewIntType); 793 | + cmp_low_bb->getInstList().push_back(op0_low); 794 | + 795 | + op1_low = new TruncInst(op1, NewIntType); 796 | + cmp_low_bb->getInstList().push_back(op1_low); 797 | + 798 | + icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); 799 | + cmp_low_bb->getInstList().push_back(icmp_low); 800 | + BranchInst::Create(end_bb, cmp_low_bb); 801 | + 802 | + /* dependant on the cmp of the high parts go to the end or go on with 803 | + * the comparison */ 804 | + auto term = bb->getTerminator(); 805 | + if (pred == CmpInst::ICMP_EQ) { 806 | + BranchInst::Create(cmp_low_bb, end_bb, icmp_high, bb); 807 | + } else { 808 | + /* CmpInst::ICMP_NE */ 809 | + BranchInst::Create(end_bb, cmp_low_bb, icmp_high, bb); 810 | + } 811 | + term->eraseFromParent(); 812 | + 813 | + /* create the PHI and connect the edges accordingly */ 814 | + PHINode *PN = PHINode::Create(Int1Ty, 2, ""); 815 | + PN->addIncoming(icmp_low, cmp_low_bb); 816 | + if (pred == CmpInst::ICMP_EQ) { 817 | + PN->addIncoming(ConstantInt::get(Int1Ty, 0), bb); 818 | + } else { 819 | + /* CmpInst::ICMP_NE */ 820 | + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); 821 | + } 822 | + 823 | + /* replace the old icmp with the new PHI */ 824 | + BasicBlock::iterator ii(IcmpInst); 825 | + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); 826 | + 827 | + } else { 828 | + /* CmpInst::ICMP_UGT and CmpInst::ICMP_ULT */ 829 | + /* transformations for < and > */ 830 | + 831 | + /* create a basic block which checks for the inverse predicate. 832 | + * if this is true we can go to the end if not we have to got to the 833 | + * bb which checks the lower half of the operands */ 834 | + Instruction *icmp_inv_cmp, *op0_low, *op1_low, *icmp_low; 835 | + BasicBlock* inv_cmp_bb = BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); 836 | + if (pred == CmpInst::ICMP_UGT) { 837 | + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, op0_high, op1_high); 838 | + } else { 839 | + icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_UGT, op0_high, op1_high); 840 | + } 841 | + inv_cmp_bb->getInstList().push_back(icmp_inv_cmp); 842 | + 843 | + auto term = bb->getTerminator(); 844 | + term->eraseFromParent(); 845 | + BranchInst::Create(end_bb, inv_cmp_bb, icmp_high, bb); 846 | + 847 | + /* create a bb which handles the cmp of the lower halfs */ 848 | + BasicBlock* cmp_low_bb = BasicBlock::Create(C, "injected", end_bb->getParent(), end_bb); 849 | + op0_low = new TruncInst(op0, NewIntType); 850 | + cmp_low_bb->getInstList().push_back(op0_low); 851 | + op1_low = new TruncInst(op1, NewIntType); 852 | + cmp_low_bb->getInstList().push_back(op1_low); 853 | + 854 | + icmp_low = CmpInst::Create(Instruction::ICmp, pred, op0_low, op1_low); 855 | + cmp_low_bb->getInstList().push_back(icmp_low); 856 | + BranchInst::Create(end_bb, cmp_low_bb); 857 | + 858 | + BranchInst::Create(end_bb, cmp_low_bb, icmp_inv_cmp, inv_cmp_bb); 859 | + 860 | + PHINode *PN = PHINode::Create(Int1Ty, 3); 861 | + PN->addIncoming(icmp_low, cmp_low_bb); 862 | + PN->addIncoming(ConstantInt::get(Int1Ty, 1), bb); 863 | + PN->addIncoming(ConstantInt::get(Int1Ty, 0), inv_cmp_bb); 864 | + 865 | + BasicBlock::iterator ii(IcmpInst); 866 | + ReplaceInstWithInst(IcmpInst->getParent()->getInstList(), ii, PN); 867 | + } 868 | + } 869 | + return true; 870 | +} 871 | + 872 | +bool SplitComparesTransform::runOnModule(Module &M) { 873 | + int bitw = 64; 874 | + 875 | + char* bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); 876 | + if (bitw_env) { 877 | + bitw = atoi(bitw_env); 878 | + } 879 | + 880 | + simplifyCompares(M); 881 | + 882 | + simplifySignedness(M); 883 | + 884 | + errs() << "Split-compare-pass by laf.intel@gmail.com\n"; 885 | + 886 | + switch (bitw) { 887 | + case 64: 888 | + errs() << "Running split-compare-pass " << 64 << "\n"; 889 | + splitCompares(M, 64); 890 | + 891 | + [[clang::fallthrough]]; 892 | + /* fallthrough */ 893 | + case 32: 894 | + errs() << "Running split-compare-pass " << 32 << "\n"; 895 | + splitCompares(M, 32); 896 | + 897 | + [[clang::fallthrough]]; 898 | + /* fallthrough */ 899 | + case 16: 900 | + errs() << "Running split-compare-pass " << 16 << "\n"; 901 | + splitCompares(M, 16); 902 | + break; 903 | + 904 | + default: 905 | + errs() << "NOT Running split-compare-pass \n"; 906 | + return false; 907 | + break; 908 | + } 909 | + 910 | + verifyModule(M); 911 | + return true; 912 | +} 913 | + 914 | +static void registerSplitComparesPass(const PassManagerBuilder &, 915 | + legacy::PassManagerBase &PM) { 916 | + PM.add(new SplitComparesTransform()); 917 | +} 918 | + 919 | +static RegisterStandardPasses RegisterSplitComparesPass( 920 | + PassManagerBuilder::EP_OptimizerLast, registerSplitComparesPass); 921 | + 922 | +static RegisterStandardPasses RegisterSplitComparesTransPass0( 923 | + PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); 924 | diff -urN ./llvm_mode/split-switches-pass.so.cc ./llvm_mode/split-switches-pass.so.cc 925 | --- ./llvm_mode/split-switches-pass.so.cc 1970-01-01 01:00:00.000000000 +0100 926 | +++ ./llvm_mode/split-switches-pass.so.cc 2019-01-08 17:17:23.073227728 +0100 927 | @@ -0,0 +1,315 @@ 928 | +/* 929 | + * Copyright 2016 laf-intel 930 | + * 931 | + * Licensed under the Apache License, Version 2.0 (the "License"); 932 | + * you may not use this file except in compliance with the License. 933 | + * You may obtain a copy of the License at 934 | + * 935 | + * http://www.apache.org/licenses/LICENSE-2.0 936 | + * 937 | + * Unless required by applicable law or agreed to in writing, software 938 | + * distributed under the License is distributed on an "AS IS" BASIS, 939 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 940 | + * See the License for the specific language governing permissions and 941 | + * limitations under the License. 942 | + */ 943 | + 944 | +#include 945 | +#include 946 | +#include 947 | + 948 | +#include "llvm/ADT/Statistic.h" 949 | +#include "llvm/IR/IRBuilder.h" 950 | +#include "llvm/IR/LegacyPassManager.h" 951 | +#include "llvm/IR/Module.h" 952 | +#include "llvm/Support/Debug.h" 953 | +#include "llvm/Support/raw_ostream.h" 954 | +#include "llvm/Transforms/IPO/PassManagerBuilder.h" 955 | +#include "llvm/Transforms/Utils/BasicBlockUtils.h" 956 | +#include "llvm/IR/Verifier.h" 957 | +#include "llvm/Pass.h" 958 | +#include "llvm/Analysis/ValueTracking.h" 959 | + 960 | +#include 961 | + 962 | +using namespace llvm; 963 | + 964 | +namespace { 965 | + 966 | + class SplitSwitchesTransform : public ModulePass { 967 | + 968 | + public: 969 | + static char ID; 970 | + SplitSwitchesTransform() : ModulePass(ID) { 971 | + } 972 | + 973 | + bool runOnModule(Module &M) override; 974 | + 975 | +#if __clang_major__ >= 4 976 | + StringRef getPassName() const override { 977 | +#else 978 | + const char * getPassName() const override { 979 | +#endif 980 | + return "splits switch constructs"; 981 | + } 982 | + struct CaseExpr { 983 | + ConstantInt* Val; 984 | + BasicBlock* BB; 985 | + 986 | + CaseExpr(ConstantInt *val = nullptr, BasicBlock *bb = nullptr) : 987 | + Val(val), BB(bb) { } 988 | + }; 989 | + 990 | + typedef std::vector CaseVector; 991 | + 992 | + private: 993 | + bool splitSwitches(Module &M); 994 | + bool transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp); 995 | + BasicBlock* switchConvert(CaseVector Cases, std::vector bytesChecked, 996 | + BasicBlock* OrigBlock, BasicBlock* NewDefault, 997 | + Value* Val, unsigned level); 998 | + }; 999 | + 1000 | +} 1001 | + 1002 | +char SplitSwitchesTransform::ID = 0; 1003 | + 1004 | + 1005 | +/* switchConvert - Transform simple list of Cases into list of CaseRange's */ 1006 | +BasicBlock* SplitSwitchesTransform::switchConvert(CaseVector Cases, std::vector bytesChecked, 1007 | + BasicBlock* OrigBlock, BasicBlock* NewDefault, 1008 | + Value* Val, unsigned level) { 1009 | + 1010 | + unsigned ValTypeBitWidth = Cases[0].Val->getBitWidth(); 1011 | + IntegerType *ValType = IntegerType::get(OrigBlock->getContext(), ValTypeBitWidth); 1012 | + IntegerType *ByteType = IntegerType::get(OrigBlock->getContext(), 8); 1013 | + unsigned BytesInValue = bytesChecked.size(); 1014 | + std::vector setSizes; 1015 | + std::vector> byteSets(BytesInValue, std::set()); 1016 | + 1017 | + 1018 | + /* for each of the possible cases we iterate over all bytes of the values 1019 | + * build a set of possible values at each byte position in byteSets */ 1020 | + for (CaseExpr& Case: Cases) { 1021 | + for (unsigned i = 0; i < BytesInValue; i++) { 1022 | + 1023 | + uint8_t byte = (Case.Val->getZExtValue() >> (i*8)) & 0xFF; 1024 | + byteSets[i].insert(byte); 1025 | + } 1026 | + } 1027 | + 1028 | + unsigned smallestIndex = 0; 1029 | + unsigned smallestSize = 257; 1030 | + for(unsigned i = 0; i < byteSets.size(); i++) { 1031 | + if (bytesChecked[i]) 1032 | + continue; 1033 | + if (byteSets[i].size() < smallestSize) { 1034 | + smallestIndex = i; 1035 | + smallestSize = byteSets[i].size(); 1036 | + } 1037 | + } 1038 | + assert(bytesChecked[smallestIndex] == false); 1039 | + 1040 | + /* there are only smallestSize different bytes at index smallestIndex */ 1041 | + 1042 | + Instruction *Shift, *Trunc; 1043 | + Function* F = OrigBlock->getParent(); 1044 | + BasicBlock* NewNode = BasicBlock::Create(Val->getContext(), "NodeBlock", F); 1045 | + Shift = BinaryOperator::Create(Instruction::LShr, Val, ConstantInt::get(ValType, smallestIndex * 8)); 1046 | + NewNode->getInstList().push_back(Shift); 1047 | + 1048 | + if (ValTypeBitWidth > 8) { 1049 | + Trunc = new TruncInst(Shift, ByteType); 1050 | + NewNode->getInstList().push_back(Trunc); 1051 | + } 1052 | + else { 1053 | + /* not necessary to trunc */ 1054 | + Trunc = Shift; 1055 | + } 1056 | + 1057 | + /* this is a trivial case, we can directly check for the byte, 1058 | + * if the byte is not found go to default. if the byte was found 1059 | + * mark the byte as checked. if this was the last byte to check 1060 | + * we can finally execute the block belonging to this case */ 1061 | + 1062 | + 1063 | + if (smallestSize == 1) { 1064 | + uint8_t byte = *(byteSets[smallestIndex].begin()); 1065 | + 1066 | + /* insert instructions to check whether the value we are switching on is equal to byte */ 1067 | + ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_EQ, Trunc, ConstantInt::get(ByteType, byte), "byteMatch"); 1068 | + NewNode->getInstList().push_back(Comp); 1069 | + 1070 | + bytesChecked[smallestIndex] = true; 1071 | + if (std::all_of(bytesChecked.begin(), bytesChecked.end(), [](bool b){return b;} )) { 1072 | + assert(Cases.size() == 1); 1073 | + BranchInst::Create(Cases[0].BB, NewDefault, Comp, NewNode); 1074 | + 1075 | + /* we have to update the phi nodes! */ 1076 | + for (BasicBlock::iterator I = Cases[0].BB->begin(); I != Cases[0].BB->end(); ++I) { 1077 | + if (!isa(&*I)) { 1078 | + continue; 1079 | + } 1080 | + PHINode *PN = cast(I); 1081 | + 1082 | + /* Only update the first occurence. */ 1083 | + unsigned Idx = 0, E = PN->getNumIncomingValues(); 1084 | + for (; Idx != E; ++Idx) { 1085 | + if (PN->getIncomingBlock(Idx) == OrigBlock) { 1086 | + PN->setIncomingBlock(Idx, NewNode); 1087 | + break; 1088 | + } 1089 | + } 1090 | + } 1091 | + } 1092 | + else { 1093 | + BasicBlock* BB = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, level + 1); 1094 | + BranchInst::Create(BB, NewDefault, Comp, NewNode); 1095 | + } 1096 | + } 1097 | + /* there is no byte which we can directly check on, split the tree */ 1098 | + else { 1099 | + 1100 | + std::vector byteVector; 1101 | + std::copy(byteSets[smallestIndex].begin(), byteSets[smallestIndex].end(), std::back_inserter(byteVector)); 1102 | + std::sort(byteVector.begin(), byteVector.end()); 1103 | + uint8_t pivot = byteVector[byteVector.size() / 2]; 1104 | + 1105 | + /* we already chose to divide the cases based on the value of byte at index smallestIndex 1106 | + * the pivot value determines the threshold for the decicion; if a case value 1107 | + * is smaller at this byte index move it to the LHS vector, otherwise to the RHS vector */ 1108 | + 1109 | + CaseVector LHSCases, RHSCases; 1110 | + 1111 | + for (CaseExpr& Case: Cases) { 1112 | + uint8_t byte = (Case.Val->getZExtValue() >> (smallestIndex*8)) & 0xFF; 1113 | + 1114 | + if (byte < pivot) { 1115 | + LHSCases.push_back(Case); 1116 | + } 1117 | + else { 1118 | + RHSCases.push_back(Case); 1119 | + } 1120 | + } 1121 | + BasicBlock *LBB, *RBB; 1122 | + LBB = switchConvert(LHSCases, bytesChecked, OrigBlock, NewDefault, Val, level + 1); 1123 | + RBB = switchConvert(RHSCases, bytesChecked, OrigBlock, NewDefault, Val, level + 1); 1124 | + 1125 | + /* insert instructions to check whether the value we are switching on is equal to byte */ 1126 | + ICmpInst* Comp = new ICmpInst(ICmpInst::ICMP_ULT, Trunc, ConstantInt::get(ByteType, pivot), "byteMatch"); 1127 | + NewNode->getInstList().push_back(Comp); 1128 | + BranchInst::Create(LBB, RBB, Comp, NewNode); 1129 | + 1130 | + } 1131 | + 1132 | + return NewNode; 1133 | +} 1134 | + 1135 | +bool SplitSwitchesTransform::splitSwitches(Module &M) { 1136 | + 1137 | + std::vector switches; 1138 | + 1139 | + /* iterate over all functions, bbs and instruction and add 1140 | + * all switches to switches vector for later processing */ 1141 | + for (auto &F : M) { 1142 | + for (auto &BB : F) { 1143 | + SwitchInst* switchInst = nullptr; 1144 | + 1145 | + if ((switchInst = dyn_cast(BB.getTerminator()))) { 1146 | + if (switchInst->getNumCases() < 1) 1147 | + continue; 1148 | + switches.push_back(switchInst); 1149 | + } 1150 | + } 1151 | + } 1152 | + 1153 | + if (!switches.size()) 1154 | + return false; 1155 | + errs() << "Rewriting " << switches.size() << " switch statements " << "\n"; 1156 | + 1157 | + for (auto &SI: switches) { 1158 | + 1159 | + BasicBlock *CurBlock = SI->getParent(); 1160 | + BasicBlock *OrigBlock = CurBlock; 1161 | + Function *F = CurBlock->getParent(); 1162 | + /* this is the value we are switching on */ 1163 | + Value *Val = SI->getCondition(); 1164 | + BasicBlock* Default = SI->getDefaultDest(); 1165 | + 1166 | + /* If there is only the default destination, don't bother with the code below. */ 1167 | + if (!SI->getNumCases()) { 1168 | + continue; 1169 | + } 1170 | + 1171 | + /* Create a new, empty default block so that the new hierarchy of 1172 | + * if-then statements go to this and the PHI nodes are happy. 1173 | + * if the default block is set as an unreachable we avoid creating one 1174 | + * because will never be a valid target.*/ 1175 | + BasicBlock *NewDefault = nullptr; 1176 | + NewDefault = BasicBlock::Create(SI->getContext(), "NewDefault"); 1177 | + NewDefault->insertInto(F, Default); 1178 | + BranchInst::Create(Default, NewDefault); 1179 | + 1180 | + 1181 | + /* Prepare cases vector. */ 1182 | + CaseVector Cases; 1183 | + for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) 1184 | +#if __clang_major__ < 7 1185 | + Cases.push_back(CaseExpr(i.getCaseValue(), i.getCaseSuccessor())); 1186 | +#else 1187 | + Cases.push_back(CaseExpr(i->getCaseValue(), i->getCaseSuccessor())); 1188 | +#endif 1189 | + std::vector bytesChecked(Cases[0].Val->getBitWidth() / 8, false); 1190 | + BasicBlock* SwitchBlock = switchConvert(Cases, bytesChecked, OrigBlock, NewDefault, Val, 0); 1191 | + 1192 | + /* Branch to our shiny new if-then stuff... */ 1193 | + BranchInst::Create(SwitchBlock, OrigBlock); 1194 | + 1195 | + /* We are now done with the switch instruction, delete it. */ 1196 | + CurBlock->getInstList().erase(SI); 1197 | + 1198 | + 1199 | + /* we have to update the phi nodes! */ 1200 | + for (BasicBlock::iterator I = Default->begin(); I != Default->end(); ++I) { 1201 | + if (!isa(&*I)) { 1202 | + continue; 1203 | + } 1204 | + PHINode *PN = cast(I); 1205 | + 1206 | + /* Only update the first occurence. */ 1207 | + unsigned Idx = 0, E = PN->getNumIncomingValues(); 1208 | + for (; Idx != E; ++Idx) { 1209 | + if (PN->getIncomingBlock(Idx) == OrigBlock) { 1210 | + PN->setIncomingBlock(Idx, NewDefault); 1211 | + break; 1212 | + } 1213 | + } 1214 | + } 1215 | + } 1216 | + 1217 | + verifyModule(M); 1218 | + return true; 1219 | +} 1220 | + 1221 | +bool SplitSwitchesTransform::runOnModule(Module &M) { 1222 | + 1223 | + llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; 1224 | + splitSwitches(M); 1225 | + verifyModule(M); 1226 | + 1227 | + return true; 1228 | +} 1229 | + 1230 | +static void registerSplitSwitchesTransPass(const PassManagerBuilder &, 1231 | + legacy::PassManagerBase &PM) { 1232 | + 1233 | + auto p = new SplitSwitchesTransform(); 1234 | + PM.add(p); 1235 | + 1236 | +} 1237 | + 1238 | +static RegisterStandardPasses RegisterSplitSwitchesTransPass( 1239 | + PassManagerBuilder::EP_OptimizerLast, registerSplitSwitchesTransPass); 1240 | + 1241 | +static RegisterStandardPasses RegisterSplitSwitchesTransPass0( 1242 | + PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); 1243 | diff -urN ./Makefile ./Makefile 1244 | --- ./Makefile 2018-12-27 15:09:45.486358000 +0100 1245 | +++ ./Makefile 2019-01-09 23:27:59.024753283 +0100 1246 | @@ -130,6 +130,9 @@ 1247 | endif 1248 | if [ -f afl-llvm-rt-32.o ]; then set -e; install -m 755 afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi 1249 | if [ -f afl-llvm-rt-64.o ]; then set -e; install -m 755 afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi 1250 | + if [ -f compare-transform-pass.so ]; then set -e; install -m 755 compare-transform-pass.so $${DESTDIR}$(HELPER_PATH); fi 1251 | + if [ -f split-compares-pass.so ]; then set -e; install -m 755 split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi 1252 | + if [ -f split-switches-pass.so ]; then set -e; install -m 755 split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi 1253 | set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $${DESTDIR}$(BIN_PATH)/$$i; done 1254 | install -m 755 afl-as $${DESTDIR}$(HELPER_PATH) 1255 | ln -sf afl-as $${DESTDIR}$(HELPER_PATH)/as 1256 | --------------------------------------------------------------------------------