diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index b5f42d9f..c53d08b4 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -75,6 +75,7 @@ #include #include #include +#include "asanfuzz.h" #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__NetBSD__) || defined(__DragonFly__) @@ -610,7 +611,12 @@ typedef struct afl_state { u8 *var_bytes; /* Bytes that appear to be variable */ #define N_FUZZ_SIZE (1 << 21) +#define N_FUZZ_SIZE_BITMAP (1 << 29) u32 *n_fuzz; + u8 *n_fuzz_dup; + u8 *classified_n_fuzz; + u8 *simplitied_n_fuzz; + volatile u8 stop_soon, /* Ctrl-C pressed? */ clear_screen; /* Window resized? */ @@ -728,6 +734,14 @@ typedef struct afl_state { char *cmplog_binary; afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */ + /* ASAN Fuzing */ + char *san_binary[MAX_EXTRA_SAN_BINARY]; + afl_forkserver_t san_fsrvs[MAX_EXTRA_SAN_BINARY]; + u8 san_binary_length; /* 0 means extra san binaries not given */ + u8 no_saving_crash_seed; + u32 san_case_status; + enum SanitizerAbstraction san_abstraction; + /* Custom mutators */ struct custom_mutator *mutator; diff --git a/include/asanfuzz.h b/include/asanfuzz.h new file mode 100644 index 00000000..ef25b992 --- /dev/null +++ b/include/asanfuzz.h @@ -0,0 +1,50 @@ +/* + american fuzzy lop++ - cmplog header + ------------------------------------ + + Originally written by Michal Zalewski + + Forkserver design by Jann Horn + + Now maintained by Marc Heuse , + Heiko Eißfeldt , + Andrea Fioraldi , + Dominik Maier + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + https://www.apache.org/licenses/LICENSE-2.0 + + Shared code to handle the shared memory. This is used by the fuzzer + as well the other components like afl-tmin, afl-showmap, etc... + + */ + +#ifndef _AFL_ASAMFUZZ_H +#define _AFL_ASAMFUZZ_H + +#include "config.h" + +// new_bits for describe_op +// new_bits value 1, 2 and 0x80 are already used! +#define SAN_CRASH_ONLY (1 << 4) +#define NON_COV_INCREASE_BUG (1 << 5) + +enum SanitizerAbstraction { + UNIQUE_TRACE = 0, // Feed all unique trace to sanitizers, the most sensitive + SIMPLIFY_TRACE, + COVERAGE_INCREASE // Feed all coverage increasing cases to sanitizers, the least sensitive +}; + +/* Execs the child */ + +struct afl_forkserver; +void sanfuzz_exec_child(struct afl_forkserver *fsrv, char **argv); + +#endif + diff --git a/include/config.h b/include/config.h index fd31f3c4..c43d984c 100644 --- a/include/config.h +++ b/include/config.h @@ -97,6 +97,12 @@ /* Maximum allowed fails per CMP value. Default: 96 */ #define CMPLOG_FAIL_MAX 96 +/* + * Effective fuzzing with selective feeding inputs + */ + +#define MAX_EXTRA_SAN_BINARY 4 + /* -------------------------------------*/ /* Now non-cmplog configuration options */ /* -------------------------------------*/ @@ -504,6 +510,9 @@ #define CMPLOG_SHM_ENV_VAR "__AFL_CMPLOG_SHM_ID" +/* ASAN SHM ID */ +#define AFL_ASAN_FUZZ_SHM_ENV_VAR "__AFL_ASAN_SHM_ID" + /* CPU Affinity lockfile env var */ #define CPU_AFFINITY_ENV_VAR "__AFL_LOCKFILE" diff --git a/include/coverage-64.h b/include/coverage-64.h index aab79d79..a855b5ab 100644 --- a/include/coverage-64.h +++ b/include/coverage-64.h @@ -72,6 +72,22 @@ inline void classify_counts(afl_forkserver_t *fsrv) { } +inline void classify_counts_mem(u64* mem, u32 size) { + + u32 i = (size >> 3); + + while (i--) { + + /* Optimize for sparse bitmaps. */ + + if (unlikely(*mem)) { *mem = classify_word(*mem); } + + mem++; + + } + +} + /* Updates the virgin bits, then reflects whether a new count or a new tuple is * seen in ret. */ inline void discover_word(u8 *ret, u64 *current, u64 *virgin) { diff --git a/include/debug.h b/include/debug.h index e7cbdb5c..95e46891 100644 --- a/include/debug.h +++ b/include/debug.h @@ -357,9 +357,9 @@ static inline const char *colorfilter(const char *x) { do { \ \ if (res < 0) \ - PFATAL(x); \ + ABORT(x); \ else \ - FATAL(x); \ + ABORT(x); \ \ } while (0) diff --git a/include/envs.h b/include/envs.h index 05fa2d3c..3ef78f05 100644 --- a/include/envs.h +++ b/include/envs.h @@ -117,8 +117,8 @@ static char *afl_environment_variables[] = { "AFL_USE_UBSAN", "AFL_UBSAN_VERBOSE", "AFL_USE_TSAN", "AFL_USE_CFISAN", "AFL_CFISAN_VERBOSE", "AFL_USE_LSAN", "AFL_WINE_PATH", "AFL_NO_SNAPSHOT", "AFL_EXPAND_HAVOC_NOW", "AFL_USE_FASAN", "AFL_USE_QASAN", - "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME", NULL - + "AFL_PRINT_FILENAMES", "AFL_PIZZA_MODE", "AFL_NO_FASTRESUME", + "AFL_SAN_ABSTRACTION", "AFL_SAN_NO_INST", "AFL_SAN_RECOVER", NULL }; extern char *afl_environment_variables[]; diff --git a/include/forkserver.h b/include/forkserver.h index d3d0e086..7b915c18 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -159,6 +159,8 @@ typedef struct afl_forkserver { bool debug; /* debug mode? */ + u8 san_but_not_instrumented; /* Is it sanitizer enabled but not instrumented? */ + bool uses_crash_exitcode; /* Custom crash exitcode specified? */ u8 crash_exitcode; /* The crash exitcode specified */ @@ -167,6 +169,7 @@ typedef struct afl_forkserver { u8 *shmem_fuzz; /* allocated memory for fuzzing */ char *cmplog_binary; /* the name of the cmplog binary */ + char *asanfuzz_binary; /* the name of the ASAN binary */ /* persistent mode replay functionality */ u32 persistent_record; /* persistent replay setting */ diff --git a/include/hash.h b/include/hash.h index 5d56a108..1a1b571a 100644 --- a/include/hash.h +++ b/include/hash.h @@ -33,6 +33,8 @@ u32 hash32(u8 *key, u32 len, u32 seed); u64 hash64(u8 *key, u32 len, u64 seed); +u32 hash32_xxh32(u8 *key, u32 len, u32 seed); + #if 0 The following code is disabled because xxh3 is 30% faster diff --git a/include/sharedmem.h b/include/sharedmem.h index 036fa560..140ee266 100644 --- a/include/sharedmem.h +++ b/include/sharedmem.h @@ -51,6 +51,7 @@ typedef struct sharedmem { size_t map_size; /* actual allocated size */ int cmplog_mode; + int sanfuzz_mode; int shmemfuzz_mode; struct cmp_map *cmp_map; diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 6ec84dcd..25eabcda 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -326,9 +326,15 @@ class ModuleSanitizerCoverageLTOLegacyPass : public ModulePass { .getPostDomTree(); }; - - return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); - + + if (!getenv("AFL_SAN_NO_INST")) { + return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); + } else { + if (getenv("AFL_DEBUG")) { + DEBUGF("Instrument disabled\n"); + } + return false; + } } private: @@ -380,8 +386,14 @@ PreservedAnalyses ModuleSanitizerCoverageLTO::run(Module &M, }; - if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) - return PreservedAnalyses::none(); + if (!getenv("AFL_SAN_NO_INST")) { + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + } else { + if (debug) { + DEBUGF("Instrument disabled\n"); + } + } return PreservedAnalyses::all(); diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index fae33d27..1706ed4f 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -261,8 +261,16 @@ PreservedAnalyses ModuleSanitizerCoverageAFL::run(Module &M, }; - if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) - return PreservedAnalyses::none(); + // TODO: Support LTO or llvm classic? + // Note we still need afl-compiler-rt so we just disable the instrumentation here. + if (!getenv("AFL_SAN_NO_INST")) { + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + } else { + if (getenv("AFL_DEBUG")) { + DEBUGF("Instrument disabled\n"); + } + } return PreservedAnalyses::all(); } diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index c599e957..fd2b116b 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -223,6 +223,22 @@ bool AFLCoverage::runOnModule(Module &M) { if (getenv("AFL_DEBUG")) debug = 1; +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + if (getenv("AFL_SAN_NO_INST")) { + if (debug) { + fprintf(stderr, "Intrument disabled\n"); + } + return PA; + } +#else + if (getenv("AFL_SAN_NO_INST")) { + if (debug) { + fprintf(stderr, "Intrument disabled\n"); + } + return true; + } +#endif + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { SAYF(cCYA "afl-llvm-pass" VERSION cRST diff --git a/src/afl-cc.c b/src/afl-cc.c index b793a6fc..38a0ad98 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -246,6 +246,13 @@ static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt, /* Insert params into the new argv, make clang load the pass. */ static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) { + if (getenv("AFL_SAN_NO_INST")) { + if (debug) { + DEBUGF("Instrument disabled\n"); + } + return; + } + #if LLVM_MAJOR >= 11 /* use new pass manager */ #if LLVM_MAJOR < 16 @@ -2063,6 +2070,10 @@ void add_sanitizers(aflcc_state_t *aflcc, char **envp) { aflcc->have_cfisan = 1; + if (getenv("AFL_SAN_RECOVER")) { + cc_params[cc_par_cnt++] = "-fsanitize-recover=all"; + } + } } @@ -2079,7 +2090,12 @@ void add_native_pcguard(aflcc_state_t *aflcc) { * anyway. */ if (aflcc->have_rust_asanrt) { return; } - + if (getenv("AFL_SAN_NO_INST")) { + if (debug) { + DEBUGF("Instrument disabled\n"); + } + return; + } /* If llvm-config doesn't figure out LLVM_MAJOR, just go on anyway and let compiler complain if doesn't work. */ @@ -2091,10 +2107,11 @@ void add_native_pcguard(aflcc_state_t *aflcc) { "pcguard instrumentation with pc-table requires LLVM 6.0.1+" " otherwise the compiler will fail"); #endif + if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { insert_param(aflcc, - "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); + "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); } else { @@ -2113,11 +2130,17 @@ void add_native_pcguard(aflcc_state_t *aflcc) { */ void add_optimized_pcguard(aflcc_state_t *aflcc) { +if (getenv("AFL_SAN_NO_INST")) { + if (debug) { + DEBUGF("Instrument disabled\n"); + } + return; +} + #if LLVM_MAJOR >= 13 #if defined __ANDROID__ || ANDROID - - insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; #else diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index bee7f1bd..8ea58b39 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -1918,18 +1918,21 @@ fsrv_run_result_t __attribute__((hot)) afl_fsrv_run_target( must prevent any earlier operations from venturing into that territory. */ + /* If the binary is not instrumented, we don't care about the coverage. Make it a bit faster */ + if (!fsrv->san_but_not_instrumented) { #ifdef __linux__ if (likely(!fsrv->nyx_mode)) { - memset(fsrv->trace_bits, 0, fsrv->map_size); - MEM_BARRIER(); + memset(fsrv->trace_bits, 0, fsrv->map_size); + MEM_BARRIER(); - } + } #else - memset(fsrv->trace_bits, 0, fsrv->map_size); - MEM_BARRIER(); + memset(fsrv->trace_bits, 0, fsrv->map_size); + MEM_BARRIER(); #endif + } /* we have the fork server (or faux server) up and running First, tell it if the previous run timed out. */ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index fd75a822..42f2741b 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -25,6 +25,9 @@ #include "afl-fuzz.h" #include +#include +#include +#include "asanfuzz.h" #if !defined NAME_MAX #define NAME_MAX _XOPEN_NAME_MAX #endif @@ -297,6 +300,9 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { u8 is_timeout = 0; + u8 san_crash_only = (afl->san_case_status & SAN_CRASH_ONLY); + u8 non_cov_incr = (afl->san_case_status & NON_COV_INCREASE_BUG); + if (new_bits & 0xf0) { @@ -388,6 +394,10 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { if (new_bits == 2) { strcat(ret, ",+cov"); } + if (san_crash_only) { strcat(ret, ",+san"); } + + if (non_cov_incr) { strcat(ret, ",+noncov"); } + if (unlikely(strlen(ret) >= max_description_len)) FATAL("describe string is too long"); @@ -452,6 +462,19 @@ void write_crash_readme(afl_state_t *afl) { } +static void bitmap_set(u8* map, u32 index) { + map[index / 8] |= (1u << ( index % 8 )); +} + +// static u8 bitmap_clear(u8* map, u32 index) { +// map[index / 8] &= ~(1u << (index % 8)); +// } + +static u8 bitmap_read(u8* map, u32 index) { + return (map[index / 8] >> (index % 8)) & 1; +} + + /* Check if the result of an execve() during routine fuzzing is interesting, save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ @@ -484,6 +507,22 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem, need_hash = 1; s32 fd; u64 cksum = 0; + u32 cksum_simplified = 0, cksum_unique = 0; + u8 san_fault = 0; + u8 san_idx = 0; + u8 feed_san = 0; + u8 crashed = 0; + + afl->san_case_status = 0; + + /* Mask out var bytes */ + if (unlikely(afl->san_binary_length)) { + for (u32 i = 0; i < afl->fsrv.map_size; i++) { + if (afl->var_bytes[i] && afl->fsrv.trace_bits[i]) { + afl->fsrv.trace_bits[i] = 1; + } + } + } /* Update path frequency. */ @@ -503,29 +542,96 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem, } + /* Only "normal" inputs seem interested to us */ if (likely(fault == afl->crash_mode)) { - /* Keep only if there are new bits in the map, add to queue for - future fuzzing, etc. */ + if (unlikely(afl->san_binary_length) && likely(afl->san_abstraction == SIMPLIFY_TRACE)) { + memcpy(afl->san_fsrvs[0].trace_bits, afl->fsrv.trace_bits, afl->fsrv.map_size); + classify_counts_mem((u64*)afl->san_fsrvs[0].trace_bits, afl->fsrv.map_size); + simplify_trace(afl, afl->san_fsrvs[0].trace_bits); - if (likely(classified)) { + // cksum_simplified = hash64(afl->san_fsrvs[0].trace_bits, afl->fsrv.map_size, HASH_CONST); + cksum_simplified = hash32_xxh32(afl->san_fsrvs[0].trace_bits, afl->fsrv.map_size, HASH_CONST); - new_bits = has_new_bits(afl, afl->virgin_bits); + if ( unlikely(!bitmap_read(afl->simplitied_n_fuzz, cksum_simplified))) { + feed_san = 1; + bitmap_set(afl->simplitied_n_fuzz, cksum_simplified); + } - } else { + } + if (unlikely(afl->san_binary_length) && unlikely(afl->san_abstraction == COVERAGE_INCREASE)) { + + /* Check if the input increase the coverage */ new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); - if (unlikely(new_bits)) { classified = 1; } + if (unlikely(new_bits)) { + feed_san = 1; + } + } + if (unlikely(afl->san_binary_length) && likely(afl->san_abstraction == UNIQUE_TRACE)) { + cksum_unique = hash32_xxh32(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); + if (unlikely(!bitmap_read(afl->n_fuzz_dup, cksum) && fault == afl->crash_mode)) { + feed_san = 1; + bitmap_set(afl->n_fuzz_dup, cksum_unique); + } + } + + if (feed_san) { + /* The input seems interested to other sanitizers, feed it into extra binaries. */ + + for (san_idx = 0; san_idx < afl->san_binary_length; san_idx++) { + + len = write_to_testcase(afl, &mem, len, 0); + san_fault = fuzz_run_target(afl, &afl->san_fsrvs[san_idx], afl->san_fsrvs[san_idx].exec_tmout); + + // DEBUGF("ASAN Result: %hhd\n", asan_fault); + + if (unlikely(san_fault && fault == afl->crash_mode)) { + /* sanitizers discovers distinct bugs! */ + afl->san_case_status |= SAN_CRASH_ONLY; + } + + if (san_fault == FSRV_RUN_CRASH) { + /* Treat this execution as fault detected by ASAN */ + // fault = san_fault; + + /* That's pretty enough, break to avoid more overhead. */ + break; + } else { + // or keep san_fault as ok + san_fault = FSRV_RUN_OK; + } + } + } + } + + /* If there is no crash, everything is fine. */ + if (likely(fault == afl->crash_mode)) { + + /* Keep only if there are new bits in the map, add to queue for + future fuzzing, etc. */ + if (!unlikely(afl->san_abstraction == COVERAGE_INCREASE && feed_san)) { + /* If we are in coverage increasing abstraction and have fed input to sanitizers, we are + sure it has new bits.*/ + new_bits = has_new_bits_unclassified(afl, afl->virgin_bits); } if (likely(!new_bits)) { - if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } - return 0; - + if (san_fault == FSRV_RUN_OK) { + if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } + return 0; + } else { + afl->san_case_status |= NON_COV_INCREASE_BUG; + fault = san_fault; + classified = new_bits; + goto may_save_fault; + } } + fault = san_fault; + classified = new_bits; save_to_queue: @@ -653,7 +759,7 @@ u8 __attribute__((hot)) save_if_interesting(afl_state_t *afl, void *mem, keeping = 1; } - +may_save_fault: switch (fault) { case FSRV_RUN_TMOUT: diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index e90495f3..7e8fdb95 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2332,7 +2332,7 @@ void setup_dirs_fds(afl_state_t *afl) { afl->fsrv.plot_file, "# relative_time, cycles_done, cur_item, corpus_count, " "pending_total, pending_favs, map_size, saved_crashes, " - "saved_hangs, max_depth, execs_per_sec, total_execs, edges_found\n"); + "saved_hangs, max_depth, execs_per_sec, total_execs, edges_found, total_crashes, servers_count, san1_exec...\n"); } else { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index a3787e5c..c40af235 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -33,6 +33,7 @@ #endif #include "cmplog.h" +#include "asanfuzz.h" #ifdef PROFILING u64 time_spent_working = 0; diff --git a/src/afl-fuzz-sanfuzz.c b/src/afl-fuzz-sanfuzz.c new file mode 100644 index 00000000..87879138 --- /dev/null +++ b/src/afl-fuzz-sanfuzz.c @@ -0,0 +1,43 @@ +/* + american fuzzy lop++ - cmplog execution routines + ------------------------------------------------ + + Originally written by Michal Zalewski + + Forkserver design by Jann Horn + + Now maintained by by Marc Heuse , + Heiko Eißfeldt and + Andrea Fioraldi + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2023 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + https://www.apache.org/licenses/LICENSE-2.0 + + Shared code to handle the shared memory. This is used by the fuzzer + as well the other components like afl-tmin, afl-showmap, etc... + + */ + +/* This file roughly folows afl-fuzz-asanfuzz */ + +#include + +#include "afl-fuzz.h" + +void sanfuzz_exec_child(afl_forkserver_t *fsrv, char **argv) { + + if (!fsrv->qemu_mode && !fsrv->frida_mode && argv[0] != fsrv->asanfuzz_binary) { + + argv[0] = fsrv->asanfuzz_binary; + + } + + execv(fsrv->target_path, argv); + +} \ No newline at end of file diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index a5785eb8..3cd80e3f 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -386,6 +386,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, "bitmap_cvg : %0.02f%%\n" "saved_crashes : %llu\n" "saved_hangs : %llu\n" + "total_tmout : %llu\n" "last_find : %llu\n" "last_crash : %llu\n" "last_hang : %llu\n" @@ -424,7 +425,7 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, afl->queued_discovered, afl->queued_imported, afl->queued_variable, afl->max_depth, afl->current_entry, afl->pending_favored, afl->pending_not_fuzzed, stability, bitmap_cvg, afl->saved_crashes, - afl->saved_hangs, afl->last_find_time / 1000, afl->last_crash_time / 1000, + afl->saved_hangs, afl->total_tmouts, afl->last_find_time / 1000, afl->last_crash_time / 1000, afl->last_hang_time / 1000, afl->fsrv.total_execs - afl->last_crash_execs, afl->fsrv.exec_tmout, afl->slowest_exec_ms, #ifndef __HAIKU__ @@ -458,6 +459,16 @@ void write_stats_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, : "default", afl->orig_cmdline); + if (afl->san_binary_length) { + for (u8 i = 0; i < afl->san_binary_length; i++) { + fprintf(f, + "extra_binary : %s\n" + "total_execs : %llu\n", + afl->san_binary[i], + afl->san_fsrvs[i].total_execs); + } + } + /* ignore errors */ if (afl->debug) { @@ -541,7 +552,7 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, afl->plot_prev_md == afl->max_depth && afl->plot_prev_ed == afl->fsrv.total_execs) || !afl->queue_cycle || - get_cur_time() - afl->start_time <= 60000))) { + get_cur_time() - afl->start_time <= 1000))) { return; @@ -565,12 +576,19 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, fprintf(afl->fsrv.plot_file, "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, " - "%u\n", + "%u, %llu, %u", ((afl->prev_run_time + get_cur_time() - afl->start_time) / 1000), afl->queue_cycle - 1, afl->current_entry, afl->queued_items, afl->pending_not_fuzzed, afl->pending_favored, bitmap_cvg, afl->saved_crashes, afl->saved_hangs, afl->max_depth, eps, - afl->plot_prev_ed, t_bytes); /* ignore errors */ + afl->plot_prev_ed, t_bytes, afl->total_crashes, (u32)afl->san_binary_length); /* ignore errors */ + + + for (u32 i = 0; i < afl->san_binary_length; i++) { + fprintf(afl->fsrv.plot_file, ", %llu", afl->san_fsrvs[i].total_execs); + } + + fprintf(afl->fsrv.plot_file, "\n"); fflush(afl->fsrv.plot_file); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 27c928fa..829e0b26 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -25,7 +25,9 @@ */ #include "afl-fuzz.h" +#include "alloc-inl.h" #include "cmplog.h" +#include "asanfuzz.h" #include "common.h" #include #include @@ -252,13 +254,15 @@ static void usage(u8 *argv0, int more_help) { " X=extreme transform solving, R=random colorization " "bytes.\n\n" "Fuzzing behavior settings:\n" - " -Z - sequential queue selection instead of weighted " + " -Z - sequential queue selection instead of weighted " "random\n" - " -N - do not unlink the fuzzing input file (for devices " + " -N - do not unlink the fuzzing input file (for devices " "etc.)\n" - " -n - fuzz without instrumentation (non-instrumented mode)\n" - " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " - "times)\n\n" + " -n - fuzz without instrumentation (non-instrumented mode)\n" + " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " + "times)\n" + " -a san_binary - Specify the extra sanitizer instrumented binaries,\n" + " can be specified multiple times.\n\n" "Test settings:\n" " -s seed - use a fixed seed for the RNG\n" @@ -549,6 +553,7 @@ int main(int argc, char **argv_orig, char **envp) { u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0 /*, have_p = 0*/; char *afl_preload; + char *san_abstraction; char *frida_afl_preload = NULL; char **use_argv; @@ -607,7 +612,7 @@ int main(int argc, char **argv_orig, char **envp) { // still available: HjJkKqruvwz while ((opt = getopt(argc, argv, - "+a:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:" + "+aw:Ab:B:c:CdDe:E:f:F:g:G:hi:I:l:L:m:M:nNo:Op:P:QRs:S:t:" "T:uUV:WXx:YzZ")) > 0) { switch (opt) { @@ -741,6 +746,17 @@ int main(int argc, char **argv_orig, char **envp) { } + case 'w' : { + + if (afl->san_binary_length == MAX_EXTRA_SAN_BINARY) { + FATAL("Only %d extra sanitizer instrumented binaries are supported.", MAX_EXTRA_SAN_BINARY); + } + + afl->shm.sanfuzz_mode = 1; + afl->san_binary[afl->san_binary_length++] = optarg; + break; + } + case 's': { if (optarg == NULL) { FATAL("No valid seed provided. Got NULL."); } @@ -1727,6 +1743,9 @@ int main(int argc, char **argv_orig, char **envp) { } + afl->n_fuzz_dup = ck_alloc(N_FUZZ_SIZE_BITMAP * sizeof(u8)); + afl->simplitied_n_fuzz = ck_alloc(N_FUZZ_SIZE_BITMAP * sizeof(u8)); + if (get_afl_env("AFL_NO_FORKSRV")) { afl->no_forkserver = 1; } if (get_afl_env("AFL_NO_CPU_RED")) { afl->no_cpu_meter_red = 1; } if (get_afl_env("AFL_NO_ARITH")) { afl->no_arith = 1; } @@ -2396,6 +2415,10 @@ int main(int argc, char **argv_orig, char **envp) { if (!afl->fsrv.out_file) { setup_stdio_file(afl); } + for (u8 i = 0; i < afl->san_binary_length; i++) { + check_binary(afl, afl->san_binary[i]); + } + if (afl->cmplog_binary) { if (afl->unicorn_mode) { @@ -2554,6 +2577,109 @@ int main(int argc, char **argv_orig, char **envp) { } + san_abstraction = getenv("AFL_SAN_ABSTRACTION"); + if (!san_abstraction || !strcmp(san_abstraction, "unique_trace")) { + afl->san_abstraction = UNIQUE_TRACE; + } else if (!strcmp(san_abstraction, "coverage_increase")) { + afl->san_abstraction = COVERAGE_INCREASE; + } else if (!strcmp(san_abstraction, "simplify_trace")) { + afl->san_abstraction = SIMPLIFY_TRACE; + } else { + WARNF("Unkown abstraction: %s, fallback to unique trace.\n", san_abstraction); + afl->san_abstraction = UNIQUE_TRACE; + } + + afl->no_saving_crash_seed = false; + + if (!afl->san_binary_length && san_abstraction) { + WARNF("No extra sanitizer instrumented binaries are given, do you forget -a?\n"); + } + + /* Maybe merge with cmplog but much cmplog code was already copy-paste style... */ + if (afl->san_binary_length) { + + for (u8 i = 0; i < afl->san_binary_length; i++) { + ACTF("Spawning forkserver for %s", afl->san_binary[i]); + afl_fsrv_init_dup(&afl->san_fsrvs[i], &afl->fsrv); + + /* + * We don't really collect trace bits for sanitizer instrumented binary so we just allocate + * some dummy memory here. + */ + afl->san_fsrvs[i].trace_bits = ck_alloc(afl->fsrv.map_size + 8); /* One more u64 according to afl_shm_init*/ + afl->san_fsrvs[i].map_size = afl->fsrv.map_size; + afl->san_fsrvs[i].san_but_not_instrumented = 1; + + afl->san_fsrvs[i].cs_mode = afl->fsrv.cs_mode; + afl->san_fsrvs[i].qemu_mode = afl->fsrv.qemu_mode; + afl->san_fsrvs[i].frida_mode = afl->fsrv.frida_mode; + afl->san_fsrvs[i].asanfuzz_binary = afl->san_binary[i]; + afl->san_fsrvs[i].target_path = afl->san_binary[i]; + afl->san_fsrvs[i].init_child_func = sanfuzz_exec_child; + + afl->san_fsrvs[i].child_kill_signal = afl->fsrv.child_kill_signal; // I believe cmplog also needs this. + afl->san_fsrvs[i].fsrv_kill_signal = afl->fsrv.fsrv_kill_signal; + + if ((map_size <= DEFAULT_SHMEM_SIZE || + afl->san_fsrvs[i].map_size < map_size) && + !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->fsrv.frida_mode && !afl->unicorn_mode && !afl->fsrv.cs_mode && + !afl->afl_env.afl_skip_bin_check) { + + afl->san_fsrvs[i].map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE); + char vbuf[16]; + snprintf(vbuf, sizeof(vbuf), "%u", afl->san_fsrvs[i].map_size); + setenv("AFL_MAP_SIZE", vbuf, 1); + + } + + u32 new_map_size = + afl_fsrv_get_mapsize(&afl->san_fsrvs[i], afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + // only reinitialize when it needs to be larger + if (map_size < new_map_size) { + + OKF("Re-initializing maps to %u bytes due to SAN instrumented binary", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, new_map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, new_map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, new_map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, new_map_size); + afl->top_rated = + ck_realloc(afl->top_rated, new_map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, new_map_size); + afl->clean_trace_custom = + ck_realloc(afl->clean_trace_custom, new_map_size); + afl->first_trace = ck_realloc(afl->first_trace, new_map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, new_map_size); + + afl_fsrv_kill(&afl->fsrv); + afl_fsrv_kill(&afl->san_fsrvs[i]); + afl_shm_deinit(&afl->shm); + + afl->san_fsrvs[i].map_size = new_map_size; // non-cmplog stays the same + map_size = new_map_size; + + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, new_map_size, afl->non_instrumented_mode); + ck_free(afl->san_fsrvs[i].trace_bits); + afl->san_fsrvs[i].trace_bits = ck_alloc(afl->fsrv.map_size + 8); + afl->san_fsrvs[i].map_size = afl->fsrv.map_size; + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + afl_fsrv_start(&afl->san_fsrvs[i], afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + } + + OKF("forkserver for %s successfully started", afl->san_binary[i]); + } + + OKF("All forkservers for extra sanitizers instrumented binares are up and we have abstraction = %d", afl->san_abstraction); + } + if (afl->cmplog_binary) { ACTF("Spawning cmplog forkserver"); @@ -3450,6 +3576,13 @@ stop_fuzzing: afl_fsrv_deinit(&afl->fsrv); + + + for (u8 i = 0; i < afl->san_binary_length; i++) { + ck_free(afl->san_fsrvs[i].trace_bits); + afl_fsrv_deinit(&afl->san_fsrvs[i]); // TODO: Is this necessary? cmplog fksrv seems never deinit-ed? + } + /* remove tmpfile */ if (!afl->in_place_resume && afl->fsrv.out_file) { diff --git a/src/afl-performance.c b/src/afl-performance.c index b824fd35..3a2bc4b0 100644 --- a/src/afl-performance.c +++ b/src/afl-performance.c @@ -425,3 +425,15 @@ char *sha1_hex_for_file(const char *fname, u32 len) { } +#ifdef _DEBUG +u32 hash32_xxh32(u8 *key, u32 len, u32 seed) { + +#else +inline u32 hash32_xxh32(u8 *key, u32 len, u32 seed) { + +#endif + + (void)seed; + return (u32)XXH32(key, len, seed); + +}