From 320f26d26f7e0cbe093e6f5af5f27f180bc31a1b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 30 Jul 2020 19:00:41 +0200 Subject: add -b option to afl-fuzz --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1e7a1c1d..dcaf64a7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,7 @@ sending a mail to . - afl-fuzz: - added -F option to allow -M main fuzzers to sync to foreign fuzzers, e.g. honggfuzz or libfuzzer + - added -b option to bind to a specific CPU - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix -- cgit 1.4.1 From cd576fa59d1b413433beef1009668f4d9b22c965 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 31 Jul 2020 10:42:43 +0200 Subject: fixes --- docs/binaryonly_fuzzing.md | 4 ++-- examples/afl_frida/GNUmakefile | 23 +++++++++++++++++++++++ examples/afl_frida/Makefile | 25 ++----------------------- examples/defork/forking_target | Bin 19520 -> 0 bytes src/afl-fuzz.c | 2 ++ 5 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 examples/afl_frida/GNUmakefile delete mode 100755 examples/defork/forking_target (limited to 'docs') diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index 111147e2..a3d3330f 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -15,9 +15,9 @@ high enough. Otherwise try retrowrite, afl-dyninst and if these fail too then standard qemu_mode with AFL_ENTRYPOINT to where you need it. - If your a target is library use examples/afl_frida/. + If your target is a library use examples/afl_frida/. - If your target is non-linux then use unicorn_mode/ + If your target is non-linux then use unicorn_mode/. ## QEMU diff --git a/examples/afl_frida/GNUmakefile b/examples/afl_frida/GNUmakefile new file mode 100644 index 00000000..c154f3a4 --- /dev/null +++ b/examples/afl_frida/GNUmakefile @@ -0,0 +1,23 @@ +ifdef DEBUG + OPT=-O0 -D_DEBUG=\"1\" +else + OPT=-O3 -funroll-loops +endif + +all: afl-frida libtestinstr.so + +libfrida-gum.a: + @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest + @exit 1 + +afl-frida: afl-frida.c libfrida-gum.a + $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-frida *~ core *.o libtestinstr.so + +deepclean: clean + rm -f libfrida-gum.a frida-gum* diff --git a/examples/afl_frida/Makefile b/examples/afl_frida/Makefile index c154f3a4..0b306dde 100644 --- a/examples/afl_frida/Makefile +++ b/examples/afl_frida/Makefile @@ -1,23 +1,2 @@ -ifdef DEBUG - OPT=-O0 -D_DEBUG=\"1\" -else - OPT=-O3 -funroll-loops -endif - -all: afl-frida libtestinstr.so - -libfrida-gum.a: - @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest - @exit 1 - -afl-frida: afl-frida.c libfrida-gum.a - $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-frida *~ core *.o libtestinstr.so - -deepclean: clean - rm -f libfrida-gum.a frida-gum* +all: + @echo please use GNU make, thanks! diff --git a/examples/defork/forking_target b/examples/defork/forking_target deleted file mode 100755 index 0f7a04fc..00000000 Binary files a/examples/defork/forking_target and /dev/null differ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e33a4bbd..54db1efb 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -163,11 +163,13 @@ static void usage(afl_state_t *afl, u8 *argv0, int more_help) { "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" + "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" "AFL_DEBUG: extra debugging output for Python mode trimming\n" "AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n" "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" + "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" -- cgit 1.4.1 From 185f4436598aacb3f25736ce6eb53309d1d9472f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 31 Jul 2020 17:53:01 +0200 Subject: add LTO AFL_LLVM_DOCUMENT_IDS feature --- docs/Changelog.md | 2 ++ docs/FAQ.md | 12 +++++----- docs/env_variables.md | 35 ++++++++++++++++------------ include/envs.h | 1 + llvm_mode/README.lto.md | 6 +++++ llvm_mode/afl-clang-fast.c | 2 ++ llvm_mode/afl-llvm-lto-instrumentation.so.cc | 18 ++++++++++++++ 7 files changed, 55 insertions(+), 21 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index dcaf64a7..14d00a43 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,8 @@ sending a mail to . - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better + - LTO: env var AFL_LLVM_DOCUMENT_IDS=file will document which edge ID + was given to which function during compilation - setting AFL_LLVM_LAF_SPLIT_FLOATS now activates AFL_LLVM_LAF_SPLIT_COMPARES - added honggfuzz mangle as a custom mutator in custom_mutators/honggfuzz diff --git a/docs/FAQ.md b/docs/FAQ.md index e09385a8..b09a16ae 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -95,12 +95,13 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! 2. Second step: Find the responsible function. - a) For LTO instrumented binaries just disassemble or decompile the target - and look which edge is writing to that edge ID. Ghidra is a good tool - for this: [https://ghidra-sre.org/](https://ghidra-sre.org/) + a) For LTO instrumented binaries this can be documented during compile + time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/afile`. + This file will have one assigned edge ID and the corresponding function + per line. - b) For PCGUARD instrumented binaries it is more difficult. Here you can - either modify the __sanitizer_cov_trace_pc_guard function in + b) For PCGUARD instrumented binaries it is much more difficult. Here you + can either modify the __sanitizer_cov_trace_pc_guard function in llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in __afl_area_ptr[*guard] is one of the unstable edge IDs. Then recompile and reinstall llvm_mode and rebuild your target. Run the recompiled @@ -121,4 +122,3 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! 4. Fourth step: recompile the target Recompile, fuzz it, be happy :) - diff --git a/docs/env_variables.md b/docs/env_variables.md index 87344331..4c0d2db7 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -121,18 +121,16 @@ Then there are a few specific features that are only available in llvm_mode: built if LLVM 11 or newer is used. - AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation. - (recommended) - - - AFL_LLVM_LTO_AUTODICTIONARY will generate a dictionary in the target - binary based on string compare and memory compare functions. - afl-fuzz will automatically get these transmitted when starting to - fuzz. + (not recommended!) None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). These are used if several seperated instrumentation are performed which are then later combined. + - AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given + to which function. This helps to identify functions with variable bytes + or which functions were touched by an input. - AFL_LLVM_MAP_ADDR sets the fixed map address to a different address than the default 0x10000. A value of 0 or empty sets the map address to be dynamic (the original afl way, which is slower) @@ -254,15 +252,6 @@ checks or alter some of the more exotic semantics of the tool: useful if you can't change the defaults (e.g., no root access to the system) and are OK with some performance loss. - - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to - fork + execve() call for every tested input. This is useful mostly when - working with unruly libraries that create threads or do other crazy - things when initializing (before the instrumentation has a chance to run). - - Note that this setting inhibits some of the user-friendly diagnostics - normally done when starting up the forkserver and causes a pretty - significant performance drop. - - AFL_EXIT_WHEN_DONE causes afl-fuzz to terminate when all existing paths have been fuzzed and there were no new finds for a while. This would be normally indicated by the cycle counter in the UI turning green. May be @@ -338,6 +327,13 @@ checks or alter some of the more exotic semantics of the tool: - In QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace. + - Setting AFL_CYCLE_SCHEDULES will switch to a different schedule everytime + a cycle is finished. + + - Setting AFL_EXPAND_HAVOC_NOW will start in the extended havoc mode that + includes costly mutations. afl-fuzz automatically enables this mode when + deemed useful otherwise. + - Setting AFL_PRELOAD causes AFL to set LD_PRELOAD for the target binary without disrupting the afl-fuzz process itself. This is useful, among other things, for bootstrapping libdislocator.so. @@ -365,6 +361,15 @@ checks or alter some of the more exotic semantics of the tool: for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. + - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to + fork + execve() call for every tested input. This is useful mostly when + working with unruly libraries that create threads or do other crazy + things when initializing (before the instrumentation has a chance to run). + + Note that this setting inhibits some of the user-friendly diagnostics + normally done when starting up the forkserver and causes a pretty + significant performance drop. + - Outdated environment variables that are that not supported anymore: AFL_DEFER_FORKSRV AFL_PERSISTENT diff --git a/include/envs.h b/include/envs.h index c1c7d387..7153ed47 100644 --- a/include/envs.h +++ b/include/envs.h @@ -65,6 +65,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", + "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", "AFL_LLVM_LTO_AUTODICTIONARY", diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index a4c969b9..e521ac82 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -140,6 +140,12 @@ to be dynamic - the original afl way, which is slower). AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which is safer but also slower). +## Document edge IDs + +Setting `export AFL_LLVM_DOCUMENT_IDS=file` will document to a file which edge +ID was given to which function. This helps to identify functions with variable +bytes or which functions were touched by an input. + ## Solving difficult targets Some targets are difficult because the configure script does unusual stuff that diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index dca11bf3..a2550d2c 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -890,6 +890,8 @@ int main(int argc, char **argv, char **envp) { "AFL_NO_BUILTIN: compile for use with libtokencap.so\n" "AFL_PATH: path to instrumenting pass and runtime " "(afl-llvm-rt.*o)\n" + "AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function (LTO " + "only)\n" "AFL_QUIET: suppress verbose output\n" "AFL_USE_ASAN: activate address sanitizer\n" "AFL_USE_CFISAN: activate control flow sanitizer\n" diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 3c1d3565..46a97e54 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -103,6 +103,7 @@ bool AFLLTOPass::runOnModule(Module &M) { std::vector calls; DenseMap valueMap; char * ptr; + FILE * documentFile = NULL; IntegerType *Int8Ty = IntegerType::getInt8Ty(C); IntegerType *Int32Ty = IntegerType::getInt32Ty(C); @@ -120,6 +121,13 @@ bool AFLLTOPass::runOnModule(Module &M) { be_quiet = 1; + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { + + if ((documentFile = fopen(ptr, "a")) == NULL) + WARNF("Cannot access document file %s", ptr); + + } + if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || @@ -579,6 +587,14 @@ bool AFLLTOPass::runOnModule(Module &M) { } + if (documentFile) { + + fprintf(documentFile, "%s %u\n", + origBB->getParent()->getName().str().c_str(), + afl_global_id); + + } + BasicBlock::iterator IP = newBB->getFirstInsertionPt(); IRBuilder<> IRB(&(*IP)); @@ -632,6 +648,8 @@ bool AFLLTOPass::runOnModule(Module &M) { } + if (documentFile) fclose(documentFile); + } // save highest location ID to global variable -- cgit 1.4.1 From 593940c39a3838b072863b7093ed31f92846cfcb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 31 Jul 2020 20:20:30 +0200 Subject: refer to llvm 12 for partial instrumentation for PCGUARD --- docs/FAQ.md | 3 ++- llvm_mode/README.instrument_file.md | 2 ++ llvm_mode/afl-clang-fast.c | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index b09a16ae..ee221d02 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -117,7 +117,8 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! remove from instrumentation. Simply follow this document on how to do this: [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) - If PCGUARD is used, then you need to follow this guide: [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) + If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): + [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) 4. Fourth step: recompile the target diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md index 29c40eec..46e45ba2 100644 --- a/llvm_mode/README.instrument_file.md +++ b/llvm_mode/README.instrument_file.md @@ -18,6 +18,8 @@ For this purpose, I have added a "partial instrumentation" support to the LLVM mode of AFLFuzz that allows you to specify on a source file level which files should be compiled with or without instrumentation. +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation ## 2) Building the LLVM module diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index a2550d2c..8e3ca90c 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -776,7 +776,7 @@ int main(int argc, char **argv, char **envp) { "switching to classic instrumentation because " "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " - "PCGUARD. See " + "PCGUARD. Requires llvm 12+. See " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); @@ -832,7 +832,8 @@ int main(int argc, char **argv, char **envp) { FATAL( "Instrumentation type PCGUARD does not support " "AFL_LLVM_INSTRUMENT_FILE! Use " - "-fsanitize-coverage-allowlist=allowlist.txt instead, see " + "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "12+), see " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); -- cgit 1.4.1 From 9439ba1dac174741c7838c17de202b0dc68c6a88 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 1 Aug 2020 16:32:01 +0200 Subject: document env var --- docs/env_variables.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 4c0d2db7..811c5658 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -262,6 +262,9 @@ checks or alter some of the more exotic semantics of the tool: the target. This must be equal or larger than the size the target was compiled with. + - Setting AFL_DISABLE_TRIM tells afl-fuzz to no trim test cases. This is + usually a bad idea! + - Setting AFL_NO_AFFINITY disables attempts to bind to a specific CPU core on Linux systems. This slows things down, but lets you run more instances of afl-fuzz than would be prudent (if you really want to). -- cgit 1.4.1 From b708cf7d45edd0297ff47b89dd95dcf5e3664a40 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 1 Aug 2020 19:43:29 +0200 Subject: fix lto single block and no zero --- docs/Changelog.md | 2 ++ examples/persistent_demo/persistent_demo_new.c | 9 +++++--- llvm_mode/afl-clang-fast.c | 13 ++++++----- llvm_mode/afl-llvm-lto-instrim.so.cc | 2 +- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 30 ++++++++++++++++++-------- 5 files changed, 36 insertions(+), 20 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 14d00a43..8ab3fdf4 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -28,6 +28,8 @@ sending a mail to . as it is always better - LTO: env var AFL_LLVM_DOCUMENT_IDS=file will document which edge ID was given to which function during compilation + - LTO: single block functions were not implemented by default, fixed + - LTO: AFL_LLVM_SKIP_NEVERZERO behaviour was inversed, fixed - setting AFL_LLVM_LAF_SPLIT_FLOATS now activates AFL_LLVM_LAF_SPLIT_COMPARES - added honggfuzz mangle as a custom mutator in custom_mutators/honggfuzz diff --git a/examples/persistent_demo/persistent_demo_new.c b/examples/persistent_demo/persistent_demo_new.c index 86b19fa8..5f347667 100644 --- a/examples/persistent_demo/persistent_demo_new.c +++ b/examples/persistent_demo/persistent_demo_new.c @@ -30,13 +30,16 @@ /* this lets the source compile without afl-clang-fast/lto */ #ifndef __AFL_FUZZ_TESTCASE_LEN - ssize_t fuzz_len; - #define __AFL_FUZZ_TESTCASE_LEN fuzz_len + + ssize_t fuzz_len; unsigned char fuzz_buf[1024000]; + + #define __AFL_FUZZ_TESTCASE_LEN fuzz_len #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf #define __AFL_FUZZ_INIT() void sync(void); #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? - #define __AFL_INIT() sync() + #define __AFL_INIT() sync() + #endif __AFL_FUZZ_INIT(); diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index b819b43a..57330395 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -162,7 +162,7 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; - u8 have_pic = 0, have_s = 0, have_c = 0, have_shared = 0; + u8 have_pic = 0; u8 *name; cc_params = ck_alloc((argc + 128) * sizeof(u8 *)); @@ -369,15 +369,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { for (idx = 1; idx < argc; idx++) { - if (!strncmp(argv[idx], "-shared", 7)) have_shared = 1; - if (!strcmp(argv[idx], "-S")) have_s = 1; - if (!strcmp(argv[idx], "-c")) have_c = 1; if (!strncasecmp(argv[idx], "-fpic", 5)) have_pic = 1; } if (!have_pic) cc_params[cc_par_cnt++] = "-fPIC"; - // if (!have_shared && (have_s || have_c)) cc_params[cc_par_cnt++] = "-shared"; } @@ -527,9 +523,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { "unsigned char __afl_fuzz_alt[1024000];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)"; + "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " + "__afl_fuzz_alt_ptr)"; cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff ? 0 : *__afl_fuzz_len)"; + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1024000)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"; cc_params[cc_par_cnt++] = "-D__AFL_LOOP(_A)=" diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc index 880963ac..dba98777 100644 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ b/llvm_mode/afl-llvm-lto-instrim.so.cc @@ -701,7 +701,7 @@ struct InsTrimLTO : public ModulePass { Value *Incr = IRB.CreateAdd(Counter, One); - if (skip_nozero) { + if (skip_nozero == NULL) { auto cf = IRB.CreateICmpEQ(Incr, Zero); auto carry = IRB.CreateZExt(cf, Int8Ty); diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 46a97e54..430cb0ad 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -130,9 +130,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || - getenv("AFL_LLVM_SKIPSINGLEBLOCK")) - function_minimum_size = 2; + if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { @@ -540,6 +538,8 @@ bool AFLLTOPass::runOnModule(Module &M) { uint32_t succ = 0; + if (F.size() == 1) InsBlocks.push_back(&BB); + for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; ++SI) if ((*SI)->size() > 0) succ++; @@ -558,9 +558,12 @@ bool AFLLTOPass::runOnModule(Module &M) { do { --i; + BasicBlock * newBB; BasicBlock * origBB = &(*InsBlocks[i]); std::vector Successors; Instruction * TI = origBB->getTerminator(); + uint32_t fs = origBB->getParent()->size(); + uint32_t countto; for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); SI != SE; ++SI) { @@ -570,15 +573,25 @@ bool AFLLTOPass::runOnModule(Module &M) { } - if (TI == NULL || TI->getNumSuccessors() < 2) continue; + if (fs == 1) { + + newBB = origBB; + countto = 1; + + } else { + + if (TI == NULL || TI->getNumSuccessors() < 2) continue; + countto = Successors.size(); + + } // if (Successors.size() != TI->getNumSuccessors()) // FATAL("Different successor numbers %lu <-> %u\n", Successors.size(), // TI->getNumSuccessors()); - for (uint32_t j = 0; j < Successors.size(); j++) { + for (uint32_t j = 0; j < countto; j++) { - BasicBlock *newBB = llvm::SplitEdge(origBB, Successors[j]); + if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]); if (!newBB) { @@ -589,8 +602,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (documentFile) { - fprintf(documentFile, "%s %u\n", - origBB->getParent()->getName().str().c_str(), + fprintf(documentFile, "%s %u\n", F.getName().str().c_str(), afl_global_id); } @@ -627,7 +639,7 @@ bool AFLLTOPass::runOnModule(Module &M) { Value *Incr = IRB.CreateAdd(Counter, One); - if (skip_nozero) { + if (skip_nozero == NULL) { auto cf = IRB.CreateICmpEQ(Incr, Zero); auto carry = IRB.CreateZExt(cf, Int8Ty); -- cgit 1.4.1 From e0d1529061a5de9d32066c05f8faedac65b29ea5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 3 Aug 2020 10:03:45 +0200 Subject: edge id documentation example for sancov --- docs/FAQ.md | 9 +++++---- llvm_mode/afl-llvm-rt.o.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index ee221d02..c15cd484 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -103,10 +103,11 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! b) For PCGUARD instrumented binaries it is much more difficult. Here you can either modify the __sanitizer_cov_trace_pc_guard function in llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in - __afl_area_ptr[*guard] is one of the unstable edge IDs. Then recompile - and reinstall llvm_mode and rebuild your target. Run the recompiled - target with afl-fuzz for a while and then check the file that you - wrote with the backtrace information. + __afl_area_ptr[*guard] is one of the unstable edge IDs. + (Example code is already there). + Then recompile and reinstall llvm_mode and rebuild your target. + Run the recompiled target with afl-fuzz for a while and then check the + file that you wrote with the backtrace information. Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init on start, check to which memory address the edge ID value is written and set a write breakpoint to that address (`watch 0x.....`). diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index c0ed1bcf..c2859d9c 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -859,6 +859,27 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { + // For stability analysis, if you want to know to which function unstable + // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile + // the target. libunwind and libbacktrace are better solutions. + // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // the backtrace output + /* + uint32_t unstable[] = { ... unstable edge IDs }; + uint32_t idx; + char bt[1024]; + for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { + if (unstable[idx] == __afl_area_ptr[*guard]) { + int bt_size = backtrace(bt, 256); + if (bt_size > 0) { + char **bt_syms = backtrace_symbols(bt, bt_size); + if (bt_syms) + fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], bt_syms[0]); + } + } + } + */ + __afl_area_ptr[*guard]++; } -- cgit 1.4.1 From 409e4ae945ab5aeb31b1e3a1497ce5fc65226f07 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 3 Aug 2020 13:13:32 +0200 Subject: fix expand havoc for ..._only modes --- docs/Changelog.md | 1 + examples/persistent_demo/persistent_demo_new.c | 4 +-- llvm_mode/afl-llvm-rt.o.c | 48 +++++++++++++++----------- src/afl-fuzz-redqueen.c | 8 ++--- src/afl-fuzz.c | 3 +- test/test-cmplog.c | 22 +++++------- 6 files changed, 46 insertions(+), 40 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 8ab3fdf4..ae7377f2 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,7 @@ sending a mail to . - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix + - fixed a bug in redqueen for strings - llvm_mode: - now supports llvm 12! - fixes for laf-intel float splitting (thanks to mark-griffin for diff --git a/examples/persistent_demo/persistent_demo_new.c b/examples/persistent_demo/persistent_demo_new.c index 5f347667..7f878c0c 100644 --- a/examples/persistent_demo/persistent_demo_new.c +++ b/examples/persistent_demo/persistent_demo_new.c @@ -31,8 +31,8 @@ /* this lets the source compile without afl-clang-fast/lto */ #ifndef __AFL_FUZZ_TESTCASE_LEN - ssize_t fuzz_len; - unsigned char fuzz_buf[1024000]; +ssize_t fuzz_len; +unsigned char fuzz_buf[1024000]; #define __AFL_FUZZ_TESTCASE_LEN fuzz_len #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index c2859d9c..88abcbe0 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -859,26 +859,34 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { - // For stability analysis, if you want to know to which function unstable - // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile - // the target. libunwind and libbacktrace are better solutions. - // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture - // the backtrace output - /* - uint32_t unstable[] = { ... unstable edge IDs }; - uint32_t idx; - char bt[1024]; - for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { - if (unstable[idx] == __afl_area_ptr[*guard]) { - int bt_size = backtrace(bt, 256); - if (bt_size > 0) { - char **bt_syms = backtrace_symbols(bt, bt_size); - if (bt_syms) - fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], bt_syms[0]); - } - } - } - */ + // For stability analysis, if you want to know to which function unstable + // edge IDs belong to - uncomment, recompile+install llvm_mode, recompile + // the target. libunwind and libbacktrace are better solutions. + // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // the backtrace output + /* + uint32_t unstable[] = { ... unstable edge IDs }; + uint32_t idx; + char bt[1024]; + for (idx = 0; i < sizeof(unstable)/sizeof(uint32_t); i++) { + + if (unstable[idx] == __afl_area_ptr[*guard]) { + + int bt_size = backtrace(bt, 256); + if (bt_size > 0) { + + char **bt_syms = backtrace_symbols(bt, bt_size); + if (bt_syms) + fprintf(stderr, "DEBUG: edge=%u caller=%s\n", unstable[idx], + bt_syms[0]); + + } + + } + + } + + */ __afl_area_ptr[*guard]++; diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index b58c8537..cb4c78df 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -673,15 +673,15 @@ static u8 rtn_extend_encoding(afl_state_t *afl, struct cmp_header *h, for (i = 0; i < its_len; ++i) { - if (pattern[i] != buf[idx + i] || - o_pattern[i] != orig_buf[idx + i] || *status == 1) { + if (pattern[i] != buf[idx + i] || o_pattern[i] != orig_buf[idx + i] || + *status == 1) { break; } buf[idx + i] = repl[i]; - + if (unlikely(its_fuzz(afl, buf, len, status))) { return 1; } } @@ -727,7 +727,7 @@ static u8 rtn_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u32 len) { } for (idx = 0; idx < len && fails < 8; ++idx) { - + if (unlikely(rtn_extend_encoding(afl, h, o->v0, o->v1, orig_o->v0, idx, orig_buf, buf, len, &status))) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 326ccc1c..da30797c 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1304,7 +1304,8 @@ int main(int argc, char **argv_orig, char **envp) { afl->expand_havoc = 1; break; case 1: - if (afl->limit_time_sig == 0) { + if (afl->limit_time_sig == 0 && !afl->custom_only && + !afl->python_only) { afl->limit_time_sig = -1; afl->limit_time_puppet = 0; diff --git a/test/test-cmplog.c b/test/test-cmplog.c index 75efd887..b077e3ab 100644 --- a/test/test-cmplog.c +++ b/test/test-cmplog.c @@ -5,23 +5,19 @@ #include #include int main(int argc, char *argv[]) { - char buf[1024]; + + char buf[1024]; ssize_t i; - if ((i = read(0, buf, sizeof(buf) - 1)) < 24) - return 0; + if ((i = read(0, buf, sizeof(buf) - 1)) < 24) return 0; buf[i] = 0; - if (buf[0] != 'A') - return 0; - if (buf[1] != 'B') - return 0; - if (buf[2] != 'C') - return 0; - if (buf[3] != 'D') - return 0; - if (memcmp(buf + 4, "1234", 4) || memcmp(buf + 8, "EFGH", 4)) - return 0; + if (buf[0] != 'A') return 0; + if (buf[1] != 'B') return 0; + if (buf[2] != 'C') return 0; + if (buf[3] != 'D') return 0; + if (memcmp(buf + 4, "1234", 4) || memcmp(buf + 8, "EFGH", 4)) return 0; if (strncmp(buf + 12, "IJKL", 4) == 0 && strcmp(buf + 16, "DEADBEEF") == 0) abort(); return 0; + } -- cgit 1.4.1 From 6d364dd2cb0ac31797b52e590b57bf9c10cc2302 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 01:13:51 +0200 Subject: add sancov-like allow/denylist instrument feature --- README.md | 16 +- TODO.md | 1 - docs/Changelog.md | 4 + docs/FAQ.md | 2 +- docs/env_variables.md | 11 +- docs/perf_tips.md | 2 +- gcc_plugin/GNUmakefile | 2 +- gcc_plugin/Makefile | 2 +- include/envs.h | 3 + llvm_mode/README.instrument_file.md | 81 ----- llvm_mode/README.instrument_list.md | 86 +++++ llvm_mode/README.lto.md | 5 +- llvm_mode/README.md | 2 +- llvm_mode/afl-clang-fast.c | 32 +- llvm_mode/afl-llvm-common.cc | 485 +++++++++++++++++++++++----- llvm_mode/afl-llvm-lto-instrumentlist.so.cc | 156 +-------- 16 files changed, 567 insertions(+), 323 deletions(-) delete mode 100644 llvm_mode/README.instrument_file.md create mode 100644 llvm_mode/README.instrument_list.md (limited to 'docs') diff --git a/README.md b/README.md index bd2784ae..2e24a534 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ anything below 9 is not recommended. +--------------------------------+ | if you want to instrument only | -> use afl-gcc-fast and afl-gcc-fast++ | parts of the target | see [gcc_plugin/README.md](gcc_plugin/README.md) and - +--------------------------------+ [gcc_plugin/README.instrument_file.md](gcc_plugin/README.instrument_file.md) + +--------------------------------+ [gcc_plugin/README.instrument_list.md](gcc_plugin/README.instrument_list.md) | | if not, or if you do not have a gcc with plugin support | @@ -290,12 +290,18 @@ selectively only instrument parts of the target that you are interested in: create a file with all the filenames of the source code that should be instrumented. For afl-clang-lto and afl-gcc-fast - or afl-clang-fast if either the clang - version is < 7 or the CLASSIC instrumentation is used - just put one - filename per line, no directory information necessary, and set - `export AFL_LLVM_INSTRUMENT_FILE=yourfile.txt` - see [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + version is below 7 or the CLASSIC instrumentation is used - just put one + filename or function per line (no directory information necessary for + filenames9, and either set `export AFL_LLVM_ALLOWLIST=allowlist.txt` **or** + `export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per + default to instrument unless noted (DENYLIST) or not perform instrumentation + unless requested (ALLOWLIST). + **NOTE:** In optimization functions might be inlined and then not match! + see [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html) + The llvm sancov format works with the allowlist/denylist feature of afl++ + however afl++ is more flexible in the format. There are many more options and modes available however these are most of the time less effective. See: diff --git a/TODO.md b/TODO.md index 999cb9d3..e81b82a3 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ ## Roadmap 2.67+ - - expand on AFL_LLVM_INSTRUMENT_FILE to also support sancov allowlist format - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores diff --git a/docs/Changelog.md b/docs/Changelog.md index ae7377f2..f98f8b9b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,10 @@ sending a mail to . - fixed a bug in redqueen for strings - llvm_mode: - now supports llvm 12! + - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous + AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and + are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm + sancov, and also supports function matching! - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: autodictionary mode is a default diff --git a/docs/FAQ.md b/docs/FAQ.md index c15cd484..33ce49e6 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -117,7 +117,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! Identify which source code files contain the functions that you need to remove from instrumentation. - Simply follow this document on how to do this: [llvm_mode/README.instrument_file.md](llvm_mode/README.instrument_file.md) + Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) diff --git a/docs/env_variables.md b/docs/env_variables.md index 811c5658..f0ae0b6c 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -202,14 +202,15 @@ Then there are a few specific features that are only available in llvm_mode: See llvm_mode/README.laf-intel.md for more information. -### INSTRUMENT_FILE +### INSTRUMENT LIST (selectively instrument files and functions) This feature allows selectively instrumentation of the source - - Setting AFL_LLVM_INSTRUMENT_FILE with a filename will only instrument those - files that match the names listed in this file. + - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or + function will only instrument (or skip) those files that match the names + listed in the specified file. - See llvm_mode/README.instrument_file.md for more information. + See llvm_mode/README.instrument_list.md for more information. ### NOT_ZERO @@ -241,7 +242,7 @@ Then there are a few specific features that are only available in the gcc_plugin - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those files that match the names listed in this file (one filename per line). - See gcc_plugin/README.instrument_file.md for more information. + See gcc_plugin/README.instrument_list.md for more information. ## 3) Settings for afl-fuzz diff --git a/docs/perf_tips.md b/docs/perf_tips.md index 7a690b77..731dc238 100644 --- a/docs/perf_tips.md +++ b/docs/perf_tips.md @@ -67,7 +67,7 @@ to get to the important parts in the code. If you are only interested in specific parts of the code being fuzzed, you can instrument_files the files that are actually relevant. This improves the speed and -accuracy of afl. See llvm_mode/README.instrument_file.md +accuracy of afl. See llvm_mode/README.instrument_list.md Also use the InsTrim mode on larger binaries, this improves performance and coverage a lot. diff --git a/gcc_plugin/GNUmakefile b/gcc_plugin/GNUmakefile index 4a4f0dcd..f10a6c1d 100644 --- a/gcc_plugin/GNUmakefile +++ b/gcc_plugin/GNUmakefile @@ -163,7 +163,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/gcc_plugin/Makefile b/gcc_plugin/Makefile index f720112f..c088b61c 100644 --- a/gcc_plugin/Makefile +++ b/gcc_plugin/Makefile @@ -152,7 +152,7 @@ install: all install -m 755 ../afl-gcc-fast $${DESTDIR}$(BIN_PATH) install -m 755 ../afl-gcc-pass.so ../afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH) install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md - install -m 644 -T README.instrument_file.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md + install -m 644 -T README.instrument_list.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.instrument_file.md clean: rm -f *.o *.so *~ a.out core core.[1-9][0-9]* test-instr .test-instr0 .test-instr1 .test2 diff --git a/include/envs.h b/include/envs.h index 7153ed47..96ae91ba 100644 --- a/include/envs.h +++ b/include/envs.h @@ -62,6 +62,9 @@ static char *afl_environment_variables[] = { "AFL_REAL_LD", "AFL_LD_PRELOAD", "AFL_LD_VERBOSE", + "AFL_LLVM_ALLOWLIST", + "AFL_LLVM_DENYLIST", + "AFL_LLVM_BLOCKLIST", "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md deleted file mode 100644 index 46e45ba2..00000000 --- a/llvm_mode/README.instrument_file.md +++ /dev/null @@ -1,81 +0,0 @@ -# Using afl++ with partial instrumentation - - This file describes how you can selectively instrument only the source files - that are interesting to you using the LLVM instrumentation provided by - afl++ - - Originally developed by Christian Holler (:decoder) . - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the LLVM -mode of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - -Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation - -## 2) Building the LLVM module - -The new code is part of the existing afl++ LLVM module in the llvm_mode/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-clang-fast and afl-clang-fast++ respectively. The only required change is -that you need to set the environment variable AFL_LLVM_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this the instrument file list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a the instrument file list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument file list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created the instrument file list file is then set to AFL_LLVM_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the the instrument file list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. - -For old LLVM versions this feature might require to be compiled with debug -information (-g), however at least from llvm version 6.0 onwards this is not -required anymore (and might hurt performance and crash detection, so better not -use -g). - -## 4) UNIX-style filename pattern matching -You can add UNIX-style pattern matching in the the instrument file list entries. See `man -fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md new file mode 100644 index 00000000..b0e0cc1e --- /dev/null +++ b/llvm_mode/README.instrument_list.md @@ -0,0 +1,86 @@ +# Using afl++ with partial instrumentation + + This file describes how you can selectively instrument only the source files + or functions that are interesting to you using the LLVM instrumentation + provided by afl++ + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, a "partial instrumentation" support en par with llvm sancov +is provided by afl++ that allows you to specify on a source file and function +level which should be compiled with or without instrumentation. + +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation + +the llvm sancov list format is fully supported by afl++, however afl++ has +more flexbility. + +## 2) Building the LLVM module + +The new code is part of the existing afl++ LLVM module in the llvm_mode/ +subdirectory. There is nothing specifically to do :) + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. +The only required change is that you need to set either the environment variable +AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. + +That file then contains the filenames or functions that should be instrumented +(AFL_LLVM_ALLOWLIST) or should specifically NOT instrumentd (AFL_LLVM_DENYLIST). + +For matching, the function/filename that is being compiled must end in the +function/filename entry contained in this the instrument file list (to avoid +breaking the matching when absolute paths are used during compilation). + +**NOTE:** In optimization functions might be inlined and then not match! + +For example if your source tree looks like this: +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create a the instrument file list file containing: +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the instrument file list file contains only this, it works as well: +``` +a1.cpp +a2.cpp +``` +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +You can also specify function names. Note that for C++ the function names +must be mangled to match! + +afl++ is intelligent to identify if an entry is a filename or a function. +However if you want to be sure (and compliant to the sancov allow/blocklist +format), you can file entries like this: +``` +src: *malloc.c +``` +and function entries like this: +``` +fun: MallocFoo +``` +Note that whitespace is ignored and comments (`# foo`) supported. + +## 4) UNIX-style pattern matching +You can add UNIX-style pattern matching in the the instrument file list entries. +See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index e521ac82..4d643324 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -108,15 +108,12 @@ make install Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. -Also the instrument file listing (AFL_LLVM_INSTRUMENT_FILE -> [README.instrument_file.md](README.instrument_file.md)) and +Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -InsTrim (control flow graph instrumentation) is supported and recommended! - (set `AFL_LLVM_INSTRUMENT=CFG`) Example: ``` CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure -export AFL_LLVM_INSTRUMENT=CFG make ``` diff --git a/llvm_mode/README.md b/llvm_mode/README.md index 22088dfd..f23d7150 100644 --- a/llvm_mode/README.md +++ b/llvm_mode/README.md @@ -109,7 +109,7 @@ Several options are present to make llvm_mode faster or help it rearrange the code to make afl-fuzz path discovery easier. If you need just to instrument specific parts of the code, you can the instrument file list -which C/C++ files to actually instrument. See [README.instrument_file](README.instrument_file.md) +which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md) For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md) diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index ef99e3f3..f75adf1e 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -229,7 +229,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode) { if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST")) { + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; @@ -637,9 +638,13 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) && + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && getenv("AFL_DONT_OPTIMIZE")) - FATAL("AFL_LLVM_INSTRUMENT_FILE and AFL_DONT_OPTIMIZE cannot be combined"); + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -787,15 +792,17 @@ int main(int argc, char **argv, char **envp) { #if LLVM_VERSION_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) { + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { instrument_mode = INSTRUMENT_AFL; WARNF( "switching to classic instrumentation because " - "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See " - "https://clang.llvm.org/docs/" + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " "SanitizerCoverage.html#partially-disabling-instrumentation"); } else @@ -846,11 +853,14 @@ int main(int argc, char **argv, char **envp) { "together"); if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))) + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_INSTRUMENT_FILE! Use " - "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " "12+), see " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 9a884ded..0b89c3b4 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -20,7 +20,10 @@ using namespace llvm; -static std::list myInstrumentList; +static std::list allowListFiles; +static std::list allowListFunctions; +static std::list denyListFiles; +static std::list denyListFunctions; char *getBBName(const llvm::BasicBlock *BB) { @@ -87,30 +90,166 @@ bool isIgnoreFunction(const llvm::Function *F) { void initInstrumentList() { - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); + char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - if (instrumentListFilename) { + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " + "but not both!"); + + if (allowlist) { std::string line; std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); getline(fileStream, line); + while (fileStream) { - myInstrumentList.push_back(line); - getline(fileStream, line); + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + } - if (debug) - SAYF(cMGN "[D] " cRST "loaded instrument list with %zu entries\n", - myInstrumentList.size()); + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } @@ -121,42 +260,173 @@ bool isInInstrumentList(llvm::Function *F) { if (!F->size() || isIgnoreFunction(F)) return false; // if we do not have a the instrument file list return true - if (myInstrumentList.empty()) return true; + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + if (!allowListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); #if LLVM_VERSION_MAJOR >= 4 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { + if (Loc) { + + DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); + + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); + if (instFilename.str().empty()) { - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { + + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); + + } + + } - if (instFilename.str().empty()) { + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return true; + + } + + } + + } + + } + + } + + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return true; + + } + + } + + } + + } } } - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { +#endif + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; + + } + + return false; + + } + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -164,16 +434,16 @@ bool isInInstrumentList(llvm::Function *F) { * specified in the list. We also allow UNIX-style pattern * matching */ - if (instFilename.str().length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the list (%s), instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return true; + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; } @@ -183,35 +453,64 @@ bool isInInstrumentList(llvm::Function *F) { } - } + if (!denyListFiles.empty()) { -#else - if (!Loc.isUnknown()) { + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + if (instFilename.str().empty()) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { - if (instFilename.str().length() >= it->length()) { + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + } - return true; + } + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the denylist (%s), not " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return false; + + } + + } } @@ -221,23 +520,65 @@ bool isInInstrumentList(llvm::Function *F) { } - } +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return false; + + } + + } + + } + + } + + } + + } #endif - else { - - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return true; + + } + + return true; } - return false; + return true; // not reached } diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc index ab7c0c58..a7331444 100644 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc @@ -59,39 +59,9 @@ class AFLcheckIfInstrument : public ModulePass { static char ID; AFLcheckIfInstrument() : ModulePass(ID) { - int entries = 0; - if (getenv("AFL_DEBUG")) debug = 1; - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); - if (instrumentListFilename) { - - std::string line; - std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { - - myInstrumentList.push_back(line); - getline(fileStream, line); - entries++; - - } - - } else - - PFATAL( - "afl-llvm-lto-instrumentlist.so loaded without " - "AFL_LLVM_INSTRUMENT_FILE?!"); - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded the instrument file list %s with %d entries\n", - instrumentListFilename, entries); + initInstrumentList(); } @@ -129,120 +99,28 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { for (auto &F : M) { if (F.size() < 1) continue; - // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - if (isIgnoreFunction(&F)) continue; - - BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (!myInstrumentList.empty()) { - - bool instrumentFunction = false; - - /* Get the current location using debug information. - * For now, just instrument the block if we are not able - * to determine our location. */ - DebugLoc Loc = IP->getDebugLoc(); - if (Loc) { - - DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); - - if (instFilename.str().empty()) { - - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { - - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); - - } - - if (instFilename.str().empty()) { - if (!be_quiet) - WARNF( - "Function %s has no source file name information and will " - "not be instrumented.", - F.getName().str().c_str()); - continue; - - } - - } - - //(void)instLine; - - fprintf(stderr, "xxx %s %s\n", F.getName().str().c_str(), - instFilename.str().c_str()); - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in file %s\n", - F.getName().str().c_str(), instFilename.str().c_str()); - - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.str().length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { - - instrumentFunction = true; - break; - - } - - } - - } - - } else { - - if (!be_quiet) - WARNF( - "No debug information found for function %s, recompile with -g " - "-O[1-3]", - F.getName().str().c_str()); - continue; - - } - - /* Either we couldn't figure out our location or the location is - * not the instrument file listed, so we skip instrumentation. - * We do this by renaming the function. */ - if (instrumentFunction == true) { - - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); - - } else { - - if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - auto & Ctx = F.getContext(); - AttributeList Attrs = F.getAttributes(); - AttrBuilder NewAttrs; - NewAttrs.addAttribute("skipinstrument"); - F.setAttributes( - Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); + if (isInInstrumentList(&F)) { - } + if (debug) + SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { - PFATAL("InstrumentList is empty"); + if (debug) + SAYF(cMGN "[D] " cRST + "function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); + + auto & Ctx = F.getContext(); + AttributeList Attrs = F.getAttributes(); + AttrBuilder NewAttrs; + NewAttrs.addAttribute("skipinstrument"); + F.setAttributes( + Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); } -- cgit 1.4.1 From 3d8f05458041d6007617e383453c7ad66b345144 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 01:19:31 +0200 Subject: readme fix --- docs/FAQ.md | 5 ++- gcc_plugin/README.instrument_file.md | 73 ------------------------------------ gcc_plugin/README.instrument_list.md | 73 ++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 75 deletions(-) delete mode 100644 gcc_plugin/README.instrument_file.md create mode 100644 gcc_plugin/README.instrument_list.md (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index 33ce49e6..e690635a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -112,10 +112,11 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! on start, check to which memory address the edge ID value is written and set a write breakpoint to that address (`watch 0x.....`). - 3. Third step: create a text file with the filenames + 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to - remove from instrumentation. + remove from instrumentation, or just specify the functions you want to + skip instrumenting. Note that optimization might inline functions! Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): diff --git a/gcc_plugin/README.instrument_file.md b/gcc_plugin/README.instrument_file.md deleted file mode 100644 index d0eaf6ff..00000000 --- a/gcc_plugin/README.instrument_file.md +++ /dev/null @@ -1,73 +0,0 @@ -======================================== -Using afl++ with partial instrumentation -======================================== - - This file describes how you can selectively instrument only the source files - that are interesting to you using the gcc instrumentation provided by - afl++. - - Plugin by hexcoder-. - - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the gcc -plugin of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - - -## 2) Building the gcc plugin - -The new code is part of the existing afl++ gcc plugin in the gcc_plugin/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-gcc-fast and afl-g++-fast respectively. The only required change is -that you need to set the environment variable AFL_GCC_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this instrument list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a instrument list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created instrument list file is then set to AFL_GCC_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the instrument list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. diff --git a/gcc_plugin/README.instrument_list.md b/gcc_plugin/README.instrument_list.md new file mode 100644 index 00000000..d0eaf6ff --- /dev/null +++ b/gcc_plugin/README.instrument_list.md @@ -0,0 +1,73 @@ +======================================== +Using afl++ with partial instrumentation +======================================== + + This file describes how you can selectively instrument only the source files + that are interesting to you using the gcc instrumentation provided by + afl++. + + Plugin by hexcoder-. + + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, I have added a "partial instrumentation" support to the gcc +plugin of AFLFuzz that allows you to specify on a source file level which files +should be compiled with or without instrumentation. + + +## 2) Building the gcc plugin + +The new code is part of the existing afl++ gcc plugin in the gcc_plugin/ +subdirectory. There is nothing specifically to do :) + + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-gcc-fast and afl-g++-fast respectively. The only required change is +that you need to set the environment variable AFL_GCC_INSTRUMENT_FILE when calling +the compiler. + +The environment variable must point to a file containing all the filenames +that should be instrumented. For matching, the filename that is being compiled +must end in the filename entry contained in this instrument list (to avoid breaking +the matching when absolute paths are used during compilation). + +For example if your source tree looks like this: + +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create a instrument list file containing: + +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the instrument list file contains only this, it works as well: + +``` +a1.cpp +a2.cpp +``` + +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +The created instrument list file is then set to AFL_GCC_INSTRUMENT_FILE when you compile +your program. For each file that didn't match the instrument list, the compiler will +issue a warning at the end stating that no blocks were instrumented. If you +didn't intend to instrument that file, then you can safely ignore that warning. -- cgit 1.4.1 From 4a6d66d8c5dcbec8b5014ff0445d9292b3958e1d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 14:43:17 +0200 Subject: fix typos --- docs/Changelog.md | 1 + llvm_mode/README.instrument_list.md | 2 +- llvm_mode/README.persistent_mode.md | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f98f8b9b..f8742b1c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,7 @@ sending a mail to . - LTO: AFL_LLVM_SKIP_NEVERZERO behaviour was inversed, fixed - setting AFL_LLVM_LAF_SPLIT_FLOATS now activates AFL_LLVM_LAF_SPLIT_COMPARES + - support for -E and -shared compilation runs - added honggfuzz mangle as a custom mutator in custom_mutators/honggfuzz - added afl-frida gum solution to examples/afl_frida (mostly imported from https://github.com/meme/hotwax/) diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md index b0e0cc1e..d4739dda 100644 --- a/llvm_mode/README.instrument_list.md +++ b/llvm_mode/README.instrument_list.md @@ -71,7 +71,7 @@ must be mangled to match! afl++ is intelligent to identify if an entry is a filename or a function. However if you want to be sure (and compliant to the sancov allow/blocklist -format), you can file entries like this: +format), you can specify source file entries like this: ``` src: *malloc.c ``` diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md index 5ed59a58..7d2fd93b 100644 --- a/llvm_mode/README.persistent_mode.md +++ b/llvm_mode/README.persistent_mode.md @@ -125,7 +125,7 @@ Add after the includes: extern unsigned char *__afl_area_ptr; #define MAX_DUMMY_SIZE 256000 -__attribute__((constructor(10))) void __afl_protect(void) { +__attribute__((constructor(1))) void __afl_protect(void) { #ifdef MAP_FIXED_NOREPLACE __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) @@ -139,6 +139,7 @@ __attribute__((constructor(10))) void __afl_protect(void) { and just before `__AFL_INIT()`: ``` munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + __afl_area_ptr = NULL; ``` ## 4) persistent mode -- cgit 1.4.1 From 701fb95d24cd754e9c116d81502b6057a29eb2bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 23:42:33 +0200 Subject: LTO: make dynamic map the default --- docs/Changelog.md | 2 ++ llvm_mode/README.lto.md | 20 ++++------------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 33 +++++++++++++++++++++++----- src/afl-forkserver.c | 13 +++++------ 4 files changed, 40 insertions(+), 28 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f8742b1c..182a15b8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -28,6 +28,8 @@ sending a mail to . sancov, and also supports function matching! - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR + for a fixed map address (eg. 0x10000) - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index 4d643324..9046c5a8 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -17,9 +17,6 @@ This version requires a current llvm 11+ compiled from the github master. 5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. -6. If a target uses _init functions or early constructors then additionally - set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise! - ## Introduction and problem description A big issue with how afl/afl++ works is that the basic block IDs that are @@ -128,14 +125,14 @@ on start. This improves coverage statistically by 5-10% :) ## Fixed memory map -To speed up fuzzing, the shared memory map is hard set to a specific address, -by default 0x10000. In most cases this will work without any problems. +To speed up fuzzing, it is possible to set a fixed shared memory map. +Recommened is the value 0x10000. +In most cases this will work without any problems. However if a target uses +early constructors, ifuncs or a deferred forkserver this can crash the target. On unusual operating systems/processors/kernels or weird libraries this might fail so to change the fixed address at compile time set AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address to be dynamic - the original afl way, which is slower). -AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which -is safer but also slower). ## Document edge IDs @@ -262,15 +259,6 @@ If this succeeeds then there is an issue with afl-clang-lto. Please report at Even some targets where clang-12 fails can be build if the fail is just in `./configure`, see `Solving difficult targets` above. -### Target crashes immediately - -If the target is using early constructors (priority values smaller than 6) -or have their own _init/.init functions and these are instrumented then the -target will likely crash when started. This can be avoided by compiling with -`AFL_LLVM_MAP_DYNAMIC=1` . - -This can e.g. happen with OpenSSL. - ## History This was originally envisioned by hexcoder- in Summer 2019, however we saw no diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 38c3f202..ddfcb400 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -49,6 +49,7 @@ #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Pass.h" +#include "llvm/IR/Constants.h" #include "afl-llvm-common.h" @@ -135,7 +136,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_AUTODICTIONARY")) autodictionary = 1; - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; @@ -196,7 +200,8 @@ bool AFLLTOPass::runOnModule(Module &M) { ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - /* This dumps all inialized global strings - might be useful in the future + // This dumps all inialized global strings - might be useful in the future + /* for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { GlobalVariable &GV=*G; @@ -212,7 +217,21 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ + */ + + if (map_addr) + for (GlobalIFunc &IF : M.ifuncs()) { + + // No clue how to follow these up and find the resolver function. + // If we would know that resolver function name we could just skip + // instrumenting it and everything would be fine :-( + // StringRef ifunc_name = IF.getName(); + // Constant *r = IF.getResolver(); + FATAL( + "Target uses ifunc attribute, dynamic map cannot be used, remove " + "AFL_LLVM_MAP_DYNAMIC"); + + } /* Instrument all the things! */ @@ -220,8 +239,12 @@ bool AFLLTOPass::runOnModule(Module &M) { for (auto &F : M) { - // fprintf(stderr, "DEBUG: Module %s Function %s\n", - // M.getName().str().c_str(), F.getName().str().c_str()); + /*For debugging + AttributeSet X = F.getAttributes().getFnAttributes(); + fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", + M.getName().str().c_str(), F.getName().str().c_str(), + X.getNumAttributes()); + */ if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 752641d7..1ececf27 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -293,8 +293,8 @@ static void report_error_and_exit(int error) { FATAL( "the fuzzing target reports that hardcoded map address might be the " "reason the mmap of the shared memory failed. Solution: recompile " - "the target with either afl-clang-lto and the environment variable " - "AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast."); + "the target with either afl-clang-lto and do not set " + "AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast."); break; case FS_ERROR_SHM_OPEN: FATAL("the fuzzing target reports that the shm_open() call failed."); @@ -828,8 +828,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated before we could" " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto then recompiling with" - " AFL_LLVM_MAP_DYNAMIC might solve your problem.\n" + "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR" + " then recompiling without this parameter.\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke for troubleshooting tips.\n"); @@ -860,9 +860,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - the target was compiled with afl-clang-lto and a constructor " "was\n" - " instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve " - "your\n" - " problem\n\n" + " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " + "your problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " "options\n" -- cgit 1.4.1 From 50e76fce123f01ec83024f3bbd3190f2e1a6d387 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 02:05:39 +0200 Subject: adding ctor function skipping in LTO fixed map mode --- docs/Changelog.md | 1 + include/debug.h | 52 +++++++++---------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 76 +++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 39 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 182a15b8..25c7a761 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -30,6 +30,7 @@ sending a mail to . reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) + - LTO: skipping ctors and ifuncs in fix map address instrumentation - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/include/debug.h b/include/debug.h index 6cc26ec2..f9ebce58 100644 --- a/include/debug.h +++ b/include/debug.h @@ -218,43 +218,43 @@ /* Die with a verbose non-OS fatal error message. */ -#define FATAL(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ +#define FATAL(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ - __FILE__, __LINE__); \ - exit(1); \ - \ + __FILE__, __LINE__); \ + exit(1); \ + \ } while (0) /* Die by calling abort() to provide a core dump. */ -#define ABORT(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ +#define ABORT(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ - __FILE__, __LINE__); \ - abort(); \ - \ + __FILE__, __LINE__); \ + abort(); \ + \ } while (0) /* Die while also including the output of perror(). */ -#define PFATAL(x...) \ - do { \ - \ - fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ +#define PFATAL(x...) \ + do { \ + \ + fflush(stdout); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ - __FILE__, __LINE__); \ - SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ - exit(1); \ - \ + __FILE__, __LINE__); \ + SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ + exit(1); \ + \ } while (0) /* Die with FATAL() or PFATAL() depending on the value of res (used to diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index abc836aa..fd8e48a7 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -224,22 +224,70 @@ bool AFLLTOPass::runOnModule(Module &M) { if (map_addr) { for (GlobalIFunc &IF : M.ifuncs()) { - + StringRef ifunc_name = IF.getName(); Constant *r = IF.getResolver(); StringRef r_name = cast(r->getOperand(0))->getName(); if (!be_quiet) - fprintf(stderr, "Found an ifunc with name %s that points to resolver function %s, we cannot instrument this, putting it into a block list.\n", + fprintf(stderr, + "Warning: Found an ifunc with name %s that points to resolver " + "function %s, we cannot instrument this, putting it into a " + "block list.\n", ifunc_name.str().c_str(), r_name.str().c_str()); - module_block_list.push_back(r_name.str()); } - // next up: ctors run before __afl_init() - - // TODO + GlobalVariable *GV = M.getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0 && + Priority <= 5) { + + if (!be_quiet) + fprintf(stderr, + "Warning: Found constructor function %s with prio " + "%u, we cannot instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + module_block_list.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } } @@ -260,21 +308,23 @@ bool AFLLTOPass::runOnModule(Module &M) { if (isIgnoreFunction(&F)) continue; if (module_block_list.size()) { - + for (auto bname : module_block_list) { std::string fname = F.getName().str(); if (fname.compare(bname) == 0) { - + if (!be_quiet) - WARNF("Skipping instrumentation of ifunc resolver function %s", - fname.c_str()); - + WARNF( + "Skipping instrumentation of dangerous early running function " + "%s", + fname.c_str()); + } - + } - + } // the instrument file list check -- cgit 1.4.1 From 132b57cf030fd206bc54d6c810868c48b17445bb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 10:41:09 +0200 Subject: nozero for llvm pcguard --- docs/Changelog.md | 1 + llvm_mode/afl-llvm-rt.o.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 25c7a761..eda57a1a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,7 @@ sending a mail to . AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm sancov, and also supports function matching! + - added nozero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 623e3a20..8a073318 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -904,7 +904,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { */ - __afl_area_ptr[*guard]++; + __afl_area_ptr[*guard] = __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); } -- cgit 1.4.1 From b38837f4ff8f2e52597b7908b9226500e5c61933 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 14:14:44 +0200 Subject: setting attribute hot intelligently gives 0.5% speed --- docs/Changelog.md | 2 +- src/afl-forkserver.c | 4 ++-- src/afl-fuzz-bitmap.c | 8 ++++---- src/afl-fuzz-run.c | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index eda57a1a..edcdac58 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,7 +26,7 @@ sending a mail to . AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm sancov, and also supports function matching! - - added nozero counting to trace-pc/pcgard + - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 1ececf27..6819fc8a 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -122,7 +122,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, +static u32 __attribute__ ((hot)) read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; @@ -322,7 +322,7 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, +void __attribute__ ((hot)) afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index aa8d5a18..f6389c06 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -u8 has_new_bits(afl_state_t *afl, u8 *virgin_map) { +u8 __attribute__ ((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { #ifdef WORD_SIZE_64 @@ -347,7 +347,7 @@ void init_count_class16(void) { #ifdef WORD_SIZE_64 -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { u64 *mem = (u64 *)fsrv->trace_bits; @@ -376,7 +376,7 @@ void classify_counts(afl_forkserver_t *fsrv) { #else -void classify_counts(afl_forkserver_t *fsrv) { +void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { u32 *mem = (u32 *)fsrv->trace_bits; @@ -534,7 +534,7 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__ ((hot)) save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index e69e9791..97fcb3c8 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -38,7 +38,7 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, +fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING @@ -72,7 +72,7 @@ fsrv_run_result_t fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__ ((hot)) write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; @@ -858,7 +858,7 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__ ((hot)) common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; -- cgit 1.4.1 From 7a6867e2f8e8b698c08366f79d0c8751b09ce431 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 16:06:30 +0200 Subject: split up __afl_manual_init, added internal AFL_DISABLE_LLVM_INSTRUMENTATION, skipping ctor+ifunc functions for all llvm, code-format --- TODO.md | 2 + docs/Changelog.md | 2 +- examples/aflpp_driver/aflpp_driver.c | 25 ---- examples/aflpp_driver/aflpp_driver_test.c | 16 ++- llvm_mode/LLVMInsTrim.so.cc | 2 + llvm_mode/afl-llvm-common.cc | 195 ++++++++++++++++++--------- llvm_mode/afl-llvm-common.h | 1 + llvm_mode/afl-llvm-lto-instrumentation.so.cc | 94 +------------ llvm_mode/afl-llvm-pass.so.cc | 1 + llvm_mode/afl-llvm-rt.o.c | 38 +++++- src/afl-forkserver.c | 11 +- src/afl-fuzz-bitmap.c | 9 +- src/afl-fuzz-run.c | 10 +- 13 files changed, 203 insertions(+), 203 deletions(-) (limited to 'docs') diff --git a/TODO.md b/TODO.md index e81b82a3..f8ef0e0f 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,8 @@ - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores + - feature for afl-showmap to generate the coverage for all queue entries + - afl-plot to support multiple plot_data ## Further down the road diff --git a/docs/Changelog.md b/docs/Changelog.md index edcdac58..1c5b3f4a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -29,9 +29,9 @@ sending a mail to . - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) - - LTO: skipping ctors and ifuncs in fix map address instrumentation - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 7d388799..b764338e 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -109,7 +109,6 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. int __afl_sharedmem_fuzzing = 1; extern unsigned int * __afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; -extern unsigned char *__afl_area_ptr; // extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. @@ -248,28 +247,8 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } -__attribute__((constructor(1))) void __afl_protect(void) { - - setenv("__AFL_DEFER_FORKSRV", "1", 1); - __afl_area_ptr = (unsigned char *)mmap( - (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char *)mmap((void *)0x10000, MAX_DUMMY_SIZE, - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = - (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - // __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; - -} - int main(int argc, char **argv) { - fprintf(stderr, "dummy map is at %p\n", __afl_area_ptr); - printf( "======================= INFO =========================\n" "This binary is built for afl++.\n" @@ -307,8 +286,6 @@ int main(int argc, char **argv) { else if (argc > 1) { __afl_sharedmem_fuzzing = 0; - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); // we need to free 0x10000 - __afl_area_ptr = NULL; __afl_manual_init(); return ExecuteFilesOnyByOne(argc, argv); @@ -317,8 +294,6 @@ int main(int argc, char **argv) { assert(N > 0); // if (!getenv("AFL_DRIVER_DONT_DEFER")) - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); - __afl_area_ptr = NULL; __afl_manual_init(); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c index e4567bbf..ddc3effb 100644 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ b/examples/aflpp_driver/aflpp_driver_test.c @@ -4,6 +4,16 @@ #include "hash.h" +void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Data[0] == 'F') + if (Data[1] == 'A') + if (Data[2] == '$') + if (Data[3] == '$') + if (Data[4] == '$') abort(); + +} + int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { fprintf(stderr, "FUNC crc: %016llx len: %lu\n", @@ -13,11 +23,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { if (Size < 5) return 0; - if (Data[0] == 'F') - if (Data[1] == 'A') - if (Data[2] == '$') - if (Data[3] == '$') - if (Data[4] == '$') abort(); + crashme(Data, Size); return 0; diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 4d8c4719..2ad7f171 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -256,6 +256,8 @@ struct InsTrim : public ModulePass { u64 total_rs = 0; u64 total_hs = 0; + scanForDangerousFunctions(&M); + for (Function &F : M) { if (debug) { diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 0b50c547..f12bbe31 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -67,8 +67,11 @@ bool isIgnoreFunction(const llvm::Function *F) { "__libc_csu", "__asan", "__msan", + "__cmplog", + "__sancov", "msan.", "LLVMFuzzer", + "__decide_deferred", "maybe_duplicate_stderr", "discard_output", "close_stdout", @@ -253,21 +256,93 @@ void initInstrumentList() { } +void scanForDangerousFunctions(llvm::Module *M) { + + if (!M) return; + + for (GlobalIFunc &IF : M->ifuncs()) { + + StringRef ifunc_name = IF.getName(); + Constant *r = IF.getResolver(); + StringRef r_name = cast(r->getOperand(0))->getName(); + if (!be_quiet) + fprintf(stderr, + "Info: Found an ifunc with name %s that points to resolver " + "function %s, we will not instrument this, putting it into the " + "block list.\n", + ifunc_name.str().c_str(), r_name.str().c_str()); + denyListFunctions.push_back(r_name.str()); + + } + + GlobalVariable *GV = M->getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0) { + + if (!be_quiet) + fprintf(stderr, + "Info: Found constructor function %s with prio " + "%u, we will not instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + denyListFunctions.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } + +} + bool isInInstrumentList(llvm::Function *F) { + bool return_default = true; + // is this a function with code? If it is external we dont instrument it // anyway and cant be in the the instrument file list. Or if it is ignored. if (!F->size() || isIgnoreFunction(F)) return false; - // if we do not have a the instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { + if (!denyListFiles.empty() || !denyListFunctions.empty()) { - if (!allowListFunctions.empty()) { + if (!denyListFunctions.empty()) { std::string instFunction = F->getName().str(); - for (std::list::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -281,10 +356,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the allow function list, " - "instrumenting ... \n", + "Function %s is in the deny function list, " + "not instrumenting ... \n", instFunction.c_str()); - return true; + return false; } @@ -294,7 +369,7 @@ bool isInInstrumentList(llvm::Function *F) { } - if (!allowListFiles.empty()) { + if (!denyListFiles.empty()) { // let's try to get the filename for the function auto bb = &F->getEntryBlock(); @@ -328,8 +403,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -344,10 +419,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the allowlist (%s), " + "Function %s is in the denylist (%s), not " "instrumenting ... \n", F->getName().str().c_str(), instFilename.str().c_str()); - return true; + return false; } @@ -359,8 +434,6 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #else if (!Loc.isUnknown()) { @@ -373,8 +446,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -387,7 +460,7 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == 0) { - return true; + return false; } @@ -399,34 +472,34 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #endif - else { + else { - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); - } + } - return false; + } } - if (!denyListFiles.empty() || !denyListFunctions.empty()) { + // if we do not have a the instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - if (!denyListFunctions.empty()) { + return_default = false; + + if (!allowListFunctions.empty()) { std::string instFunction = F->getName().str(); - for (std::list::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -440,10 +513,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the deny function list, " - "not instrumenting ... \n", + "Function %s is in the allow function list, " + "instrumenting ... \n", instFunction.c_str()); - return false; + return true; } @@ -453,7 +526,7 @@ bool isInInstrumentList(llvm::Function *F) { } - if (!denyListFiles.empty()) { + if (!allowListFiles.empty()) { // let's try to get the filename for the function auto bb = &F->getEntryBlock(); @@ -487,8 +560,8 @@ bool isInInstrumentList(llvm::Function *F) { /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -503,10 +576,10 @@ bool isInInstrumentList(llvm::Function *F) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the denylist (%s), not " + "Function %s is in the allowlist (%s), " "instrumenting ... \n", F->getName().str().c_str(), instFilename.str().c_str()); - return false; + return true; } @@ -518,22 +591,20 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #else if (!Loc.isUnknown()) { DILocation cDILoc(Loc.getAsMDNode(F->getContext())); unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + StringRef instFilename = cDILoc.getFilename(); (void)instLine; /* Continue only if we know where we actually are */ if (!instFilename.str().empty()) { - for (std::list::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -546,7 +617,7 @@ bool isInInstrumentList(llvm::Function *F) { if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == 0) { - return false; + return true; } @@ -558,27 +629,25 @@ bool isInInstrumentList(llvm::Function *F) { } - } - #endif - else { + else { - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return true; + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; - } + } - return true; + } } - return true; // not reached + return return_default; } diff --git a/llvm_mode/afl-llvm-common.h b/llvm_mode/afl-llvm-common.h index 5b96be43..a1561d9c 100644 --- a/llvm_mode/afl-llvm-common.h +++ b/llvm_mode/afl-llvm-common.h @@ -37,6 +37,7 @@ bool isIgnoreFunction(const llvm::Function *F); void initInstrumentList(); bool isInInstrumentList(llvm::Function *F); unsigned long long int calculateCollisions(uint32_t edges); +void scanForDangerousFunctions(llvm::Module *M); #ifndef IS_EXTERN #define IS_EXTERN diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index fd8e48a7..6bd232ab 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -217,79 +217,9 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ - - std::vector module_block_list; - - if (map_addr) { - - for (GlobalIFunc &IF : M.ifuncs()) { - - StringRef ifunc_name = IF.getName(); - Constant *r = IF.getResolver(); - StringRef r_name = cast(r->getOperand(0))->getName(); - if (!be_quiet) - fprintf(stderr, - "Warning: Found an ifunc with name %s that points to resolver " - "function %s, we cannot instrument this, putting it into a " - "block list.\n", - ifunc_name.str().c_str(), r_name.str().c_str()); - module_block_list.push_back(r_name.str()); - - } - - GlobalVariable *GV = M.getNamedGlobal("llvm.global_ctors"); - if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { - - ConstantArray *InitList = dyn_cast(GV->getInitializer()); - - if (InitList) { - - for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { - - if (ConstantStruct *CS = - dyn_cast(InitList->getOperand(i))) { - - if (CS->getNumOperands() >= 2) { - - if (CS->getOperand(1)->isNullValue()) - break; // Found a null terminator, stop here. - - ConstantInt *CI = dyn_cast(CS->getOperand(0)); - int Priority = CI ? CI->getSExtValue() : 0; - - Constant *FP = CS->getOperand(1); - if (ConstantExpr *CE = dyn_cast(FP)) - if (CE->isCast()) FP = CE->getOperand(0); - if (Function *F = dyn_cast(FP)) { - - if (!F->isDeclaration() && - strncmp(F->getName().str().c_str(), "__afl", 5) != 0 && - Priority <= 5) { - - if (!be_quiet) - fprintf(stderr, - "Warning: Found constructor function %s with prio " - "%u, we cannot instrument this, putting it into a " - "block list.\n", - F->getName().str().c_str(), Priority); - module_block_list.push_back(F->getName().str()); - - } - - } - - } - - } - - } - - } + */ - } - - } + scanForDangerousFunctions(&M); /* Instrument all the things! */ @@ -307,26 +237,6 @@ bool AFLLTOPass::runOnModule(Module &M) { if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; - if (module_block_list.size()) { - - for (auto bname : module_block_list) { - - std::string fname = F.getName().str(); - - if (fname.compare(bname) == 0) { - - if (!be_quiet) - WARNF( - "Skipping instrumentation of dangerous early running function " - "%s", - fname.c_str()); - - } - - } - - } - // the instrument file list check AttributeList Attrs = F.getAttributes(); if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 618abe48..2ea9fd84 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -297,6 +297,7 @@ bool AFLCoverage::runOnModule(Module &M) { /* Instrument all the things! */ int inst_blocks = 0; + scanForDangerousFunctions(&M); for (auto &F : M) { diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index a567593e..dacc46a6 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -842,9 +844,22 @@ void __afl_manual_init(void) { static u8 init_done; + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) { + + init_done = 1; + is_persistent = 0; + __afl_sharedmem_fuzzing = 0; + if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_initial; + + if (getenv("AFL_DEBUG")) + fprintf(stderr, + "DEBUG: disabled instrumenation because of " + "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); + + } + if (!init_done) { - __afl_map_shm(); __afl_start_forkserver(); init_done = 1; @@ -852,11 +867,11 @@ void __afl_manual_init(void) { } -/* Proper initialization routine. */ +/* Initialization of the forkserver - latest possible */ -__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { +__attribute__((constructor())) void __afl_auto_init(void) { - is_persistent = !!getenv(PERSIST_ENV_VAR); + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; if (getenv(DEFER_ENV_VAR)) return; @@ -864,6 +879,18 @@ __attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { } +/* Initialization of the shmem - earliest possible because of LTO fixed mem. */ + +__attribute__((constructor(0))) void __afl_auto_early(void) { + + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; + + is_persistent = !!getenv(PERSIST_ENV_VAR); + + __afl_map_shm(); + +} + /* The following stuff deals with supporting -fsanitize-coverage=trace-pc-guard. It remains non-operational in the traditional, plugin-backed LLVM mode. For more info about 'trace-pc-guard', see llvm_mode/README.md. @@ -912,7 +939,8 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { #else - __afl_area_ptr[*guard] = __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); + __afl_area_ptr[*guard] = + __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); #endif diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 6819fc8a..8684bcc0 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -58,6 +58,8 @@ static list_t fsrv_list = {.element_prealloc_count = 0}; static void fsrv_exec_child(afl_forkserver_t *fsrv, char **argv) { + if (fsrv->qemu_mode) setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0); + execv(fsrv->target_path, argv); } @@ -122,8 +124,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { Returns the time passed to read. If the wait times out, returns timeout_ms + 1; Returns 0 if an error occurred (fd closed, signal, ...); */ -static u32 __attribute__ ((hot)) read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, - volatile u8 *stop_soon_p) { +static u32 __attribute__((hot)) +read_s32_timed(s32 fd, s32 *buf, u32 timeout_ms, volatile u8 *stop_soon_p) { fd_set readfds; FD_ZERO(&readfds); @@ -322,8 +324,9 @@ static void report_error_and_exit(int error) { cloning a stopped child. So, we just execute once, and then send commands through a pipe. The other part of this logic is in afl-as.h / llvm_mode */ -void __attribute__ ((hot)) afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, - volatile u8 *stop_soon_p, u8 debug_child_output) { +void __attribute__((hot)) +afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, + u8 debug_child_output) { int st_pipe[2], ctl_pipe[2]; s32 status; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index f6389c06..1b9df624 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -57,7 +57,7 @@ void write_bitmap(afl_state_t *afl) { This function is called after every exec() on a fairly large buffer, so it needs to be fast. We do this in 32-bit and 64-bit flavors. */ -u8 __attribute__ ((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { +u8 __attribute__((hot)) has_new_bits(afl_state_t *afl, u8 *virgin_map) { #ifdef WORD_SIZE_64 @@ -347,7 +347,7 @@ void init_count_class16(void) { #ifdef WORD_SIZE_64 -void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u64 *mem = (u64 *)fsrv->trace_bits; @@ -376,7 +376,7 @@ void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { #else -void __attribute__ ((hot)) classify_counts(afl_forkserver_t *fsrv) { +void __attribute__((hot)) classify_counts(afl_forkserver_t *fsrv) { u32 *mem = (u32 *)fsrv->trace_bits; @@ -534,7 +534,8 @@ static void write_crash_readme(afl_state_t *afl) { save or queue the input test case for further analysis if so. Returns 1 if entry is saved, 0 otherwise. */ -u8 __attribute__ ((hot)) save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { +u8 __attribute__((hot)) +save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 97fcb3c8..d3f823c9 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -38,8 +38,8 @@ u64 time_spent_working = 0; /* Execute target application, monitoring for timeouts. Return status information. The called program will update afl->fsrv->trace_bits. */ -fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, - u32 timeout) { +fsrv_run_result_t __attribute__((hot)) +fuzz_run_target(afl_state_t *afl, afl_forkserver_t *fsrv, u32 timeout) { #ifdef PROFILING static u64 time_spent_start = 0; @@ -72,7 +72,8 @@ fsrv_run_result_t __attribute__ ((hot)) fuzz_run_target(afl_state_t *afl, afl_fo old file is unlinked and a new one is created. Otherwise, afl->fsrv.out_fd is rewound and truncated. */ -void __attribute__ ((hot)) write_to_testcase(afl_state_t *afl, void *mem, u32 len) { +void __attribute__((hot)) +write_to_testcase(afl_state_t *afl, void *mem, u32 len) { #ifdef _AFL_DOCUMENT_MUTATIONS s32 doc_fd; @@ -858,7 +859,8 @@ abort_trimming: error conditions, returning 1 if it's time to bail out. This is a helper function for fuzz_one(). */ -u8 __attribute__ ((hot)) common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { +u8 __attribute__((hot)) +common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) { u8 fault; -- cgit 1.4.1 From 2c5e103278c3266c15226f097e8e9e15267c57d6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 12:39:18 +0200 Subject: make cmplog deterministic --- docs/Changelog.md | 2 +- src/afl-fuzz-redqueen.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1c5b3f4a..45d640ea 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,7 +19,7 @@ sending a mail to . - eliminated CPU affinity race condition for -S/-M runs - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix - - fixed a bug in redqueen for strings + - fixed a bug in redqueen for strings and made deterministic with -s - llvm_mode: - now supports llvm 12! - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 9716be95..4309098a 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -151,7 +151,7 @@ static u8 colorization(afl_state_t *afl, u8 *buf, u32 len, u64 exec_cksum) { /* Discard if the mutations change the paths or if it is too decremental in speed */ if (cksum != exec_cksum || - (stop_us - start_us > 2 * afl->queue_cur->exec_us)) { + (stop_us - start_us > 2 * afl->queue_cur->exec_us) && likely(!afl->fixed_seed)) { ranges = add_range(ranges, rng->start, rng->start + s / 2); ranges = add_range(ranges, rng->start + s / 2 + 1, rng->end); -- cgit 1.4.1 From 212bb990b7579831baad70735b767dbaf89e9e89 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 21:27:11 +0200 Subject: LTO: apply laf-intel+redqueen/cmplog at link time --- GNUmakefile | 9 ++- README.md | 27 ++++++--- TODO.md | 1 - docs/Changelog.md | 2 + llvm_mode/afl-clang-fast.c | 104 +++++++++++++++++++++++---------- llvm_mode/cmplog-instructions-pass.cc | 4 ++ llvm_mode/cmplog-routines-pass.cc | 4 ++ llvm_mode/compare-transform-pass.so.cc | 3 + llvm_mode/split-compares-pass.so.cc | 4 ++ llvm_mode/split-switches-pass.so.cc | 4 ++ src/afl-showmap.c | 104 ++++++++++++++++++++++++++++----- 11 files changed, 213 insertions(+), 53 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index fe5f8c03..f9020a90 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -97,7 +97,13 @@ ifneq "$(shell uname -m)" "x86_64" endif endif -CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) +ifdef DEBUG + $(info Compiling DEBUG version of binaries) + CFLAGS += -ggdb3 -O0 -Wall -Wextra -Werror +else + CFLAGS ?= -O3 -funroll-loops $(CFLAGS_OPT) +endif + override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \ -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \ -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\" @@ -305,6 +311,7 @@ help: @echo "==========================================" @echo STATIC - compile AFL++ static @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes + @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information @echo AFL_NO_X86 - if compiling on non-intel/amd platforms @echo "==========================================" diff --git a/README.md b/README.md index 18983832..97c0a0d7 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ These build options exist: * STATIC - compile AFL++ static * ASAN_BUILD - compiles with memory sanitizer for debug purposes +* DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) * NO_PYTHON - disable python support * AFL_NO_X86 - if compiling on non-intel/amd platforms @@ -509,8 +510,8 @@ fuzz your target. On the same machine - due to the design of how afl++ works - there is a maximum number of CPU cores/threads that are useful, use more and the overall performance -degrades instead. This value depends on the target and the limit is between 48 -and 96 cores/threads per machine. +degrades instead. This value depends on the target, and the limit is between 32 +and 64 cores/threads per machine. There should be one main fuzzer (`-M main` option) and as many secondary fuzzers (eg `-S variant1`) as you have cores that you use. @@ -562,11 +563,18 @@ To have only the summary use the `-s` switch e.g.: `afl-whatsup -s output/` The `paths found` value is a bad indicator how good the coverage is. A better indicator - if you use default llvm instrumentation with at least -version 9 - to use `afl-showmap` on the target with all inputs of the -queue/ directory one after another and collecting the found edge IDs (`-o N.out`), -removing the counters of the edge IDs, making them unique - and there you have -the total number of found instrumented edges. - +version 9 - is to use `afl-showmap` with the collect coverage option `-C` on +the output directory: +``` +$ afl-showmap -C -i out -o /dev/null -- ./target -params @@ +... +[*] Using SHARED MEMORY FUZZING feature. +[*] Target map size: 9960 +[+] Processed 7849 input files. +[+] Captured 4331 tuples (highest value 255, total values 67130596) in '/dev/nul +l'. +[+] A coverage of 4331 edges were achieved out of 9960 existing (43.48%) with 7849 input files. +``` It is even better to check out the exact lines of code that have been reached - and which have not been found so far. @@ -580,6 +588,11 @@ then terminate it. The main node will pick it up and make it available to the other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no free core. +Note that you in nearly all cases you can never reach full coverage. A lot of +functionality is usually behind options that were not activated or fuzz e.g. +if you fuzz a library to convert image formats and your target is the png to +tiff API then you will not touch any of the other library APIs and features. + #### e) How long to fuzz a target? This is a difficult question. diff --git a/TODO.md b/TODO.md index f8ef0e0f..e74fa1d5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,6 @@ - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores - - feature for afl-showmap to generate the coverage for all queue entries - afl-plot to support multiple plot_data ## Further down the road diff --git a/docs/Changelog.md b/docs/Changelog.md index 45d640ea..2c57448b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,8 @@ sending a mail to . - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) + - LTO: laf-intel and redqueen/cmplogare are now applied at link time + to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index efaba122..10cb3fa3 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -246,33 +246,60 @@ static void edit_params(u32 argc, char **argv, char **envp) { // laf if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + } } if (getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/compare-transform-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/compare-transform-pass.so", obj_path); + + } } if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-compares-pass.so", obj_path); + if (lto_mode) { + + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path); + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-compares-pass.so", obj_path); + + } } @@ -282,24 +309,37 @@ static void edit_params(u32 argc, char **argv, char **envp) { unsetenv("AFL_LD_CALLER"); if (cmplog_mode) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-routines-pass.so", obj_path); + if (lto_mode) { - // reuse split switches from laf - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path); + cc_params[cc_par_cnt++] = alloc_printf( + "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path); - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-routines-pass.so", obj_path); + + // reuse split switches from laf + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/split-switches-pass.so", obj_path); + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/cmplog-instructions-pass.so", obj_path); + + } cc_params[cc_par_cnt++] = "-fno-inline"; @@ -314,6 +354,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD); cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; + /* The current LTO instrim mode is not good, so we disable it if (instrument_mode == INSTRUMENT_CFG) @@ -321,6 +362,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so", obj_path); else */ + cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc index 7c48d906..6ad7dd06 100644 --- a/llvm_mode/cmplog-instructions-pass.cc +++ b/llvm_mode/cmplog-instructions-pass.cc @@ -284,3 +284,7 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPass( static RegisterStandardPasses RegisterCmpLogInstructionsPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass); +static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogInstructionsPass); + diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc index a0f8f64f..7e5a1ca6 100644 --- a/llvm_mode/cmplog-routines-pass.cc +++ b/llvm_mode/cmplog-routines-pass.cc @@ -204,3 +204,7 @@ static RegisterStandardPasses RegisterCmpLogRoutinesPass( static RegisterStandardPasses RegisterCmpLogRoutinesPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass); +static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerCmpLogRoutinesPass); + diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc index bed3597a..83885037 100644 --- a/llvm_mode/compare-transform-pass.so.cc +++ b/llvm_mode/compare-transform-pass.so.cc @@ -585,3 +585,6 @@ static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); +static RegisterStandardPasses RegisterCompTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); + diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 3630bd8c..ef3cf7f6 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -1342,3 +1342,7 @@ static RegisterStandardPasses RegisterSplitComparesPass( static RegisterStandardPasses RegisterSplitComparesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); +static RegisterStandardPasses RegisterSplitComparesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitComparesPass); + diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc index f025df77..3a464391 100644 --- a/llvm_mode/split-switches-pass.so.cc +++ b/llvm_mode/split-switches-pass.so.cc @@ -439,3 +439,7 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass( static RegisterStandardPasses RegisterSplitSwitchesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); +static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + registerSplitSwitchesTransPass); + diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0aa116e5..fa9eedc4 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -68,9 +68,11 @@ static char *stdin_file; /* stdin file */ static u8 *in_dir = NULL, /* input folder */ *out_file = NULL, *at_file = NULL; /* Substitution string for @@ */ -static u8 *in_data; /* Input data */ +static u8 *in_data, /* Input data */ + *coverage_map; /* Coverage map */ -static u32 total, highest; /* tuple content information */ +static u64 total; /* tuple content information */ +static u32 tcnt, highest; /* tuple content information */ static u32 in_len, /* Input data length */ arg_offset; /* Total number of execs */ @@ -83,7 +85,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ cmin_mode, /* Generate output in afl-cmin mode? */ binary_mode, /* Write output as a binary map */ keep_cores, /* Allow coredumps? */ - remove_shm = 1; /* remove shmem? */ + remove_shm = 1, /* remove shmem? */ + collect_coverage; /* collect coverage */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -175,6 +178,25 @@ static void at_exit_handler(void) { } +/* Analyze results. */ + +static void analyze_results(afl_forkserver_t *fsrv) { + + u32 i; + for (i = 0; i < map_size; i++) { + + if (fsrv->trace_bits[i]) { + + total += fsrv->trace_bits[i]; + if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i]; + if (!coverage_map[i]) { coverage_map[i] = 1; } + + } + + } + +} + /* Write results. */ static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) { @@ -588,9 +610,14 @@ static void usage(u8 *argv0) { " (Not necessary, here for consistency with other afl-* " "tools)\n\n" "Other settings:\n" - " -i dir - process all files in this directory, -o must be a " + " -i dir - process all files in this directory, must be combined " + "with -o.\n" + " With -C, -o is a file, without -C it must be a " "directory\n" " and each bitmap will be written there individually.\n" + " -C - collect coverage, writes all edges to -o and gives a " + "summary\n" + " Must be combined with -i.\n" " -q - sink program's output and don't show messages\n" " -e - show edge coverage only, ignore hit counts\n" " -r - show real tuple values instead of AFL filter values\n" @@ -624,7 +651,6 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - u32 tcnt = 0; char **use_argv; char **argv = argv_cpy_dup(argc, argv_orig); @@ -639,10 +665,14 @@ int main(int argc, char **argv_orig, char **envp) { if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; } - while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) { + while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) { switch (opt) { + case 'C': + collect_coverage = 1; + break; + case 'i': if (in_dir) { FATAL("Multiple -i options not supported"); } in_dir = optarg; @@ -820,6 +850,13 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !out_file) { usage(argv[0]); } + if (in_dir) { + + if (!out_file && !collect_coverage) + FATAL("for -i you need to specify either -C and/or -o"); + + } + if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } @@ -910,7 +947,7 @@ int main(int argc, char **argv_orig, char **envp) { if (in_dir) { - DIR * dir_in, *dir_out; + DIR * dir_in, *dir_out = NULL; struct dirent *dir_ent; int done = 0; u8 infile[PATH_MAX], outfile[PATH_MAX]; @@ -924,20 +961,43 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->dev_null_fd = open("/dev/null", O_RDWR); if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + // if a queue subdirectory exists switch to that + u8 *dn = alloc_printf("%s/queue", in_dir); + if ((dir_in = opendir(in_dir))) { + + closedir(dir_in); + in_dir = dn; + + } else + + ck_free(dn); + if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir); + if (!(dir_in = opendir(in_dir))) { PFATAL("cannot open directory %s", in_dir); } - if (!(dir_out = opendir(out_file))) { + if (!collect_coverage) { + + if (!(dir_out = opendir(out_file))) { + + if (mkdir(out_file, 0700)) { - if (mkdir(out_file, 0700)) { + PFATAL("cannot create output directory %s", out_file); - PFATAL("cannot create output directory %s", out_file); + } } + } else { + + if ((coverage_map = (u8 *)malloc(map_size)) == NULL) + FATAL("coult not grab memory"); + edges_only = 0; + raw_instr_output = 1; + } u8 *use_dir = "."; @@ -978,6 +1038,7 @@ int main(int argc, char **argv_orig, char **envp) { afl_fsrv_start(fsrv, use_argv, &stop_soon, get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); @@ -1005,7 +1066,8 @@ int main(int argc, char **argv_orig, char **envp) { if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue; #endif - snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); + if (!collect_coverage) + snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name); if (read_file(infile)) { @@ -1019,7 +1081,10 @@ int main(int argc, char **argv_orig, char **envp) { showmap_run_target_forkserver(fsrv, in_data, in_len); ck_free(in_data); - tcnt = write_results_to_file(fsrv, outfile); + if (collect_coverage) + analyze_results(fsrv); + else + tcnt = write_results_to_file(fsrv, outfile); } @@ -1030,6 +1095,13 @@ int main(int argc, char **argv_orig, char **envp) { closedir(dir_in); if (dir_out) { closedir(dir_out); } + if (collect_coverage) { + + memcpy(fsrv->trace_bits, coverage_map, map_size); + tcnt = write_results_to_file(fsrv, out_file); + + } + } else { if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) @@ -1043,8 +1115,14 @@ int main(int argc, char **argv_orig, char **envp) { if (!quiet_mode) { if (!tcnt) { FATAL("No instrumentation detected" cRST); } - OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST, + OKF("Captured %u tuples (highest value %u, total values %llu) in " + "'%s'." cRST, tcnt, highest, total, out_file); + if (collect_coverage) + OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) " + "with %llu input files.", + tcnt, map_size, ((float)tcnt * 100) / (float)map_size, + fsrv->total_execs); } -- cgit 1.4.1 From b5d1a021efaede5e084418fe552330590ee43641 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 13 Aug 2020 22:34:11 +0200 Subject: fix llvm and afl-showmap --- docs/Changelog.md | 1 + llvm_mode/LLVMInsTrim.so.cc | 2 +- llvm_mode/afl-clang-fast.c | 4 ++++ llvm_mode/afl-llvm-common.cc | 4 ++++ llvm_mode/afl-llvm-pass.so.cc | 2 +- src/afl-showmap.c | 2 +- 6 files changed, 12 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 2c57448b..5044dce5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -29,6 +29,7 @@ sending a mail to . - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - fixes for llvm 4.0 - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 2ad7f171..9812b804 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -94,7 +94,7 @@ struct InsTrim : public ModulePass { } -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 10cb3fa3..0597ba17 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -371,8 +371,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { +#if LLVM_VERSION_MAJOR >= 4 cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default +#else + FATAL("pcguard instrumentation requires llvm 4.0.1+"); +#endif } else { diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 4b864cf7..4a94ae89 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -260,6 +260,8 @@ void scanForDangerousFunctions(llvm::Module *M) { if (!M) return; +#if LLVM_VERSION_MAJOR >= 4 + for (GlobalIFunc &IF : M->ifuncs()) { StringRef ifunc_name = IF.getName(); @@ -325,6 +327,8 @@ void scanForDangerousFunctions(llvm::Module *M) { } +#endif + } static std::string getSourceName(llvm::Function *F) { diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 2ea9fd84..92823187 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -112,7 +112,7 @@ uint64_t PowerOf2Ceil(unsigned in) { #endif /* #if LLVM_VERSION_STRING >= "4.0.1" */ -#if LLVM_VERSION_MAJOR >= 4 || \ +#if LLVM_VERSION_MAJOR > 4 || \ (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif diff --git a/src/afl-showmap.c b/src/afl-showmap.c index fa9eedc4..47c615d8 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -963,7 +963,7 @@ int main(int argc, char **argv_orig, char **envp) { // if a queue subdirectory exists switch to that u8 *dn = alloc_printf("%s/queue", in_dir); - if ((dir_in = opendir(in_dir))) { + if ((dir_in = opendir(dn)) != NULL) { closedir(dir_in); in_dir = dn; -- cgit 1.4.1 From d0ab83a202ef5bb0614dde01825e4dd48f0da645 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Thu, 13 Aug 2020 23:19:05 +0200 Subject: Changelog wording/whitespace --- docs/Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 5044dce5..3c28ff98 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -33,7 +33,7 @@ sending a mail to . - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) - - LTO: laf-intel and redqueen/cmplogare are now applied at link time + - LTO: laf-intel and redqueen/cmplog are now applied at link time to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used -- cgit 1.4.1 From e94cc1fae0e78cd6c2e7e3cad737ad039148888f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 14 Aug 2020 07:09:01 +0200 Subject: new faq entries --- docs/FAQ.md | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index e690635a..997f4c40 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -3,9 +3,11 @@ ## Contents 1. [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - 2. [What is an edge?](#what-is-an-edge) - 3. [Why is my stability below 100%?](#why-is-my-stability-below-100) - 4. [How can I improve the stability value](#how-can-i-improve-the-stability-value) + 2. [How do I fuzz a network service?](#how-to-fuzz-a-network-service) + 3. [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) + 4. [What is an edge?](#what-is-an-edge) + 5. [Why is my stability below 100%?](#why-is-my-stability-below-100) + 6. [How can I improve the stability value](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) @@ -20,6 +22,43 @@ If you find an interesting or important question missing, submit it via 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) +## How do I fuzz a network service? + +The short answer is - you cannot, at least "out of the box". + +Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, +and finally usually it is more than one initial data packet but a back-and-forth +which is totally unsupported by most coverage aware fuzzers. + +The established method to fuzz network services is to modify the source code +to read from a file or stdin (fd 0) (or even faster via shared memory, combine +this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) +and you have a performance gain of x10 instead of a performance loss of over +x10 - that is a x100 difference! + +If modifying the source is not an option (e.g. because you only have a binary +and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD +to emulate the network. This is also much faster than network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/) + +There is an outdated afl++ branch that implements networking if you are +desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - +however a better option is AFLnet ([https://github.com/aflnet/aflnet](https://github.com/aflnet/aflnet)) +which allows you to define network state with different type of data packets. + +## How do I fuzz a GUI program? + +If the GUI program can read the fuzz data from a file (via the command line, +a fixed location or via an environment variable) without needing any user +interaction then then yes. + +Otherwise it is not possible without modifying the source code - which is a +very good idea anyway as the GUI functionality is a huge CPU/time overhead +for the fuzzing. + +So create a new `main()` that just reads the test case and calls the +functionality for processing the input that the GUI program is using. + ## What is an "edge" A program contains `functions`, `functions` contain the compiled machine code. -- cgit 1.4.1 From 1cf473848789b340134b68b02d793c6d90f746a8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 15 Aug 2020 10:27:40 +0200 Subject: more FAQ --- docs/FAQ.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 13 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index 997f4c40..93a87a72 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,16 +2,39 @@ ## Contents - 1. [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - 2. [How do I fuzz a network service?](#how-to-fuzz-a-network-service) - 3. [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) - 4. [What is an edge?](#what-is-an-edge) - 5. [Why is my stability below 100%?](#why-is-my-stability-below-100) - 6. [How can I improve the stability value](#how-can-i-improve-the-stability-value) + * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) + * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) + * [How do I fuzz a network service?](#how-to-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) + * [What is an edge?](#what-is-an-edge) + * [Why is my stability below 100%?](#why-is-my-stability-below-100) + * [How can I improve the stability value](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) +## What is the difference between afl and afl++? + +American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in +2013/2014, and when he left Google end of 2017 he stopped developing it. + +At the end of 2019 the Google fuzzing team took over maintance of AFL, however +it is only accepting PR from the community and is not developing enhancements +anymore. + +In the second quarter of 2019, 1 1/2 years after no further development of +AFL had happened and it became clear there would be none coming, afl++ +was born, where initially first community patches were collected and applied +for bugs and enhancements. Then from various AFL spin-offs - mostly academic +research - features were integrated. This already resulted in a much advanced +AFL. + +Until the end of 2019 the afl++ team had grown to four active developers which +then implemented their own research and feature, making it now by far the most +flexible and feature rich guided fuzzer available as open source. +And in independent fuzzing benchmarks it is one of the best fuzzers available, +e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) + ## How to improve the fuzzing speed 1. use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) @@ -101,15 +124,15 @@ code example above): ``` Every line between two blocks is an `edge`. -## Why is my stability below 100 +## Why is my stability below 100% Stability is measured by how many percent of the edges in the target are "stable". Sending the same input again and again should take the exact same path through the target every time. If that is the case, the stability is 100%. -If however randomness happens, e.g. a thread reading from shared memory, +If however randomness happens, e.g. a thread reading other external data, reaction to timing, etc. then in some of the re-executions with the same data -will result in the edge information being different accross runs. +the result in the edge information will be different accross runs. Those edges that change are then flagged "unstable". The more "unstable" edges, the more difficult for afl++ to identify valid new @@ -122,9 +145,25 @@ improve the stability. ## How can I improve the stability value +For fuzzing a 100% stable target that covers all edges is the best. +A 90% stable target that covers all edges is however better than a 100% stable +target that ignores 10% of the edges. + +With instability you basically have a partial coverage loss on an edge, with +ignore you have a full loss on that edge. + +There are functions that are unstable, but also provide value to coverage, eg +init functions that use fuzz data as input for example. +If however it is a function that has nothing to do with the input data is the +source, e.g. checking jitter, or is a hash map function etc. then it should +not be instrumented. + +To be able to make this decision the following process will allow you to +identify the functions with variable edges so you can make this decision. + Four steps are required to do this and requires quite some knowledge of coding and/or disassembly and it is only effectively possible with -afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! +afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -135,9 +174,9 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! 2. Second step: Find the responsible function. a) For LTO instrumented binaries this can be documented during compile - time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/afile`. - This file will have one assigned edge ID and the corresponding function - per line. + time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`. + This file will have one assigned edge ID and the corresponding + function per line. b) For PCGUARD instrumented binaries it is much more difficult. Here you can either modify the __sanitizer_cov_trace_pc_guard function in @@ -151,6 +190,10 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! on start, check to which memory address the edge ID value is written and set a write breakpoint to that address (`watch 0x.....`). + c) in all other instrumentation types this is not possible. So just + recompile with the the two mentioned above. This is just for + identifying the functions that have unstable edges. + 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to @@ -161,6 +204,15 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation! If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) + Only deny those functions from instrumentation that provide no value + for coverage - that is if it does not process any fuzz data directly + or indirectly (e.g. hash maps, thread management etc.). + If however a function directly or indirectly handles fuzz data then you + should not put the function in a deny instrumentation list and rather + live with the instability it comes with. + 4. Fourth step: recompile the target Recompile, fuzz it, be happy :) + + This link explains this process for [Fuzzbench](https://github.com/google/fuzzbench/issues/677) -- cgit 1.4.1 From 0a251f93e0842c92755e9bcba61e520669a6c2e6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 15 Aug 2020 13:34:51 +0200 Subject: increase initial memory sized --- docs/Changelog.md | 2 ++ examples/aflpp_driver/aflpp_driver.c | 1 - include/config.h | 10 +++++----- llvm_mode/afl-llvm-rt.o.c | 12 ++++-------- 4 files changed, 11 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 3c28ff98..ea7c7caf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,6 +12,8 @@ sending a mail to . ### Version ++2.66d (devel) - Support for improved afl++ snapshot module: https://github.com/AFLplusplus/AFL-Snapshot-LKM + - Due to the instrumentation needing more memory, the initial memory sizes + for -m have been increased - afl-fuzz: - added -F option to allow -M main fuzzers to sync to foreign fuzzers, e.g. honggfuzz or libfuzzer diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index b764338e..ff5446e9 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -109,7 +109,6 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. int __afl_sharedmem_fuzzing = 1; extern unsigned int * __afl_fuzz_len; extern unsigned char *__afl_fuzz_ptr; -// extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); diff --git a/include/config.h b/include/config.h index 344a368f..a978a27c 100644 --- a/include/config.h +++ b/include/config.h @@ -70,21 +70,21 @@ #ifndef __NetBSD__ #ifndef WORD_SIZE_64 - #define MEM_LIMIT 25 - #else #define MEM_LIMIT 50 + #else + #define MEM_LIMIT 75 #endif /* ^!WORD_SIZE_64 */ #else /* NetBSD's kernel needs more space for stack, see discussion for issue \ #165 */ - #define MEM_LIMIT 200 + #define MEM_LIMIT 250 #endif /* Default memory limit when running in QEMU mode (MB): */ -#define MEM_LIMIT_QEMU 200 +#define MEM_LIMIT_QEMU 250 /* Default memory limit when running in Unicorn mode (MB): */ -#define MEM_LIMIT_UNICORN 200 +#define MEM_LIMIT_UNICORN 250 /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 5479c3da..a56b54b2 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -902,15 +902,11 @@ __attribute__((constructor(0))) void __afl_auto_first(void) { if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; u8 *ptr; + u32 get_size = __afl_final_loc ? __afl_final_loc : 1024000; - if (__afl_final_loc > MAP_INITIAL_SIZE) { - - ptr = (u8 *)mmap(NULL, __afl_final_loc, PROT_READ | PROT_WRITE, MAP_PRIVATE, - -1, 0); - - if (ptr && (ssize_t)ptr != -1) { __afl_area_ptr = ptr; } - - } + ptr = (u8 *)mmap(NULL, __afl_final_loc, PROT_READ | PROT_WRITE, MAP_PRIVATE, + -1, 0); + if (ptr && (ssize_t)ptr != -1) { __afl_area_ptr = ptr; } } -- cgit 1.4.1 From cc1fe2f2d29b4b306c1558189251adae781dbb70 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 15 Aug 2020 22:59:49 +0200 Subject: skip instrumenting blocks following __afl_loop to improve stability in LTO. not in afl-llvm-pass and instrim because they are outdated, sancov cant be fixed --- docs/Changelog.md | 2 ++ llvm_mode/afl-llvm-lto-instrumentation.so.cc | 41 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ea7c7caf..ead4ff26 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,8 @@ sending a mail to . - skipping ctors and ifuncs for instrumentation - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) + - LTO: improved stability for persistent mode, no other instrumentation + has that advantage - LTO: laf-intel and redqueen/cmplog are now applied at link time to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index a4caf77b..2b99d4c6 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -103,6 +103,7 @@ bool AFLLTOPass::runOnModule(Module &M) { std::vector dictionary; std::vector calls; DenseMap valueMap; + std::vector BlockList; char * ptr; FILE * documentFile = NULL; @@ -310,6 +311,24 @@ bool AFLLTOPass::runOnModule(Module &M) { isStrncasecmp &= !FuncName.compare("strncasecmp"); isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + /* we do something different here, putting this BB and the + successors in a block map */ + if (!FuncName.compare("__afl_persistent_loop")) { + + BlockList.push_back(&BB); + /* + for (succ_iterator SI = succ_begin(&BB), SE = + succ_end(&BB); SI != SE; ++SI) { + + BasicBlock *succ = *SI; + BlockList.push_back(succ); + + } + + */ + + } + if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && !isStrncasecmp && !isIntMemcpy) continue; @@ -603,6 +622,28 @@ bool AFLLTOPass::runOnModule(Module &M) { uint32_t fs = origBB->getParent()->size(); uint32_t countto; + if (BlockList.size()) { + + int skip = 0; + for (uint32_t k = 0; k < BlockList.size(); k++) { + + if (origBB == BlockList[k]) { + + if (debug) + fprintf( + stderr, + "DEBUG: Function %s skipping BB with/after __afl_loop\n", + F.getName().str().c_str()); + skip = 1; + + } + + } + + if (skip) continue; + + } + for (succ_iterator SI = succ_begin(origBB), SE = succ_end(origBB); SI != SE; ++SI) { -- cgit 1.4.1 From 1d56de6c1d24e6ed24bf7193df18110da753c6b2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 16 Aug 2020 13:29:24 +0200 Subject: fix lto autodict for long strings --- docs/Changelog.md | 1 + llvm_mode/afl-llvm-lto-instrumentation.so.cc | 33 ++++++++++++++++++---------- llvm_mode/afl-llvm-rt.o.c | 5 +++-- 3 files changed, 25 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ead4ff26..55b0c7dd 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,7 @@ sending a mail to . for a fixed map address (eg. 0x10000) - LTO: improved stability for persistent mode, no other instrumentation has that advantage + - LTO: fixed autodict for long strings - LTO: laf-intel and redqueen/cmplog are now applied at link time to prevent llvm optimizing away the splits - LTO: autodictionary mode is a default diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 2b99d4c6..5320df09 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -291,14 +291,14 @@ bool AFLLTOPass::runOnModule(Module &M) { if ((callInst = dyn_cast(&IN))) { - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool addedNull = false; - uint8_t optLen = 0; + bool isStrcmp = true; + bool isMemcmp = true; + bool isStrncmp = true; + bool isStrcasecmp = true; + bool isStrncasecmp = true; + bool isIntMemcpy = true; + bool addedNull = false; + size_t optLen = 0; Function *Callee = callInst->getCalledFunction(); if (!Callee) continue; @@ -546,17 +546,26 @@ bool AFLLTOPass::runOnModule(Module &M) { // add null byte if this is a string compare function and a null // was not already added - if (addedNull == false && !isMemcmp) { + if (!isMemcmp) { - thestring.append("\0", 1); // add null byte - optLen++; + if (addedNull == false) { + + thestring.append("\0", 1); // add null byte + optLen++; + + } + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); } if (!be_quiet) { std::string outstring; - fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen, + fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, (unsigned int)thestring.length()); for (uint8_t i = 0; i < thestring.length(); i++) { diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index e5ff7b19..d00fd26f 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -329,8 +329,9 @@ static void __afl_map_shm(void) { __afl_area_ptr[0] = 1; - } else if (__afl_map_addr && - (!__afl_area_ptr || __afl_area_ptr == __afl_area_initial)) { + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { __afl_area_ptr = mmap((void *)__afl_map_addr, __afl_map_size, PROT_READ | PROT_WRITE, -- cgit 1.4.1 From b504b9313a47c597296a2dab4fd8a591d93242b4 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 18 Aug 2020 01:36:49 +0200 Subject: code-format, changelog --- docs/Changelog.md | 1 + src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 26 ++++++++++---------------- src/afl-fuzz-state.c | 2 +- 4 files changed, 13 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 55b0c7dd..d9c2a9c0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,7 @@ sending a mail to . - expanded havoc mode added, on no cycle finds add extra splicing and MOpt into the mix - fixed a bug in redqueen for strings and made deterministic with -s + - Compiletime autodictionary fixes - llvm_mode: - now supports llvm 12! - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 85450e4a..8277116b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -716,7 +716,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, (u8)dict[offset] + offset < (u32)status) { fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, - (u8)dict[offset]); + (u8)dict[offset]); offset += (1 + dict[offset]); count++; diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 77a6c05e..094c30b9 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -227,12 +227,12 @@ void load_extras_file(afl_state_t *afl, u8 *fname, u32 *min_len, u32 *max_len, } -static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 *dir) { +static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, + u8 *dir) { u8 val_bufs[2][STRINGIFY_VAL_SIZE_MAX]; - if (!afl->extras_cnt) { - FATAL("No usable files in '%s'", dir); } + if (!afl->extras_cnt) { FATAL("No usable files in '%s'", dir); } qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); @@ -257,8 +257,6 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, u8 } - - /* Read extras from the extras directory and sort them by size. */ void load_extras(afl_state_t *afl, u8 *dir) { @@ -379,22 +377,18 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (len > MAX_DICT_FILE) { - FATAL( - "Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, - stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), - stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); + FATAL("Extra '%.*s' is too big (%s, limit is %s)", (int)len, mem, + stringify_mem_size(val_bufs[0], sizeof(val_bufs[0]), len), + stringify_mem_size(val_bufs[1], sizeof(val_bufs[1]), MAX_DICT_FILE)); } else if (len > 32) { - WARNF( - "Extra '%.*s' is pretty large, consider trimming.", (int)len, mem - ); + WARNF("Extra '%.*s' is pretty large, consider trimming.", (int)len, mem); } - afl->extras = - afl_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); if (unlikely(!afl->extras)) { PFATAL("alloc"); } afl->extras[afl->extras_cnt].data = ck_alloc(len); @@ -410,7 +404,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { /* We only want to print this once */ if (afl->extras_cnt == MAX_DET_EXTRAS + 1) { - + WARNF("More than %d tokens - will use them probabilistically.", MAX_DET_EXTRAS); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 7e9f15b7..dd0e316c 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -114,7 +114,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->fsrv.map_size = map_size; // afl_state_t is not available in forkserver.c afl->fsrv.afl_ptr = (void *)afl; - afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) &add_extra; + afl->fsrv.add_extra_func = (void (*)(void *, u8 *, u32)) & add_extra; afl->cal_cycles = CAL_CYCLES; afl->cal_cycles_long = CAL_CYCLES_LONG; -- cgit 1.4.1 From 1c64048d0f3a7bb7ec460d86a6abba7c4123d18f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 18 Aug 2020 19:56:47 +0200 Subject: 2.67c --- README.md | 4 ++-- docs/Changelog.md | 8 ++++---- include/config.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 97c0a0d7..b7b699cf 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ![Travis State](https://api.travis-ci.com/AFLplusplus/AFLplusplus.svg?branch=stable) - Release Version: [2.66c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 2.66d + Github Version: 2.67d Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index 55b0c7dd..f562b727 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++2.66d (devel) +### Version ++2.67c (release) - Support for improved afl++ snapshot module: https://github.com/AFLplusplus/AFL-Snapshot-LKM - Due to the instrumentation needing more memory, the initial memory sizes @@ -23,11 +23,11 @@ sending a mail to . MOpt into the mix - fixed a bug in redqueen for strings and made deterministic with -s - llvm_mode: - - now supports llvm 12! + - now supports llvm 12 - support for AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST (previous AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm - sancov, and also supports function matching! + sancov, and also supports function matching :) - added neverzero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) @@ -40,7 +40,7 @@ sending a mail to . - LTO: fixed autodict for long strings - LTO: laf-intel and redqueen/cmplog are now applied at link time to prevent llvm optimizing away the splits - - LTO: autodictionary mode is a default + - LTO: autodictionary mode is a fixed default now - LTO: instrim instrumentation disabled, only classic support used as it is always better - LTO: env var AFL_LLVM_DOCUMENT_IDS=file will document which edge ID diff --git a/include/config.h b/include/config.h index a978a27c..9cc2dae5 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.66d" +#define VERSION "++2.67c" /****************************************************** * * -- cgit 1.4.1 From 9faf7b6fc81cf88aed43c7d4955bc05ee74a8a54 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 18 Aug 2020 20:08:29 +0200 Subject: v2.67d --- docs/Changelog.md | 4 ++++ include/config.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index b80d4047..15dccd2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,10 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++2.67d (develop) + - Fix for auto dictionary not to throw out a -x dictionary + + ### Version ++2.67c (release) - Support for improved afl++ snapshot module: https://github.com/AFLplusplus/AFL-Snapshot-LKM diff --git a/include/config.h b/include/config.h index 9cc2dae5..6c73bced 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.67c" +#define VERSION "++2.67d" /****************************************************** * * -- cgit 1.4.1 From 779d8f6b7e3454fcfd7a43c4cf54d72ea025e67d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 20 Aug 2020 10:56:51 +0200 Subject: support current llvm12 changes --- docs/Changelog.md | 1 + llvm_mode/LLVMInsTrim.so.cc | 10 ++++++++-- llvm_mode/afl-clang-fast.c | 3 +++ llvm_mode/afl-llvm-pass.so.cc | 10 ++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 15dccd2c..efb5ed0b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++2.67d (develop) + - Further llvm 12 support (fast moving target like afl++ :-) ) - Fix for auto dictionary not to throw out a -x dictionary diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 206e2682..6b2aaf23 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -181,10 +181,16 @@ struct InsTrim : public ModulePass { #ifdef AFL_HAVE_VECTOR_INTRINSICS // IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); IntegerType *IntLocTy = IntegerType::getIntNTy(C, sizeof(PREV_LOC_T) * CHAR_BIT); - if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); #endif /* Get globals for the SHM region and the previous location. Note that diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 99b17430..72b81922 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -978,8 +978,11 @@ int main(int argc, char **argv, char **envp) { "AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" "AFL_HARDEN: adds code hardening to catch memory bugs\n" "AFL_INST_RATIO: percentage of branches to instrument\n" +#if LLVM_VERSION_MAJOR < 9 "AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#else "AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#endif "AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" "AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" "AFL_LLVM_LAF_SPLIT_SWITCHES: casc. comp. in 'switch'\n" diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index a791d720..0206080f 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -225,8 +225,14 @@ bool AFLCoverage::runOnModule(Module &M) { PrevLocSize = 1; #ifdef AFL_HAVE_VECTOR_INTRINSICS - uint64_t PrevLocVecSize = PowerOf2Ceil(PrevLocSize); - if (ngram_size) PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize); + int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); + if (ngram_size) + PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize + #if LLVM_VERSION_MAJOR >= 12 + , + false + #endif + ); #endif /* Get globals for the SHM region and the previous location. Note that -- cgit 1.4.1 From 4ce5ed370a1c18c8c7d7f6ad4fa029a34e1326e4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 20 Aug 2020 18:57:05 +0200 Subject: LTO: sancov made default, deprecated SKIPSINGLEBLOCK, deactivate LTO autodict for cmplog binaries --- docs/Changelog.md | 10 +- llvm_mode/GNUmakefile | 9 +- llvm_mode/LLVMInsTrim.so.cc | 4 - llvm_mode/SanitizerCoverageLTO.so.cc | 2 + llvm_mode/afl-clang-fast.c | 15 +- llvm_mode/afl-llvm-lto-instrim.so.cc | 951 --------------------------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 4 +- llvm_mode/afl-llvm-pass.so.cc | 4 - 8 files changed, 22 insertions(+), 977 deletions(-) delete mode 100644 llvm_mode/afl-llvm-lto-instrim.so.cc (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index efb5ed0b..d00d59d7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,8 +10,14 @@ sending a mail to . ### Version ++2.67d (develop) - - Further llvm 12 support (fast moving target like afl++ :-) ) - - Fix for auto dictionary not to throw out a -x dictionary + - afl-fuzz: + - Fix for auto dictionary entries found during fuzzing to not throw out + a -x dictionary + - llvm_mode: + - Ported SanCov to LTO, and made it the default for LTO. better + instrumentation locations + - Further llvm 12 support (fast moving target like afl++ :-) ) + - deprecated LLVM SKIPSINGLEBLOCK env environment ### Version ++2.67c (release) diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index 72295bc8..5d938469 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -274,7 +274,7 @@ ifeq "$(TEST_MMAP)" "1" LDFLAGS += -Wno-deprecated-declarations endif - PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.so ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../libLLVMInsTrim.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 ../cmplog-routines-pass.so ../cmplog-instructions-pass.so ../SanitizerCoverageLTO.so + PROGS = ../afl-clang-fast ../afl-llvm-pass.so ../afl-ld-lto ../afl-llvm-lto-instrumentlist.so ../afl-llvm-lto-instrumentation.so ../libLLVMInsTrim.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 ../cmplog-routines-pass.so ../cmplog-instructions-pass.so ../SanitizerCoverageLTO.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -374,11 +374,6 @@ ifeq "$(LLVM_LTO)" "1" @$(CLANG_BIN) $(CFLAGS_SAFE) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c afl-llvm-rt-lto.o.c -o ../afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi endif -../afl-llvm-lto-instrim.so: afl-llvm-lto-instrim.so.cc afl-llvm-common.o -ifeq "$(LLVM_LTO)" "1" - $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< MarkNodes.cc -o $@ $(CLANG_LFL) afl-llvm-common.o -endif - # laf ../split-switches-pass.so: split-switches-pass.so.cc afl-llvm-common.o | test_deps $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o @@ -427,7 +422,7 @@ all_done: test_build install: all install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) if [ -f ../afl-clang-fast -a -f ../libLLVMInsTrim.so -a -f ../afl-llvm-rt.o ]; then set -e; install -m 755 ../afl-clang-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-fast++; install -m 755 ../libLLVMInsTrim.so ../afl-llvm-pass.so ../afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH); fi - if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-lto-instrim.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi + if [ -f ../afl-clang-lto ]; then set -e; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ../afl-llvm-lto-instrumentation.so ../afl-llvm-rt-lto*.o ../afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-ld-lto ]; then set -e; install -m 755 ../afl-ld-lto $${DESTDIR}$(BIN_PATH); fi if [ -f ../afl-llvm-rt-32.o ]; then set -e; install -m 755 ../afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH); fi if [ -f ../afl-llvm-rt-64.o ]; then set -e; install -m 755 ../afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH); fi diff --git a/llvm_mode/LLVMInsTrim.so.cc b/llvm_mode/LLVMInsTrim.so.cc index 6b2aaf23..775e9591 100644 --- a/llvm_mode/LLVMInsTrim.so.cc +++ b/llvm_mode/LLVMInsTrim.so.cc @@ -132,10 +132,6 @@ struct InsTrim : public ModulePass { } - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || - getenv("AFL_LLVM_SKIPSINGLEBLOCK")) - function_minimum_size = 2; - unsigned int PrevLocSize = 0; char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); diff --git a/llvm_mode/SanitizerCoverageLTO.so.cc b/llvm_mode/SanitizerCoverageLTO.so.cc index 412582fa..95834a36 100644 --- a/llvm_mode/SanitizerCoverageLTO.so.cc +++ b/llvm_mode/SanitizerCoverageLTO.so.cc @@ -425,6 +425,8 @@ bool ModuleSanitizerCoverage::instrumentModule( if ((afl_global_id = atoi(ptr)) < 0) FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is negative\n", ptr); + if (getenv("AFL_LLVM_CMPLOG")) autodictionary = 0; + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { if ((documentFile = fopen(ptr, "a")) == NULL) diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 70d7181a..cafc9265 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -347,11 +347,6 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode) { - if (cmplog_mode) - unsetenv("AFL_LLVM_LTO_AUTODICTIONARY"); - else - setenv("AFL_LLVM_LTO_AUTODICTIONARY", "1", 1); - #if defined(AFL_CLANG_LDPATH) && LLVM_VERSION_MAJOR >= 12 u8 *ld_ptr = strrchr(AFL_REAL_LD, '/'); if (!ld_ptr) ld_ptr = "ld.lld"; @@ -753,7 +748,13 @@ int main(int argc, char **argv, char **envp) { if (strncasecmp(ptr, "afl", strlen("afl")) == 0 || strncasecmp(ptr, "classic", strlen("classic")) == 0) { - if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) + if (instrument_mode == INSTRUMENT_LTO) { + + instrument_mode = INSTRUMENT_CLASSIC; + lto_mode = 1; + + } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) + instrument_mode = INSTRUMENT_AFL; else FATAL("main instrumentation mode already set with %s", @@ -845,7 +846,7 @@ int main(int argc, char **argv, char **envp) { callname = "afl-clang-lto"; if (!instrument_mode) { - instrument_mode = INSTRUMENT_LTO; + instrument_mode = INSTRUMENT_CFG; ptr = instrument_mode_string[instrument_mode]; } diff --git a/llvm_mode/afl-llvm-lto-instrim.so.cc b/llvm_mode/afl-llvm-lto-instrim.so.cc deleted file mode 100644 index 98e9ff9a..00000000 --- a/llvm_mode/afl-llvm-lto-instrim.so.cc +++ /dev/null @@ -1,951 +0,0 @@ -/* - american fuzzy lop++ - LLVM-mode instrumentation pass - --------------------------------------------------- - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-fast - or afl-clang-lto with AFL_LLVM_INSTRUMENT=CFG or =INSTRIM - - */ - -#define AFL_LLVM_PASS - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "llvm/Config/llvm-config.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Analysis/LoopInfo.h" -#include "llvm/Analysis/MemorySSAUpdater.h" -#include "llvm/Analysis/ValueTracking.h" - -#include "MarkNodes.h" -#include "afl-llvm-common.h" - -#include "config.h" -#include "debug.h" - -using namespace llvm; - -static cl::opt MarkSetOpt("markset", cl::desc("MarkSet"), - cl::init(false)); -static cl::opt LoopHeadOpt("loophead", cl::desc("LoopHead"), - cl::init(false)); - -namespace { - -struct InsTrimLTO : public ModulePass { - - protected: - uint32_t function_minimum_size = 1; - char * skip_nozero = NULL; - int afl_global_id = 1, autodictionary = 1; - uint32_t inst_blocks = 0, inst_funcs = 0; - uint64_t map_addr = 0x10000; - - public: - static char ID; - - InsTrimLTO() : ModulePass(ID) { - - char *ptr; - - if (getenv("AFL_DEBUG")) debug = 1; - if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) - if ((afl_global_id = atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) - FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %d\n", - ptr, MAP_SIZE - 1); - - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - ModulePass::getAnalysisUsage(AU); - AU.addRequired(); - AU.addRequired(); - - } - - StringRef getPassName() const override { - - return "InstTrim LTO Instrumentation"; - - } - - bool runOnModule(Module &M) override { - - char be_quiet = 0; - char * ptr; - uint32_t locations = 0, functions = 0; - - setvbuf(stdout, NULL, _IONBF, 0); - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "InsTrimLTO" VERSION cRST - " by csienslab and Marc \"vanHauser\" Heuse\n"); - - } else - - be_quiet = 1; - - /* Process environment variables */ - - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; - - if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { - - uint64_t val; - if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { - - map_addr = 0; - - } else if (map_addr == 0) { - - FATAL( - "AFL_LLVM_MAP_ADDR and AFL_LLVM_MAP_DYNAMIC cannot be used " - "together"); - - } else if (strncmp(ptr, "0x", 2) != 0) { - - map_addr = 0x10000; // the default - - } else { - - val = strtoull(ptr, NULL, 16); - if (val < 0x100 || val > 0xffffffff00000000) { - - FATAL( - "AFL_LLVM_MAP_ADDR must be a value between 0x100 and " - "0xffffffff00000000"); - - } - - map_addr = val; - - } - - } - - if (debug) { fprintf(stderr, "map address is %lu\n", map_addr); } - - if (getenv("AFL_LLVM_INSTRIM_LOOPHEAD") != NULL || - getenv("LOOPHEAD") != NULL) { - - LoopHeadOpt = true; - - } - - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || - getenv("AFL_LLVM_SKIPSINGLEBLOCK")) - function_minimum_size = 2; - - // this is our default - MarkSetOpt = true; - - /* Initialize LLVM instrumentation */ - - LLVMContext & C = M.getContext(); - std::vector dictionary; - std::vector calls; - DenseMap valueMap; - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); - IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - /* Get/set globals for the SHM region. */ - - GlobalVariable *AFLMapPtr = NULL; - Value * MapPtrFixed = NULL; - - if (!map_addr) { - - AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - - } else { - - ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); - MapPtrFixed = - ConstantExpr::getIntToPtr(MapAddr, PointerType::getUnqual(Int8Ty)); - - } - - if (autodictionary) { - - /* Some implementation notes. - * - * We try to handle 3 cases: - * - memcmp("foo", arg, 3) <- literal string - * - static char globalvar[] = "foo"; - * memcmp(globalvar, arg, 3) <- global variable - * - char localvar[] = "foo"; - * memcmp(locallvar, arg, 3) <- local variable - * - * The local variable case is the hardest. We can only detect that - * case if there is no reassignment or change in the variable. - * And it might not work across llvm version. - * What we do is hooking the initializer function for local variables - * (llvm.memcpy.p0i8.p0i8.i64) and note the string and the assigned - * variable. And if that variable is then used in a compare function - * we use that noted string. - * This seems not to work for tokens that have a size <= 4 :-( - * - * - if the compared length is smaller than the string length we - * save the full string. This is likely better for fuzzing but - * might be wrong in a few cases depending on optimizers - * - * - not using StringRef because there is a bug in the llvm 11 - * checkout I am using which sometimes points to wrong strings - * - * Over and out. Took me a full day. damn. mh/vh - */ - - for (Function &F : M) { - - for (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - - if ((callInst = dyn_cast(&IN))) { - - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool addedNull = false; - uint8_t optLen = 0; - - Function *Callee = callInst->getCalledFunction(); - if (!Callee) continue; - if (callInst->getCallingConv() != llvm::CallingConv::C) continue; - std::string FuncName = Callee->getName().str(); - isStrcmp &= !FuncName.compare("strcmp"); - isMemcmp &= !FuncName.compare("memcmp"); - isStrncmp &= !FuncName.compare("strncmp"); - isStrcasecmp &= !FuncName.compare("strcasecmp"); - isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp - * function prototype */ - FunctionType *FT = Callee->getFunctionType(); - - isStrcmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isStrcasecmp &= FT->getNumParams() == 2 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()); - isMemcmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0)->isPointerTy() && - FT->getParamType(1)->isPointerTy() && - FT->getParamType(2)->isIntegerTy(); - isStrncmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - isStrncasecmp &= FT->getNumParams() == 3 && - FT->getReturnType()->isIntegerTy(32) && - FT->getParamType(0) == FT->getParamType(1) && - FT->getParamType(0) == - IntegerType::getInt8PtrTy(M.getContext()) && - FT->getParamType(2)->isIntegerTy(); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) - continue; - - /* is a str{n,}{case,}cmp/memcmp, check if we have - * str{case,}cmp(x, "const") or str{case,}cmp("const", x) - * strn{case,}cmp(x, "const", ..) or strn{case,}cmp("const", x, - * ..) memcmp(x, "const", ..) or memcmp("const", x, ..) */ - Value *Str1P = callInst->getArgOperand(0), - *Str2P = callInst->getArgOperand(1); - std::string Str1, Str2; - StringRef TmpStr; - bool HasStr1 = getConstantStringInfo(Str1P, TmpStr); - if (TmpStr.empty()) - HasStr1 = false; - else - Str1 = TmpStr.str(); - bool HasStr2 = getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) - HasStr2 = false; - else - Str2 = TmpStr.str(); - - if (debug) - fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", - FuncName.c_str(), Str1P, Str1P->getName().str().c_str(), - Str1.c_str(), HasStr1 == true ? "true" : "false", Str2P, - Str2P->getName().str().c_str(), Str2.c_str(), - HasStr2 == true ? "true" : "false"); - - // we handle the 2nd parameter first because of llvm memcpy - if (!HasStr2) { - - auto *Ptr = dyn_cast(Str2P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = - dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr2 = true; - Str2 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // for the internal memcpy routine we only care for the second - // parameter and are not reporting anything. - if (isIntMemcpy == true) { - - if (HasStr2 == true) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = Str2.size(); - uint64_t optLength = ilen->getZExtValue(); - if (literalLength + 1 == optLength) { - - Str2.append("\0", 1); // add null byte - addedNull = true; - - } - - } - - valueMap[Str1P] = new std::string(Str2); - - if (debug) - fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), Str1P); - continue; - - } - - continue; - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr2) { - - std::string *strng = valueMap[Str2P]; - if (strng && !strng->empty()) { - - Str2 = *strng; - HasStr2 = true; - if (debug) - fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), - Str2P); - - } - - } - - if (!HasStr1) { - - auto Ptr = dyn_cast(Str1P); - - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = - dyn_cast(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast( - Var->getInitializer())) { - - HasStr1 = true; - Str1 = Array->getAsString().str(); - - } - - } - - } - - } - - } - - // Neither a literal nor a global variable? - // maybe it is a local variable that we saved - if (!HasStr1) { - - std::string *strng = valueMap[Str1P]; - if (strng && !strng->empty()) { - - Str1 = *strng; - HasStr1 = true; - if (debug) - fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), - Str1P); - - } - - } - - /* handle cases of one string is const, one string is variable */ - if (!(HasStr1 ^ HasStr2)) continue; - - std::string thestring; - - if (HasStr1) - thestring = Str1; - else - thestring = Str2; - - optLen = thestring.length(); - - if (isMemcmp || isStrncmp || isStrncasecmp) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast(op2); - if (ilen) { - - uint64_t literalLength = optLen; - optLen = ilen->getZExtValue(); - if (literalLength + 1 == optLen) { // add null byte - thestring.append("\0", 1); - addedNull = true; - - } - - } - - } - - // add null byte if this is a string compare function and a null - // was not already added - if (addedNull == false && !isMemcmp) { - - thestring.append("\0", 1); // add null byte - optLen++; - - } - - if (!be_quiet) { - - std::string outstring; - fprintf(stderr, "%s: length %u/%u \"", FuncName.c_str(), optLen, - (unsigned int)thestring.length()); - for (uint8_t i = 0; i < thestring.length(); i++) { - - uint8_t c = thestring[i]; - if (c <= 32 || c >= 127) - fprintf(stderr, "\\x%02x", c); - else - fprintf(stderr, "%c", c); - - } - - fprintf(stderr, "\"\n"); - - } - - // we take the longer string, even if the compare was to a - // shorter part. Note that depending on the optimizer of the - // compiler this can be wrong, but it is more likely that this - // is helping the fuzzer - if (optLen != thestring.length()) optLen = thestring.length(); - if (optLen > MAX_AUTO_EXTRA) optLen = MAX_AUTO_EXTRA; - if (optLen < MIN_AUTO_EXTRA) // too short? skip - continue; - - dictionary.push_back(thestring.substr(0, optLen)); - - } - - } - - } - - } - - } - - /* InsTrim instrumentation starts here */ - - u64 total_rs = 0; - u64 total_hs = 0; - - for (Function &F : M) { - - if (debug) { - - uint32_t bb_cnt = 0; - - for (auto &BB : F) - if (BB.size() > 0) ++bb_cnt; - SAYF(cMGN "[D] " cRST "Function %s size %zu %u\n", - F.getName().str().c_str(), F.size(), bb_cnt); - - } - - // if the function below our minimum size skip it (1 or 2) - if (F.size() < function_minimum_size) continue; - if (isIgnoreFunction(&F)) continue; - - functions++; - - // the instrument file list check - AttributeList Attrs = F.getAttributes(); - if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { - - if (debug) - fprintf(stderr, - "DEBUG: Function %s is not the instrument file listed\n", - F.getName().str().c_str()); - continue; - - } - - std::unordered_set MS; - if (!MarkSetOpt) { - - for (auto &BB : F) { - - MS.insert(&BB); - - } - - total_rs += F.size(); - - } else { - - auto Result = markNodes(&F); - auto RS = Result.first; - auto HS = Result.second; - - MS.insert(RS.begin(), RS.end()); - if (!LoopHeadOpt) { - - MS.insert(HS.begin(), HS.end()); - total_rs += MS.size(); - - } else { - - DenseSet> EdgeSet; - DominatorTreeWrapperPass * DTWP = - &getAnalysis(F); - auto DT = &DTWP->getDomTree(); - - total_rs += RS.size(); - total_hs += HS.size(); - - for (BasicBlock *BB : HS) { - - bool Inserted = false; - for (auto BI = pred_begin(BB), BE = pred_end(BB); BI != BE; ++BI) { - - auto Edge = BasicBlockEdge(*BI, BB); - if (Edge.isSingleEdge() && DT->dominates(Edge, BB)) { - - EdgeSet.insert({*BI, BB}); - Inserted = true; - break; - - } - - } - - if (!Inserted) { - - MS.insert(BB); - total_rs += 1; - total_hs -= 1; - - } - - } - - for (auto I = EdgeSet.begin(), E = EdgeSet.end(); I != E; ++I) { - - auto PredBB = I->first; - auto SuccBB = I->second; - auto NewBB = SplitBlockPredecessors(SuccBB, {PredBB}, ".split", DT, - nullptr, nullptr, false); - MS.insert(NewBB); - - } - - } - - } - - for (BasicBlock &BB : F) { - - auto PI = pred_begin(&BB); - auto PE = pred_end(&BB); - IRBuilder<> IRB(&*BB.getFirstInsertionPt()); - Value * L = NULL; - - if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } - - if (PI == PE) { - - L = ConstantInt::get(Int32Ty, afl_global_id++); - locations++; - - } else { - - auto *PN = PHINode::Create(Int32Ty, 0, "", &*BB.begin()); - DenseMap PredMap; - for (auto PI = pred_begin(&BB), PE = pred_end(&BB); PI != PE; ++PI) { - - BasicBlock *PBB = *PI; - auto It = PredMap.insert({PBB, afl_global_id++}); - unsigned Label = It.first->second; - PN->addIncoming(ConstantInt::get(Int32Ty, Label), PBB); - locations++; - - } - - L = PN; - - } - - /* Load SHM pointer */ - Value *MapPtrIdx; - - if (map_addr) { - - MapPtrIdx = IRB.CreateGEP(MapPtrFixed, L); - - } else { - - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - MapPtrIdx = IRB.CreateGEP(MapPtr, L); - - } - - /* Update bitmap */ - LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); - Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - - if (skip_nozero == NULL) { - - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); - - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - // done :) - - inst_blocks++; - - } - - } - - // save highest location ID to global variable - // do this after each function to fail faster - if (!be_quiet && afl_global_id > MAP_SIZE && - afl_global_id > FS_OPT_MAX_MAPSIZE) { - - uint32_t pow2map = 1, map = afl_global_id; - while ((map = map >> 1)) - pow2map++; - WARNF( - "We have %u blocks to instrument but the map size is only %u. Either " - "edit config.h and set MAP_SIZE_POW2 from %u to %u, then recompile " - "afl-fuzz and llvm_mode and then make this target - or set " - "AFL_MAP_SIZE with at least size %u when running afl-fuzz with this " - "target.", - afl_global_id, MAP_SIZE, MAP_SIZE_POW2, pow2map, afl_global_id); - - } - - if (!getenv("AFL_LLVM_LTO_DONTWRITEID") || dictionary.size() || map_addr) { - - // yes we could create our own function, insert it into ctors ... - // but this would be a pain in the butt ... so we use afl-llvm-rt-lto.o - - Function *f = M.getFunction("__afl_auto_init_globals"); - - if (!f) { - - fprintf(stderr, - "Error: init function could not be found (this should not " - "happen)\n"); - exit(-1); - - } - - BasicBlock *bb = &f->getEntryBlock(); - if (!bb) { - - fprintf(stderr, - "Error: init function does not have an EntryBlock (this should " - "not happen)\n"); - exit(-1); - - } - - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (map_addr) { - - GlobalVariable *AFLMapAddrFixed = - new GlobalVariable(M, Int64Ty, true, GlobalValue::ExternalLinkage, - 0, "__afl_map_addr"); - ConstantInt *MapAddr = ConstantInt::get(Int64Ty, map_addr); - StoreInst * StoreMapAddr = IRB.CreateStore(MapAddr, AFLMapAddrFixed); - StoreMapAddr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { - - uint32_t write_loc = afl_global_id; - - if (afl_global_id % 8) write_loc = (((afl_global_id + 8) >> 3) << 3); - - GlobalVariable *AFLFinalLoc = - new GlobalVariable(M, Int32Ty, true, GlobalValue::ExternalLinkage, - 0, "__afl_final_loc"); - ConstantInt *const_loc = ConstantInt::get(Int32Ty, write_loc); - StoreInst * StoreFinalLoc = IRB.CreateStore(const_loc, AFLFinalLoc); - StoreFinalLoc->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - if (dictionary.size()) { - - size_t memlen = 0, count = 0, offset = 0; - char * ptr; - - for (auto token : dictionary) { - - memlen += token.length(); - count++; - - } - - if (!be_quiet) - printf("AUTODICTIONARY: %lu string%s found\n", count, - count == 1 ? "" : "s"); - - if (count) { - - if ((ptr = (char *)malloc(memlen + count)) == NULL) { - - fprintf(stderr, "Error: malloc for %lu bytes failed!\n", - memlen + count); - exit(-1); - - } - - count = 0; - - for (auto token : dictionary) { - - if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { - - ptr[offset++] = (uint8_t)token.length(); - memcpy(ptr + offset, token.c_str(), token.length()); - offset += token.length(); - count++; - - } - - } - - GlobalVariable *AFLDictionaryLen = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, - "__afl_dictionary_len"); - ConstantInt *const_len = ConstantInt::get(Int32Ty, offset); - StoreInst * StoreDictLen = - IRB.CreateStore(const_len, AFLDictionaryLen); - StoreDictLen->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - ArrayType *ArrayTy = ArrayType::get(IntegerType::get(C, 8), offset); - GlobalVariable *AFLInternalDictionary = new GlobalVariable( - M, ArrayTy, true, GlobalValue::ExternalLinkage, - ConstantDataArray::get( - C, *(new ArrayRef((char *)ptr, offset))), - "__afl_internal_dictionary"); - AFLInternalDictionary->setInitializer(ConstantDataArray::get( - C, *(new ArrayRef((char *)ptr, offset)))); - AFLInternalDictionary->setConstant(true); - - GlobalVariable *AFLDictionary = new GlobalVariable( - M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_dictionary"); - - Value *AFLDictOff = IRB.CreateGEP(AFLInternalDictionary, Zero); - Value *AFLDictPtr = - IRB.CreatePointerCast(AFLDictOff, PointerType::get(Int8Ty, 0)); - StoreInst *StoreDict = IRB.CreateStore(AFLDictPtr, AFLDictionary); - StoreDict->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } - - } - - } - - // count basic blocks for comparison with classic instrumentation - - u32 edges = 0; - for (auto &F : M) { - - if (F.size() < function_minimum_size) continue; - - for (auto &BB : F) { - - bool would_instrument = false; - - for (BasicBlock *Pred : predecessors(&BB)) { - - int count = 0; - for (BasicBlock *Succ : successors(Pred)) - if (Succ != NULL) count++; - - if (count > 1) would_instrument = true; - - } - - if (would_instrument == true) edges++; - - } - - } - - /* Say something nice. */ - - if (!be_quiet) { - - if (!inst_blocks) - WARNF("No instrumentation targets found."); - else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %u locations for %u edges in %u functions (%llu, " - "%llu) with no collisions (on " - "average %llu collisions would be in afl-gcc/afl-clang-fast for %u " - "edges) (%s mode).", - inst_blocks, locations, functions, total_rs, total_hs, - calculateCollisions(edges), edges, modeline); - - } - - } - - return true; - - } - -}; // end of struct InsTrim - -} // end of anonymous namespace - -char InsTrimLTO::ID = 0; - -static void registerInsTrimLTO(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new InsTrimLTO()); - -} - -static RegisterPass X("afl-lto-instrim", - "afl++ InsTrim LTO instrumentation pass", - false, false); - -static RegisterStandardPasses RegisterInsTrimLTO( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerInsTrimLTO); - diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 18bee7a5..12509ab2 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -128,6 +128,8 @@ bool AFLLTOPass::runOnModule(Module &M) { be_quiet = 1; + if (getenv("AFL_LLVM_CMPLOG")) autodictionary = 0; + if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { if ((documentFile = fopen(ptr, "a")) == NULL) @@ -142,8 +144,6 @@ bool AFLLTOPass::runOnModule(Module &M) { /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ map_addr = 0; - if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; - if ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { uint64_t val; diff --git a/llvm_mode/afl-llvm-pass.so.cc b/llvm_mode/afl-llvm-pass.so.cc index 0206080f..86c6f3c6 100644 --- a/llvm_mode/afl-llvm-pass.so.cc +++ b/llvm_mode/afl-llvm-pass.so.cc @@ -182,10 +182,6 @@ bool AFLCoverage::runOnModule(Module &M) { #endif skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - if (getenv("AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK") || - getenv("AFL_LLVM_SKIPSINGLEBLOCK")) - function_minimum_size = 2; - unsigned PrevLocSize = 0; char *ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); -- cgit 1.4.1 From 182b8b3e142814ac865396f8a66453f071d8a2d6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 20 Aug 2020 19:00:15 +0200 Subject: remove doc reference for SKIPSINGLEBLOCK --- docs/env_variables.md | 10 +++------- llvm_mode/README.instrim.md | 9 --------- 2 files changed, 3 insertions(+), 16 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index f0ae0b6c..94c34400 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -83,17 +83,12 @@ tools make fairly broad use of environmental variables: The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset of the settings discussed in section #1, with the exception of: - - Setting AFL_LLVM_SKIPSINGLEBLOCK=1 will skip instrumenting - functions with a single basic block. This is useful for most C and - some C++ targets. This works for all instrumentation modes. - - AFL_AS, since this toolchain does not directly invoke GNU as. - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are created. - - AFL_INST_RATIO, as we switched for instrim instrumentation which - is more effective but makes not much sense together with this option. + - AFL_INST_RATIO, as we by default collision free instrumentation is used. Then there are a few specific features that are only available in llvm_mode: @@ -121,7 +116,8 @@ Then there are a few specific features that are only available in llvm_mode: built if LLVM 11 or newer is used. - AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation. - (not recommended!) + (not recommended for afl-clang-fast, default for afl-clang-lto as there + it is a different and better kind of instrumentation.) None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). diff --git a/llvm_mode/README.instrim.md b/llvm_mode/README.instrim.md index 53a518a9..7758091b 100644 --- a/llvm_mode/README.instrim.md +++ b/llvm_mode/README.instrim.md @@ -19,15 +19,6 @@ see how often the loop has been rerun. This again is a tradeoff for speed for less path information. To enable this mode set `AFL_LLVM_INSTRIM_LOOPHEAD=1`. -There is an additional optimization option that skips single block -functions. In 95% of the C targets and (guess) 50% of the C++ targets -it is good to enable this, as otherwise pointless instrumentation occurs. -The corner case where we want this instrumentation is when vtable/call table -is used and the index to that vtable/call table is not set in specific -basic blocks. -To enable skipping these (most of the time) unnecessary instrumentations set -`AFL_LLVM_INSTRIM_SKIPSINGLEBLOCK=1` - ## Background The paper: [InsTrim: Lightweight Instrumentation for Coverage-guided Fuzzing] -- cgit 1.4.1 From 47878f697485e0543ce9e5d81369aa1ecc56e55e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 21 Aug 2020 23:33:35 +0200 Subject: add execs_done to plot file --- docs/Changelog.md | 1 + include/afl-fuzz.h | 2 +- src/afl-fuzz-stats.c | 17 ++++++++++------- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index d00d59d7..8bbb4e19 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -13,6 +13,7 @@ sending a mail to . - afl-fuzz: - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary + - added total execs done to plot file - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c04ba396..1deeddd3 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -624,7 +624,7 @@ typedef struct afl_state { /* plot file saves from last run */ u32 plot_prev_qp, plot_prev_pf, plot_prev_pnf, plot_prev_ce, plot_prev_md; - u64 plot_prev_qc, plot_prev_uc, plot_prev_uh; + u64 plot_prev_qc, plot_prev_uc, plot_prev_uh, plot_prev_ed; u64 stats_last_stats_ms, stats_last_plot_ms, stats_last_ms, stats_last_execs; double stats_avg_exec; diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index aeb290bd..0ce35cb7 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -206,7 +206,8 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { afl->plot_prev_qc == afl->queue_cycle && afl->plot_prev_uc == afl->unique_crashes && afl->plot_prev_uh == afl->unique_hangs && - afl->plot_prev_md == afl->max_depth) || + afl->plot_prev_md == afl->max_depth && + afl->plot_prev_ed == afl->fsrv.total_execs) || unlikely(!afl->queue_cycle) || unlikely(get_cur_time() - afl->start_time <= 60)) { @@ -222,6 +223,7 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { afl->plot_prev_uc = afl->unique_crashes; afl->plot_prev_uh = afl->unique_hangs; afl->plot_prev_md = afl->max_depth; + afl->plot_prev_ed = afl->fsrv.total_execs; /* Fields in the file: @@ -229,12 +231,13 @@ void maybe_update_plot_file(afl_state_t *afl, double bitmap_cvg, double eps) { favored_not_fuzzed, afl->unique_crashes, afl->unique_hangs, afl->max_depth, execs_per_sec */ - fprintf(afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n", - get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, - afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, - bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, - eps); /* ignore errors */ + fprintf( + afl->fsrv.plot_file, + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu\n", + get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, + afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, + bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps, + afl->plot_prev_ed); /* ignore errors */ fflush(afl->fsrv.plot_file); -- cgit 1.4.1 From 6184832ea98050c91cdefa5059e18e78c39b14de Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sun, 23 Aug 2020 10:59:56 +0200 Subject: added more env var docs, fsrv fixes for cmin, tmin --- docs/Changelog.md | 4 ++++ docs/env_variables.md | 15 +++++++++++++++ src/afl-showmap.c | 10 ++++++++++ src/afl-tmin.c | 10 ++++++++++ 4 files changed, 39 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 8bbb4e19..f7bc9600 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,10 @@ sending a mail to . - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary - added total execs done to plot file + - AFL_MAX_DET_EXTRAS env variable added to control the amount of deterministic + dict entries without recompiling. + - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for + the forkserver to come up without the need to increase the overall timeout. - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/docs/env_variables.md b/docs/env_variables.md index 94c34400..e8129a3f 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -278,6 +278,14 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. + - Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout + to wait for the forkserver to spin up. The default is the `-t` value times + `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the + default would wait `1000` milis. Setting a different time here is useful + if the target has a very slow startup time, for example when doing + full-system fuzzing or emulation, but you don't want the actual runs + to wait too long for timeouts. + - AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. @@ -361,6 +369,13 @@ checks or alter some of the more exotic semantics of the tool: for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. + - Setting AFL_MAX_DET_EXRAS will change the threshold at what number of elements + in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will + kick off. In probabilistic mode, not all dictionary entires will be used all + of the times for fuzzing mutations to not make fuzzing slower by it. + The default count is `200` element. So for the 200 + 1st element, there is a + 1 in 201 chance, that one of the dictionary entry will not be used directly. + - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when working with unruly libraries that create threads or do other crazy diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 47c615d8..2c20e419 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1036,6 +1036,16 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } + fsrv->init_tmout = (u32) forksrv_init_tmout; + + } + afl_fsrv_start(fsrv, use_argv, &stop_soon, get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); map_size = fsrv->map_size; diff --git a/src/afl-tmin.c b/src/afl-tmin.c index b50d8597..a15a6079 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1103,6 +1103,16 @@ int main(int argc, char **argv_orig, char **envp) { } SAYF("\n"); + + if (getenv("AFL_FORKSRV_INIT_TMOUT")) { + + s32 forksrv_init_tmout = atoi(getenv("AFL_FORKSRV_INIT_TMOUT")); + if (forksrv_init_tmout < 1) { + FATAL("Bad value specified for AFL_FORKSRV_INIT_TMOUT"); + } + fsrv->init_tmout = (u32) forksrv_init_tmout; + + } shm_fuzz = ck_alloc(sizeof(sharedmem_t)); -- cgit 1.4.1 From c7f0d3066875bca0ec28e9429df40293339dc05c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 24 Aug 2020 17:32:41 +0200 Subject: added afl_custom_fuzz_count --- GNUmakefile | 12 ++--- docs/Changelog.md | 3 ++ docs/custom_mutators.md | 9 ++++ include/afl-fuzz.h | 20 ++++++++ src/afl-forkserver.c | 2 +- src/afl-fuzz-extras.c | 4 +- src/afl-fuzz-mutators.c | 5 ++ src/afl-fuzz-one.c | 129 +++++++++++++++++++++++++++--------------------- src/afl-fuzz-python.c | 44 +++++++++++++++++ 9 files changed, 162 insertions(+), 66 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index 3c5e10ed..cae172dd 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -37,18 +37,18 @@ MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8 ASAN_OPTIONS=detect_leaks=0 ifeq "$(findstring android, $(shell $(CC) --version 2>/dev/null))" "" -ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=full -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto=full -else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" - CFLAGS_FLTO ?= -flto=thin else - ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto=thin -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" + CFLAGS_FLTO ?= -flto=thin + else + ifeq "$(shell echo 'int main() {return 0; }' | $(CC) $(CFLAGS) -Werror -x c - -flto -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" CFLAGS_FLTO ?= -flto + endif endif endif endif -endif ifeq "$(shell echo 'int main() {return 0; }' | $(CC) -fno-move-loop-invariants -fdisable-tree-cunrolli -x c - -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" SPECIAL_PERFORMANCE += -fno-move-loop-invariants -fdisable-tree-cunrolli diff --git a/docs/Changelog.md b/docs/Changelog.md index f7bc9600..45fbd528 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -18,6 +18,9 @@ sending a mail to . dict entries without recompiling. - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for the forkserver to come up without the need to increase the overall timeout. + - custom mutators: + - added afl_custom_fuzz_count/fuzz_count function to allow specifying the + number of fuzz attempts for custom_fuzz - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index a22c809b..75dbea21 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -32,6 +32,7 @@ performed with the custom mutator. C/C++: ```c void *afl_custom_init(afl_t *afl, unsigned int seed); +uint32_t afl_custom_fuzz_count(void *data, const u8 *buf, size_t buf_size); size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, u8 **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size); size_t afl_custom_post_process(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf); int32_t afl_custom_init_trim(void *data, uint8_t *buf, size_t buf_size); @@ -49,6 +50,9 @@ Python: def init(seed): pass +def fuzz_count(buf, add_buf, max_size): + return cnt + def fuzz(buf, add_buf, max_size): return mutated_out @@ -88,6 +92,11 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): This method determines whether the custom fuzzer should fuzz the current queue entry or not +- `fuzz_count` (optional): + + This method can be used to instruct afl-fuzz how often to perform a fuzz + attempt on this input data. + - `fuzz` (optional): This method performs custom mutations on a given input. It also accepts an diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 1f1dda3a..01aa1a73 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -288,6 +288,7 @@ enum { enum { /* 00 */ PY_FUNC_INIT, + /* 01 */ PY_FUNC_FUZZ_COUNT, /* 01 */ PY_FUNC_FUZZ, /* 02 */ PY_FUNC_POST_PROCESS, /* 03 */ PY_FUNC_INIT_TRIM, @@ -679,6 +680,24 @@ struct custom_mutator { */ void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed); + /** + * This method is called just before fuzzing a queue entry with the custom + * mutator, and receives the initial buffer. It should return the number of + * fuzzes to perform. + * + * A value of 0 means no fuzzing of this queue entry. + * + * The function is now allowed to change the data. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param buf Buffer containing the test case + * @param buf_size Size of the test case + * @return The amount of fuzzes to perform on this queue entry, 0 = skip + */ + u32 (*afl_custom_fuzz_count)(void *data, const u8 *buf, size_t buf_size); + /** * Perform custom mutations on a given input * @@ -867,6 +886,7 @@ u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf, struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); void finalize_py_module(void *); +u32 fuzz_count_py(void *, const u8 *, size_t); size_t post_process_py(void *, u8 *, size_t, u8 **); s32 init_trim_py(void *, u8 *, size_t); s32 post_trim_py(void *, u8); diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index c496975f..72f3dc3b 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -634,7 +634,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { - // this is not afl-fuzz - we deny and return + // this is not afl-fuzz - or it is cmplog - we deny and return if (fsrv->use_shmem_fuzz) { status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 8cc2425f..d6c368d1 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -251,7 +251,7 @@ static void extras_check_and_sort(afl_state_t *afl, u32 min_len, u32 max_len, if (afl->extras_cnt > afl->max_det_extras) { OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + afl->max_det_extras); } @@ -406,7 +406,7 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { if (afl->extras_cnt == afl->max_det_extras + 1) { OKF("More than %d tokens - will use them probabilistically.", - afl->max_det_extras); + afl->max_det_extras); } diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 22578df9..d24b7db9 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -166,6 +166,11 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { } + /* "afl_custom_fuzz_count", optional */ + mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); + if (!mutator->afl_custom_fuzz_count) + ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); if (!mutator->afl_custom_deinit) diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c0c036db..03c0d3a1 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1672,7 +1672,7 @@ custom_mutator_stage: if (afl->stage_max < HAVOC_MIN) { afl->stage_max = HAVOC_MIN; } - const u32 max_seed_size = MAX_FILE; + const u32 max_seed_size = MAX_FILE, saved_max = afl->stage_max; orig_hit_cnt = afl->queued_paths + afl->unique_crashes; @@ -1680,104 +1680,119 @@ custom_mutator_stage: if (el->afl_custom_fuzz) { + if (el->afl_custom_fuzz_count) + afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); + else + afl->stage_max = saved_max; + has_custom_fuzz = true; afl->stage_short = el->name_short; - for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; - ++afl->stage_cur) { + if (afl->stage_max) { - struct queue_entry *target; - u32 tid; - u8 * new_buf; + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; + ++afl->stage_cur) { - retry_external_pick: - /* Pick a random other queue entry for passing to external API */ + struct queue_entry *target; + u32 tid; + u8 * new_buf; - do { + retry_external_pick: + /* Pick a random other queue entry for passing to external API */ - tid = rand_below(afl, afl->queued_paths); + do { - } while (tid == afl->current_entry && afl->queued_paths > 1); + tid = rand_below(afl, afl->queued_paths); - target = afl->queue; + } while (tid == afl->current_entry && afl->queued_paths > 1); - while (tid >= 100) { + target = afl->queue; - target = target->next_100; - tid -= 100; + while (tid >= 100) { - } - - while (tid--) { + target = target->next_100; + tid -= 100; - target = target->next; + } - } + while (tid--) { - /* Make sure that the target has a reasonable length. */ + target = target->next; - while (target && (target->len < 2 || target == afl->queue_cur) && - afl->queued_paths > 3) { + } - target = target->next; - ++afl->splicing_with; + /* Make sure that the target has a reasonable length. */ - } + while (target && (target->len < 2 || target == afl->queue_cur) && + afl->queued_paths > 3) { - if (!target) { goto retry_external_pick; } + target = target->next; + ++afl->splicing_with; - /* Read the additional testcase into a new buffer. */ - fd = open(target->fname, O_RDONLY); - if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); } + } - new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); - if (unlikely(!new_buf)) { PFATAL("alloc"); } - ck_read(fd, new_buf, target->len, target->fname); - close(fd); + if (!target) { goto retry_external_pick; } - u8 *mutated_buf = NULL; + /* Read the additional testcase into a new buffer. */ + fd = open(target->fname, O_RDONLY); + if (unlikely(fd < 0)) { - size_t mutated_size = - el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, - target->len, max_seed_size); + PFATAL("Unable to open '%s'", target->fname); - if (unlikely(!mutated_buf)) { + } - FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); + new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), target->len); + if (unlikely(!new_buf)) { PFATAL("alloc"); } + ck_read(fd, new_buf, target->len, target->fname); + close(fd); - } + u8 *mutated_buf = NULL; - if (mutated_size > 0) { + size_t mutated_size = + el->afl_custom_fuzz(el->data, out_buf, len, &mutated_buf, new_buf, + target->len, max_seed_size); - if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { + if (unlikely(!mutated_buf)) { - goto abandon_entry; + FATAL("Error in custom_fuzz. Size returned: %zd", mutated_size); } - /* If we're finding new stuff, let's run for a bit longer, limits - permitting. */ - - if (afl->queued_paths != havoc_queued) { + if (mutated_size > 0) { - if (perf_score <= afl->havoc_max_mult * 100) { + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { - afl->stage_max *= 2; - perf_score *= 2; + goto abandon_entry; } - havoc_queued = afl->queued_paths; + /* If we're finding new stuff, let's run for a bit longer, limits + permitting. */ + + if (afl->queued_paths != havoc_queued) { + + if (perf_score <= afl->havoc_max_mult * 100) { + + afl->stage_max *= 2; + perf_score *= 2; + + } + + havoc_queued = afl->queued_paths; + + } } - } + /* `(afl->)out_buf` may have been changed by the call to custom_fuzz + */ + /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs + * Memcpy. + */ + memcpy(out_buf, in_buf, len); - /* `(afl->)out_buf` may have been changed by the call to custom_fuzz */ - /* TODO: Only do this when `mutated_buf` == `out_buf`? Branch vs Memcpy. - */ - memcpy(out_buf, in_buf, len); + } } diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index e540f548..68540dd7 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -347,6 +347,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + if (py_functions[PY_FUNC_FUZZ_COUNT]) { + + mutator->afl_custom_fuzz_count = fuzz_count_py; + + } + if (py_functions[PY_FUNC_POST_TRIM]) { mutator->afl_custom_post_trim = post_trim_py; @@ -477,6 +483,44 @@ s32 init_trim_py(void *py_mutator, u8 *buf, size_t buf_size) { } +u32 fuzz_count_py(void *py_mutator, const u8 *buf, size_t buf_size) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + py_value = PyByteArray_FromStringAndSize(buf, buf_size); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_FUZZ_COUNT], py_args); + Py_DECREF(py_args); + + if (py_value != NULL) { + + #if PY_MAJOR_VERSION >= 3 + u32 retcnt = (u32)PyLong_AsLong(py_value); + #else + u32 retcnt = PyInt_AsLong(py_value); + #endif + Py_DECREF(py_value); + return retcnt; + + } else { + + PyErr_Print(); + FATAL("Call failed"); + + } + +} + s32 post_trim_py(void *py_mutator, u8 success) { PyObject *py_args, *py_value; -- cgit 1.4.1 From 41bb359428e4559821c95831f25c772d1a8403d9 Mon Sep 17 00:00:00 2001 From: Raphaël Hertzog Date: Fri, 28 Aug 2020 23:04:25 +0200 Subject: Fix various spelling errors (#532) All those spelling errors have been caught by lintian's built-in spellchecker: https://lintian.debian.org/tags/spelling-error-in-binary.html --- docs/Changelog.md | 2 +- llvm_mode/afl-llvm-rt.o.c | 2 +- llvm_mode/split-compares-pass.so.cc | 8 ++++---- src/afl-fuzz.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 45fbd528..cb6e14b8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -416,7 +416,7 @@ sending a mail to . - big code refactoring: * all includes are now in include/ * all afl sources are now in src/ - see src/README.md - * afl-fuzz was splitted up in various individual files for including + * afl-fuzz was split up in various individual files for including functionality in other programs (e.g. forkserver, memory map, etc.) for better readability. * new code indention everywhere diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index d00fd26f..bdafbe0b 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -339,7 +339,7 @@ static void __afl_map_shm(void) { if (__afl_area_ptr == MAP_FAILED) { - fprintf(stderr, "can not aquire mmap for address %p\n", + fprintf(stderr, "can not acquire mmap for address %p\n", (void *)__afl_map_addr); exit(1); diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc index 617b55de..2e57a30a 100644 --- a/llvm_mode/split-compares-pass.so.cc +++ b/llvm_mode/split-compares-pass.so.cc @@ -1272,7 +1272,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { if (!be_quiet) { errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons splitted\n"; + << " FP comparisons split\n"; } @@ -1290,7 +1290,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1301,7 +1301,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ @@ -1312,7 +1312,7 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitIntCompares(M, bitw); if (!be_quiet) errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " splitted\n"; + << " split\n"; bitw >>= 1; break; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 1abd49d8..5ad2ace9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -191,7 +191,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_QUIET: suppress forkserver status messages\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n" - "AFL_SKIP_BIN_CHECK: skip the check, if the target is an excutable\n" + "AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n" "AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n" "AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" -- cgit 1.4.1 From 567042d14698a588f83c16e50c4e83143971fe46 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 31 Aug 2020 12:32:31 +0200 Subject: typos fixed. --- docs/binaryonly_fuzzing.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index a3d3330f..cb1288ef 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -6,14 +6,14 @@ However, if there is only the binary program and no source code available, then standard `afl-fuzz -n` (non-instrumented mode) is not effective. - The following is a description of how these binaries can be fuzzed with afl++ + The following is a description of how these binaries can be fuzzed with afl++. ## TL;DR: qemu_mode in persistent mode is the fastest - if the stability is high enough. Otherwise try retrowrite, afl-dyninst and if these - fail too then standard qemu_mode with AFL_ENTRYPOINT to where you need it. + fail too then try standard qemu_mode with AFL_ENTRYPOINT to where you need it. If your target is a library use examples/afl_frida/. @@ -29,10 +29,10 @@ The speed decrease is at about 50%. However various options exist to increase the speed: - - using AFL_ENTRYPOINT to move the forkserver to a later basic block in + - using AFL_ENTRYPOINT to move the forkserver entry to a later basic block in the binary (+5-10% speed) - using persistent mode [qemu_mode/README.persistent.md](../qemu_mode/README.persistent.md) - this will result in 150-300% overall speed - so 3-8x the original + this will result in 150-300% overall speed increase - so 3-8x the original qemu_mode speed! - using AFL_CODE_START/AFL_CODE_END to only instrument specific parts @@ -104,7 +104,7 @@ ## RETROWRITE - If you have an x86/x86_64 binary that still has it's symbols, is compiled + If you have an x86/x86_64 binary that still has its symbols, is compiled with position independant code (PIC/PIE) and does not use most of the C++ features then the retrowrite solution might be for you. It decompiles to ASM files which can then be instrumented with afl-gcc. @@ -148,7 +148,7 @@ ## CORESIGHT Coresight is ARM's answer to Intel's PT. - There is no implementation so far which handle coresight and getting + There is no implementation so far which handles coresight and getting it working on an ARM Linux is very difficult due to custom kernel building on embedded systems is difficult. And finding one that has coresight in the ARM chip is difficult too. -- cgit 1.4.1 From e7db4d4fe0c334404c531821ae52a5f20f9185a1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 12:36:30 +0200 Subject: fix sync script, update remote sync documentation --- docs/parallel_fuzzing.md | 105 ++++++++++++++++------------ examples/distributed_fuzzing/sync_script.sh | 11 +-- 2 files changed, 68 insertions(+), 48 deletions(-) (limited to 'docs') diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index 2ab1466c..14c237c1 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -10,8 +10,8 @@ n-core system, you can almost always run around n concurrent fuzzing jobs with virtually no performance hit (you can use the afl-gotcpu tool to make sure). In fact, if you rely on just a single job on a multi-core system, you will -be underutilizing the hardware. So, parallelization is usually the right -way to go. +be underutilizing the hardware. So, parallelization is always the right way to +go. When targeting multiple unrelated binaries or using the tool in "non-instrumented" (-n) mode, it is perfectly fine to just start up several @@ -65,22 +65,7 @@ still perform deterministic checks; while the secondary instances will proceed straight to random tweaks. Note that you must always have one -M main instance! - -Note that running multiple -M instances is wasteful, although there is an -experimental support for parallelizing the deterministic checks. To leverage -that, you need to create -M instances like so: - -``` -./afl-fuzz -i testcase_dir -o sync_dir -M mainA:1/3 [...] -./afl-fuzz -i testcase_dir -o sync_dir -M mainB:2/3 [...] -./afl-fuzz -i testcase_dir -o sync_dir -M mainC:3/3 [...] -``` - -...where the first value after ':' is the sequential ID of a particular main -instance (starting at 1), and the second value is the total number of fuzzers to -distribute the deterministic fuzzing across. Note that if you boot up fewer -fuzzers than indicated by the second number passed to -M, you may end up with -poor coverage. +Running multiple -M instances is wasteful! You can also monitor the progress of your jobs from the command line with the provided afl-whatsup tool. When the instances are no longer finding new paths, @@ -99,61 +84,88 @@ example may be: This is not a concern if you use @@ without -f and let afl-fuzz come up with the file name. -## 3) Syncing with non-afl fuzzers or independant instances +## 3) Multiple -M mains + + +There is support for parallelizing the deterministic checks. +This is only needed where + + 1. many new paths are found fast over a long time and it looks unlikely that + main node will ever catch up, and + 2. deterministic fuzzing is actively helping path discovery (you can see this + in the main node for the first for lines in the "fuzzing strategy yields" + section. If the ration `found/attemps` is high, then it is effective. It + most commonly isn't.) + +Only if both are true it is beneficial to have more than one main. +You can leverage this by creating -M instances like so: + +``` +./afl-fuzz -i testcase_dir -o sync_dir -M mainA:1/3 [...] +./afl-fuzz -i testcase_dir -o sync_dir -M mainB:2/3 [...] +./afl-fuzz -i testcase_dir -o sync_dir -M mainC:3/3 [...] +``` + +... where the first value after ':' is the sequential ID of a particular main +instance (starting at 1), and the second value is the total number of fuzzers to +distribute the deterministic fuzzing across. Note that if you boot up fewer +fuzzers than indicated by the second number passed to -M, you may end up with +poor coverage. + +## 4) Syncing with non-afl fuzzers or independant instances A -M main node can be told with the `-F other_fuzzer_queue_directory` option to sync results from other fuzzers, e.g. libfuzzer or honggfuzz. Only the specified directory will by synced into afl, not subdirectories. -The specified directories do not need to exist yet at the start of afl. +The specified directory does not need to exist yet at the start of afl. -## 4) Multi-system parallelization +The `-F` option can be passed to the main node several times. + +## 5) Multi-system parallelization The basic operating principle for multi-system parallelization is similar to the mechanism explained in section 2. The key difference is that you need to write a simple script that performs two actions: - Uses SSH with authorized_keys to connect to every machine and retrieve - a tar archive of the /path/to/sync_dir//queue/ directories for - every local to the machine. It's best to use a naming scheme - that includes host name in the fuzzer ID, so that you can do something - like: + a tar archive of the /path/to/sync_dir/ directory local to + the machine. + It is best to use a naming scheme that includes host name and it's being + a main node (e.g. main1, main2) in the fuzzer ID, so that you can do + something like: ```sh - for s in {1..10}; do - ssh user@host${s} "tar -czf - sync/host${s}_fuzzid*/[qf]*" >host${s}.tgz + for host in `cat HOSTLIST`; do + ssh user@$host "tar -czf - sync/$host_main*/" > $host.tgz done ``` - Distributes and unpacks these files on all the remaining machines, e.g.: ```sh - for s in {1..10}; do - for d in {1..10}; do + for srchost in `cat HOSTLIST`; do + for dsthost in `cat HOSTLIST`; do test "$s" = "$d" && continue - ssh user@host${d} 'tar -kxzf -' ".sync_tmp/${host}.tgz" + "cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz" done @@ -80,7 +83,7 @@ while :; do echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ - "cd '$SYNC_DIR' && tar -xkzf -" <".sync_tmp/${src_host}.tgz" + "cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz" done -- cgit 1.4.1 From 6c980e2a023a8bb0af63bb0319a7869f455954ed Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 12:56:14 +0200 Subject: rewrite documentation --- docs/custom_mutators.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 75dbea21..a128f587 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -94,8 +94,11 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): - `fuzz_count` (optional): - This method can be used to instruct afl-fuzz how often to perform a fuzz - attempt on this input data. + When a queue entry is selected to be fuzzed, afl-fuzz selects the number + of fuzzing attempts with this input based on a few factors. + If however the custom mutator wants to set this number instead on how often + it is called for a specific queue entry, use this function. + This function in mostly useful if **not** `AFL_CUSTOM_MUTATOR_ONLY` is used. - `fuzz` (optional): -- cgit 1.4.1 From 7fb72f10387979ac5e46fdfb8901e928901a94e7 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 31 Aug 2020 14:47:22 +0200 Subject: typos --- docs/parallel_fuzzing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index 14c237c1..0c4cd237 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -206,9 +206,9 @@ in I/O wait state can mess things up. You can use screen, nohup, tmux, or something equivalent to run remote instances of afl-fuzz. If you redirect the program's output to a file, it will automatically switch from a fancy UI to more limited status reports. There is -also basic machine-readable information always written to the fuzzer_stats file -in the output directory. Locally, that information can be interpreted with -afl-whatsup. +also basic machine-readable information which is always written to the +fuzzer_stats file in the output directory. Locally, that information can be +interpreted with afl-whatsup. In principle, you can use the status screen of the main (-M) instance to monitor the overall fuzzing progress and decide when to stop. In this -- cgit 1.4.1 From 8253f9013418f2b8e94768c16a33e63a92e8e440 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Mon, 31 Aug 2020 15:16:55 +0200 Subject: typos/wording --- docs/env_variables.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 7d4f6716..d377e83f 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -10,8 +10,8 @@ Because they can't directly accept command-line options, the compile-time tools make fairly broad use of environmental variables: - - Most afl tools do not print any ouput if stout/stderr are redirected. - If you want to have the output into a file then set the AFL_DEBUG + - Most afl tools do not print any output if stdout/stderr are redirected. + If you want to save the output in a file then set the AFL_DEBUG environment variable. This is sadly necessary for various build processes which fail otherwise. @@ -44,7 +44,7 @@ tools make fairly broad use of environmental variables: you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) - - Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the + - Setting AFL_INST_RATIO to a percentage between 0% and 100% controls the probability of instrumenting every branch. This is (very rarely) useful when dealing with exceptionally complex programs that saturate the output bitmap. Examples include v8, ffmpeg, and perl. @@ -88,7 +88,7 @@ of the settings discussed in section #1, with the exception of: - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are created. - - AFL_INST_RATIO, as we by default collision free instrumentation is used. + - AFL_INST_RATIO, as we by default use collision free instrumentation. Then there are a few specific features that are only available in llvm_mode: @@ -121,7 +121,7 @@ Then there are a few specific features that are only available in llvm_mode: None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). - These are used if several seperated instrumentation are performed which + These are used if several seperated instrumentations are performed which are then later combined. - AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given @@ -200,7 +200,7 @@ Then there are a few specific features that are only available in llvm_mode: ### INSTRUMENT LIST (selectively instrument files and functions) - This feature allows selectively instrumentation of the source + This feature allows selective instrumentation of the source - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or function will only instrument (or skip) those files that match the names @@ -371,10 +371,10 @@ checks or alter some of the more exotic semantics of the tool: - Setting AFL_MAX_DET_EXRAS will change the threshold at what number of elements in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will - kick off. In probabilistic mode, not all dictionary entires will be used all - of the times for fuzzing mutations to not make fuzzing slower by it. - The default count is `200` element. So for the 200 + 1st element, there is a - 1 in 201 chance, that one of the dictionary entry will not be used directly. + kick off. In probabilistic mode, not all dictionary entries will be used all + the times for fuzzing mutations in order not to slow down fuzzing. + The default count is `200` elements. So for the 200 + 1st element, there is a + 1 in 201 chance, that one of the dictionary entries will not be used directly. - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when @@ -385,14 +385,14 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. - - Setting AFL_MAX_DET_EXTRAS changes the count of dictionary enties/extras + - Setting AFL_MAX_DET_EXTRAS changes the count of dictionary entries/extras (default 200), after which the entries will be used probabilistically. So, if the dict/extras file (`-x`) contains more tokens than this threshold, not all of the tokens will be used in each fuzzing step, every time. Instead, there is a chance that the entry will be skipped during fuzzing. This makes sure that the fuzzer doesn't spend all its time only inserting the extras, but will still do other mutations. However, it decreases the - likelyhood for each token to be inserted, before the next queue entrie is fuzzed. + likelihood for each token to be inserted, before the next queue entry is fuzzed. Either way, all tokens will be used eventually, in a longer fuzzing campaign. - Outdated environment variables that are that not supported anymore: -- cgit 1.4.1 From 58cf030546b1fb2dbe9d5325c4e69c0611c4c35b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 16:34:57 +0200 Subject: fix for MacOS sudo --- GNUmakefile | 2 +- docs/parallel_fuzzing.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index c0614d4d..fb60f301 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -492,7 +492,7 @@ ifndef AFL_NO_X86 test_build: afl-gcc afl-as afl-showmap @echo "[*] Testing the CC wrapper and instrumentation output..." - @unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 ) + @unset AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_DEBUG=1 AFL_INST_RATIO=100 AFL_AS_FORCE_INSTRUMENT=1 AFL_PATH=. ./$(TEST_CC) $(CFLAGS) test-instr.c -o test-instr $(LDFLAGS) 2>&1 | grep 'afl-as' >/dev/null || (echo "Oops, afl-as did not get called from "$(TEST_CC)". This is normally achieved by "$(CC)" honoring the -B option."; exit 1 ) ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index 0c4cd237..12895ac3 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -195,7 +195,7 @@ to keep in mind: - Syncing is only necessary for the main nodes on a system. It is possible to run main-less with only secondaries. However then you need to find out which secondary took over the temporary role to be the main node. Look for - the `is_main` file in the fuzzer directories, eg. `sync-dir/hostname-*/is_main` + the `is_main_node` file in the fuzzer directories, eg. `sync-dir/hostname-*/is_main_node` It is *not* advisable to skip the synchronization script and run the fuzzers directly on a network filesystem; unexpected latency and unkillable processes -- cgit 1.4.1 From 192cadee3485eef1c69ca662323ac3e50669aee9 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 18:10:18 +0200 Subject: fix docs --- docs/env_variables.md | 7 ------- 1 file changed, 7 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index d377e83f..c47d10e8 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -369,13 +369,6 @@ checks or alter some of the more exotic semantics of the tool: for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. - - Setting AFL_MAX_DET_EXRAS will change the threshold at what number of elements - in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will - kick off. In probabilistic mode, not all dictionary entries will be used all - the times for fuzzing mutations in order not to slow down fuzzing. - The default count is `200` elements. So for the 200 + 1st element, there is a - 1 in 201 chance, that one of the dictionary entries will not be used directly. - - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when working with unruly libraries that create threads or do other crazy -- cgit 1.4.1 From e4de4e350009584f5cdb8cf4c47a79fff9358cad Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 18:32:01 +0200 Subject: update gitignore --- .gitignore | 1 + docs/Changelog.md | 1 + llvm_mode/afl-clang-fast.c | 4 ++-- src/afl-showmap.c | 8 ++++---- src/afl-tmin.c | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/.gitignore b/.gitignore index 4307fc4c..9c169c49 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ afl-cmin.8 afl-cmin.bash.8 afl-fuzz.8 afl-gcc.8 +afl-g++.8 afl-gcc-fast.8 afl-g++-fast.8 afl-gotcpu.8 diff --git a/docs/Changelog.md b/docs/Changelog.md index cb6e14b8..3966464e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++2.67d (develop) + - a few QOL changes for Apple and its outdated gmake - afl-fuzz: - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 281d6b4b..ccdbca9d 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -670,11 +670,11 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#ifndef __APPLE__ + #ifndef __APPLE__ if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); -#endif + #endif #endif diff --git a/src/afl-showmap.c b/src/afl-showmap.c index ae33cc48..f4a7c336 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -636,10 +636,10 @@ static void usage(u8 *argv0) { "size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - "AFL_QUIET: do not print extra informational output\n" - "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" - - ,argv0, MEM_LIMIT, doc_path); + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " + "startup (in milliseconds)\n" + "AFL_QUIET: do not print extra informational output\n", + argv0, MEM_LIMIT, doc_path); exit(1); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index f231cde9..e1d08054 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -846,7 +846,7 @@ static void usage(u8 *argv0) { " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" - "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); -- cgit 1.4.1 From 338638b124f46ac9fda25efc0060910a781d199c Mon Sep 17 00:00:00 2001 From: ploppelop <70337824+ploppelop@users.noreply.github.com> Date: Mon, 31 Aug 2020 18:34:27 +0200 Subject: Update parallel_fuzzing.md fix multisystem example --- docs/parallel_fuzzing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index 12895ac3..bf57ace8 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -146,7 +146,7 @@ write a simple script that performs two actions: ```sh for srchost in `cat HOSTLIST`; do for dsthost in `cat HOSTLIST`; do - test "$s" = "$d" && continue + test "$srchost" = "$dsthost" && continue ssh user@$srchost 'tar -kxzf -' < $dsthost.tgz done done -- cgit 1.4.1 From a552631d3b04da880f18a25860169ac4ccd8f85b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 31 Aug 2020 20:22:20 +0200 Subject: update changelog --- docs/Changelog.md | 2 ++ 1 file changed, 2 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 3966464e..72c8952c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,8 @@ sending a mail to . dict entries without recompiling. - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for the forkserver to come up without the need to increase the overall timeout. + - bugfix for cmplog that results in a heap overflow based on target data + (thanks to the magma team for reporting!) - custom mutators: - added afl_custom_fuzz_count/fuzz_count function to allow specifying the number of fuzz attempts for custom_fuzz -- cgit 1.4.1 From ed6243df5a284c0e915d577ef45e80218e07907c Mon Sep 17 00:00:00 2001 From: hexcoder Date: Tue, 1 Sep 2020 00:49:26 +0200 Subject: Review FAQ.md --- docs/FAQ.md | 95 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 46 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index 93a87a72..df5cc79c 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -4,11 +4,11 @@ * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - * [How do I fuzz a network service?](#how-to-fuzz-a-network-service) - * [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) + * [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program) * [What is an edge?](#what-is-an-edge) * [Why is my stability below 100%?](#why-is-my-stability-below-100) - * [How can I improve the stability value](#how-can-i-improve-the-stability-value) + * [How can I improve the stability value?](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) @@ -18,51 +18,54 @@ If you find an interesting or important question missing, submit it via American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in 2013/2014, and when he left Google end of 2017 he stopped developing it. -At the end of 2019 the Google fuzzing team took over maintance of AFL, however -it is only accepting PR from the community and is not developing enhancements +At the end of 2019 the Google fuzzing team took over maintenance of AFL, however +it is only accepting PRs from the community and is not developing enhancements anymore. -In the second quarter of 2019, 1 1/2 years after no further development of -AFL had happened and it became clear there would be none coming, afl++ -was born, where initially first community patches were collected and applied -for bugs and enhancements. Then from various AFL spin-offs - mostly academic +In the second quarter of 2019, 1 1/2 year later when no further development of +AFL had happened and it became clear there would none be coming, afl++ +was born, where initially community patches were collected and applied +for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic research - features were integrated. This already resulted in a much advanced AFL. Until the end of 2019 the afl++ team had grown to four active developers which -then implemented their own research and feature, making it now by far the most +then implemented their own research and features, making it now by far the most flexible and feature rich guided fuzzer available as open source. And in independent fuzzing benchmarks it is one of the best fuzzers available, e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) -## How to improve the fuzzing speed +## How to improve the fuzzing speed? - 1. use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) + 1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) 2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) 3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) - 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) - 5. Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) + 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) + 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) ## How do I fuzz a network service? -The short answer is - you cannot, at least "out of the box". +The short answer is - you cannot, at least not "out of the box". -Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, -and finally usually it is more than one initial data packet but a back-and-forth -which is totally unsupported by most coverage aware fuzzers. +Using a network channel is inadequate for several reasons: +- it has a slow-down of x10-20 on the fuzzing speed +- it does not scale to multiple connections, +- instead of one initial data packet often a back-and-forth +interplay of packets is needed for stateful protocols +(which is totally unsupported by most coverage aware fuzzers). The established method to fuzz network services is to modify the source code to read from a file or stdin (fd 0) (or even faster via shared memory, combine this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) and you have a performance gain of x10 instead of a performance loss of over -x10 - that is a x100 difference! +x10 - that is a x100 difference!). If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD -to emulate the network. This is also much faster than network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/) +to emulate the network. This is also much faster than the real network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/). There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - @@ -73,7 +76,7 @@ which allows you to define network state with different type of data packets. If the GUI program can read the fuzz data from a file (via the command line, a fixed location or via an environment variable) without needing any user -interaction then then yes. +interaction then it would be suitable for fuzzing. Otherwise it is not possible without modifying the source code - which is a very good idea anyway as the GUI functionality is a huge CPU/time overhead @@ -82,13 +85,13 @@ for the fuzzing. So create a new `main()` that just reads the test case and calls the functionality for processing the input that the GUI program is using. -## What is an "edge" +## What is an "edge"? A program contains `functions`, `functions` contain the compiled machine code. The compiled machine code in a `function` can be in a single or many `basic blocks`. A `basic block` is the largest possible number of subsequent machine code -instructions that runs independent, meaning it does not split up to different -locations nor is it jumped into it from a different location: +instructions that has exactly one entry (at the beginning) and runs linearly without +branching or jumping to other addresses (except at the end). ``` function() { A: @@ -98,7 +101,7 @@ function() { if (x) goto C; else goto D; C: some code - goto D + goto E D: some code goto B @@ -108,7 +111,7 @@ function() { ``` Every code block between two jump locations is a `basic block`. -An `edge` is then the unique relationship between two `basic blocks` (from the +An `edge` is then the unique relationship between two directly connected `basic blocks` (from the code example above): ``` Block A @@ -124,7 +127,7 @@ code example above): ``` Every line between two blocks is an `edge`. -## Why is my stability below 100% +## Why is my stability below 100%? Stability is measured by how many percent of the edges in the target are "stable". Sending the same input again and again should take the exact same @@ -132,37 +135,37 @@ path through the target every time. If that is the case, the stability is 100%. If however randomness happens, e.g. a thread reading other external data, reaction to timing, etc. then in some of the re-executions with the same data -the result in the edge information will be different accross runs. +the edge coverage result will be different accross runs. Those edges that change are then flagged "unstable". The more "unstable" edges, the more difficult for afl++ to identify valid new paths. A value above 90% is usually fine and a value above 80% is also still ok, and -even above 20% can still result in successful finds of bugs. -However, it is recommended that below 90% or 80% you should take measures to -improve the stability. +even a value above 20% can still result in successful finds of bugs. +However, it is recommended that for values below 90% or 80% you should take +countermeasures to improve stability. -## How can I improve the stability value +## How can I improve the stability value? -For fuzzing a 100% stable target that covers all edges is the best. +For fuzzing a 100% stable target that covers all edges is the best case. A 90% stable target that covers all edges is however better than a 100% stable target that ignores 10% of the edges. With instability you basically have a partial coverage loss on an edge, with -ignore you have a full loss on that edge. +ignored functions you have a full loss on that edges. There are functions that are unstable, but also provide value to coverage, eg init functions that use fuzz data as input for example. -If however it is a function that has nothing to do with the input data is the -source, e.g. checking jitter, or is a hash map function etc. then it should -not be instrumented. +If however a function that has nothing to do with the input data is the +source of instability, e.g. checking jitter, or is a hash map function etc. +then it should not be instrumented. -To be able to make this decision the following process will allow you to -identify the functions with variable edges so you can make this decision. +To be able to exclude these functions (based on AFL++'s measured stability) +the following process will allow to identify functions with variable edges. -Four steps are required to do this and requires quite some knowledge of -coding and/or disassembly and it is only effectively possible with +Four steps are required to do this and it also requires quite some knowledge +of coding and/or disassembly and is effectively possible only with afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -171,7 +174,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. The out/fuzzer_stats file will then show the edge IDs that were identified as unstable. - 2. Second step: Find the responsible function. + 2. Second step: Find the responsible function(s). a) For LTO instrumented binaries this can be documented during compile time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`. @@ -191,20 +194,20 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. and set a write breakpoint to that address (`watch 0x.....`). c) in all other instrumentation types this is not possible. So just - recompile with the the two mentioned above. This is just for + recompile with the two mentioned above. This is just for identifying the functions that have unstable edges. 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to remove from instrumentation, or just specify the functions you want to - skip instrumenting. Note that optimization might inline functions! + skip for instrumentation. Note that optimization might inline functions! Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) - Only deny those functions from instrumentation that provide no value + Only exclude those functions from instrumentation that provide no value for coverage - that is if it does not process any fuzz data directly or indirectly (e.g. hash maps, thread management etc.). If however a function directly or indirectly handles fuzz data then you -- cgit 1.4.1 From 651ad18e217992a2befbceff1c44dd5d495ab9c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 1 Sep 2020 10:52:39 +0200 Subject: added the grammar mutator as a git submodule + documentation --- .gitmodules | 6 +++++- custom_mutators/README.md | 16 ++++++++++++++++ docs/Changelog.md | 16 ++++++++++------ 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/.gitmodules b/.gitmodules index 80752342..a9c181da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "unicorn_mode/unicornafl"] path = unicorn_mode/unicornafl - url = https://github.com/AFLplusplus/unicornafl.git + url = https://github.com/AFLplusplus/unicornafl + +[submodule "custom_mutators/Grammar-Mutator"] + path = custom_mutators/Grammar-Mutator + url = https://github.com/AFLplusplus/Grammar-Mutator diff --git a/custom_mutators/README.md b/custom_mutators/README.md index 3abcfef3..993ccaa1 100644 --- a/custom_mutators/README.md +++ b/custom_mutators/README.md @@ -3,6 +3,22 @@ Custom mutators enhance and alter the mutation strategies of afl++. For further information and documentation on how to write your own, read [the docs](../docs/custom_mutators.md). +## The afl++ Grammar Mutator + +If you use git to clone afl++, then the following will incorporate our +excellent grammar custom mutator: +``` +git submodule init +git submodule update +``` + +otherwise just checkout the repository here with either +`git clone https://github.com/AFLplusplus/Grammar-Mutator` or +`svn co https://github.com/AFLplusplus/Grammar-Mutator`. + +Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use +it. + ## Production-Ready Custom Mutators This directory holds ready to use custom mutators. diff --git a/docs/Changelog.md b/docs/Changelog.md index 72c8952c..d1ee9656 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,20 +10,24 @@ sending a mail to . ### Version ++2.67d (develop) + - added the GSoC excellent afl++ grammar mutator by Shengtuo to our + custom_mutators/ (see custom_mutators/README.md) - or get it here: + https://github.com/AFLplusplus/Grammar-Mutator - a few QOL changes for Apple and its outdated gmake - afl-fuzz: - Fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary - added total execs done to plot file - - AFL_MAX_DET_EXTRAS env variable added to control the amount of deterministic - dict entries without recompiling. - - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait for - the forkserver to come up without the need to increase the overall timeout. + - AFL_MAX_DET_EXTRAS env variable added to control the amount of + deterministic dict entries without recompiling. + - AFL_FORKSRV_INIT_TMOUT env variable added to control the time to wait + for the forkserver to come up without the need to increase the overall + timeout. - bugfix for cmplog that results in a heap overflow based on target data (thanks to the magma team for reporting!) - custom mutators: - - added afl_custom_fuzz_count/fuzz_count function to allow specifying the - number of fuzz attempts for custom_fuzz + - added afl_custom_fuzz_count/fuzz_count function to allow specifying + the number of fuzz attempts for custom_fuzz - llvm_mode: - Ported SanCov to LTO, and made it the default for LTO. better instrumentation locations -- cgit 1.4.1 From 020b8a49648ec5f58f44dc104f3a3c83624a41d1 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 2 Sep 2020 20:19:49 +0200 Subject: minor FAQ fixes --- docs/FAQ.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index df5cc79c..064638f4 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -41,7 +41,7 @@ e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) 2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) 3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) - 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) + 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure) 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) @@ -51,10 +51,8 @@ The short answer is - you cannot, at least not "out of the box". Using a network channel is inadequate for several reasons: - it has a slow-down of x10-20 on the fuzzing speed -- it does not scale to multiple connections, -- instead of one initial data packet often a back-and-forth -interplay of packets is needed for stateful protocols -(which is totally unsupported by most coverage aware fuzzers). +- it does not scale to fuzzing multiple instances easily, +- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers). The established method to fuzz network services is to modify the source code to read from a file or stdin (fd 0) (or even faster via shared memory, combine @@ -90,8 +88,8 @@ functionality for processing the input that the GUI program is using. A program contains `functions`, `functions` contain the compiled machine code. The compiled machine code in a `function` can be in a single or many `basic blocks`. A `basic block` is the largest possible number of subsequent machine code -instructions that has exactly one entry (at the beginning) and runs linearly without -branching or jumping to other addresses (except at the end). +instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks) +and runs linearly without branching or jumping to other addresses (except at the end). ``` function() { A: @@ -126,6 +124,7 @@ code example above): Block E ``` Every line between two blocks is an `edge`. +Note that a few basic block loop to itself, this too would be an edge. ## Why is my stability below 100%? -- cgit 1.4.1 From 6c715f1a69f91d4336023a8ba10fb4a7e126f9c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 17:04:42 +0200 Subject: more changes to fuzzer_setup --- docs/Changelog.md | 6 +++-- include/afl-fuzz.h | 2 +- include/common.h | 6 +++++ src/afl-common.c | 33 +++++++++++++++++++++++++ src/afl-fuzz-stats.c | 70 ++++++++++++++++++++++++++++------------------------ src/afl-fuzz.c | 3 ++- 6 files changed, 84 insertions(+), 36 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index d1ee9656..0d93ee1f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,7 +15,7 @@ sending a mail to . https://github.com/AFLplusplus/Grammar-Mutator - a few QOL changes for Apple and its outdated gmake - afl-fuzz: - - Fix for auto dictionary entries found during fuzzing to not throw out + - fix for auto dictionary entries found during fuzzing to not throw out a -x dictionary - added total execs done to plot file - AFL_MAX_DET_EXTRAS env variable added to control the amount of @@ -25,11 +25,13 @@ sending a mail to . timeout. - bugfix for cmplog that results in a heap overflow based on target data (thanks to the magma team for reporting!) + - write fuzzing setup into out/fuzzer_setup (environment variables and + command line) - custom mutators: - added afl_custom_fuzz_count/fuzz_count function to allow specifying the number of fuzz attempts for custom_fuzz - llvm_mode: - - Ported SanCov to LTO, and made it the default for LTO. better + - ported SanCov to LTO, and made it the default for LTO. better instrumentation locations - Further llvm 12 support (fast moving target like afl++ :-) ) - deprecated LLVM SKIPSINGLEBLOCK env environment diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e3c3d5aa..358cb5dc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -945,7 +945,7 @@ void destroy_extras(afl_state_t *); /* Stats */ -void write_fuzzer_config_file(afl_state_t *); +void write_setup_file(afl_state_t *, int, char **); void write_stats_file(afl_state_t *, double, double, double); void maybe_update_plot_file(afl_state_t *, double, double); void show_stats(afl_state_t *); diff --git a/include/common.h b/include/common.h index 87a7425b..c364ade0 100644 --- a/include/common.h +++ b/include/common.h @@ -110,5 +110,11 @@ u8 *u_stringify_time_diff(u8 *buf, u64 cur_ms, u64 event_ms); /* Reads the map size from ENV */ u32 get_map_size(void); +/* create a stream file */ +FILE *create_ffile(u8 *fn); + +/* create a file */ +s32 create_file(u8 *fn); + #endif diff --git a/src/afl-common.c b/src/afl-common.c index 367dec72..d66440aa 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -877,3 +877,36 @@ u32 get_map_size(void) { } +/* Create a stream file */ + +FILE *create_ffile(u8 *fn) { + + s32 fd; + FILE *f; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + f = fdopen(fd, "w"); + + if (!f) { PFATAL("fdopen() failed"); } + + return f; + +} + +/* Create a file */ + +s32 create_file(u8 *fn) { + + s32 fd; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) { PFATAL("Unable to create '%s'", fn); } + + return fd; + +} + diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index b59a40e4..a84f1c7a 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -27,51 +27,57 @@ #include "envs.h" #include -/* Open file for writing */ - -inline FILE *open_file(const char *fn) { - - s32 fd; - FILE *f; - - fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); +/* Write fuzzer setup file */ - if (fd < 0) { PFATAL("Unable to create '%s'", fn); } +void write_setup_file(afl_state_t *afl, int argc, char **argv) { - f = fdopen(fd, "w"); + char *val; + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/fuzzer_setup", afl->out_dir); + FILE *f = create_ffile(fn); - if (!f) { PFATAL("fdopen() failed"); } + fprintf(f, "# environment variables:\n"); + u32 s_afl_env = + sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - + 1; + for (u32 i = 0; i < s_afl_env; i++) { - return f; + if ((val = getenv(afl_environment_variables[i])) != NULL) { -} + fprintf(f, "%s=%s\n", afl_environment_variables[i], val); -/* Write fuzzer setup file */ + } -void write_fuzzer_config_file(afl_state_t *afl) { + } - u8 fn[PATH_MAX]; - FILE *f; + fprintf(f, "# command line:\n"); - snprintf(fn, PATH_MAX, "%s/fuzzer_config", afl->out_dir); - f = open_file(fn); + s32 i; + size_t j; + for (i = 0; i < argc; i++) { - char *val; + if (i) fprintf(f, " "); + if (index(argv[i], '\'')) { - uint32_t s_afl_env = - sizeof(afl_environment_variables) / sizeof(afl_environment_variables[0]) - - 1; - for (uint32_t i = 0; i < s_afl_env; i++) { + fprintf(f, "'"); + for (j = 0; j < strlen(argv[i]); j++) + if (argv[i][j] == '\'') + fprintf(f, "'\"'\"'"); + else + fprintf(f, "%c", argv[i][j]); + fprintf(f, "'"); - if ((val = getenv(afl_environment_variables[i])) != NULL) { + } else { - fprintf(f, "%s=%s\n", afl_environment_variables[i], val); + fprintf(f, "'%s'", argv[i]); } } + fprintf(f, "\n"); fclose(f); + (void)(afl_environment_deprecated); } @@ -84,13 +90,13 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, struct rusage rus; #endif - unsigned long long int cur_time = get_cur_time(); - u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); - u8 fn[PATH_MAX]; - FILE * f; + u64 cur_time = get_cur_time(); + u32 t_bytes = count_non_255_bytes(afl, afl->virgin_bits); + u8 fn[PATH_MAX]; + FILE *f; snprintf(fn, PATH_MAX, "%s/fuzzer_stats", afl->out_dir); - f = open_file(fn); + f = create_ffile(fn); /* Keep last values in case we're called from another context where exec/sec stats and such are not readily available. */ @@ -209,7 +215,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability, if (afl->debug) { - uint32_t i = 0; + u32 i = 0; fprintf(f, "virgin_bytes :"); for (i = 0; i < afl->fsrv.map_size; i++) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e9ea8b62..c12d5db5 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1128,6 +1128,8 @@ int main(int argc, char **argv_orig, char **envp) { setup_custom_mutators(afl); + write_setup_file(afl, argc, argv); + setup_cmdline_file(afl, argv + optind); read_testcases(afl); @@ -1274,7 +1276,6 @@ int main(int argc, char **argv_orig, char **envp) { seek_to = find_start_position(afl); - write_fuzzer_config_file(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); save_auto(afl); -- cgit 1.4.1 From fac108476c1cb5326cf4339b2a4c846828698816 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 4 Sep 2020 22:50:45 +0200 Subject: v2.68c --- README.md | 4 ++-- docs/Changelog.md | 2 +- include/config.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 7b73a5f3..4cad6b47 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ![Travis State](https://api.travis-ci.com/AFLplusplus/AFLplusplus.svg?branch=stable) - Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [2.68c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 2.67d + Github Version: 3.00a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index 0d93ee1f..6321aee4 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++2.67d (develop) +### Version ++2.68c (release) - added the GSoC excellent afl++ grammar mutator by Shengtuo to our custom_mutators/ (see custom_mutators/README.md) - or get it here: https://github.com/AFLplusplus/Grammar-Mutator diff --git a/include/config.h b/include/config.h index 6c73bced..77407d50 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.67d" +#define VERSION "++2.68c" /****************************************************** * * -- cgit 1.4.1 From e30b2c6af6e369844c92c00a20ebdd53473a747c Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 5 Sep 2020 13:18:28 +0200 Subject: final changes for pre-3.0 --- .gitignore | 6 ++ Android.bp | 6 +- GNUmakefile | 3 +- GNUmakefile.gcc_plugin | 1 + GNUmakefile.llvm | 8 ++- README.md | 2 +- docs/Changelog.md | 14 ++++ docs/FAQ.md | 104 +++++++++++++++--------------- docs/INSTALL.md | 19 +++--- docs/env_variables.md | 121 ++++++++++++++++++----------------- docs/ideas.md | 57 ----------------- docs/life_pro_tips.md | 4 +- docs/perf_tips.md | 8 +-- docs/sister_projects.md | 4 +- docs/status_screen.md | 2 +- examples/README.md | 2 +- examples/aflpp_driver/aflpp_driver.c | 2 +- include/afl-fuzz.h | 3 +- include/config.h | 4 +- include/envs.h | 1 + qemu_mode/patches/afl-qemu-cpu-inl.h | 2 +- src/afl-fuzz-init.c | 3 +- src/afl-fuzz-queue.c | 13 ++-- src/afl-fuzz-stats.c | 8 +-- src/afl-fuzz.c | 3 +- test/test-gcc-plugin.sh | 2 +- test/test-unittests.sh | 2 + 27 files changed, 188 insertions(+), 216 deletions(-) (limited to 'docs') diff --git a/.gitignore b/.gitignore index 0527a0b2..e3adb6ef 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,12 @@ afl-showmap.8 afl-system-config.8 afl-tmin.8 afl-whatsup.8 +afl-c++ +afl-cc +afl-lto +afl-lto++ +afl-lto++.8 +afl-lto.8 qemu_mode/libcompcov/compcovtest qemu_mode/qemu-* unicorn_mode/samples/*/\.test-* diff --git a/Android.bp b/Android.bp index e59129db..2c2114b2 100644 --- a/Android.bp +++ b/Android.bp @@ -101,7 +101,7 @@ cc_binary_host { ], srcs: [ - "llvm_mode/afl-clang-fast.c", + "src/afl-cc.c", ], } @@ -119,7 +119,7 @@ cc_binary_host { ], srcs: [ - "llvm_mode/afl-clang-fast.c", + "src/afl-cc.c", ], } @@ -136,6 +136,6 @@ cc_library_static { ], srcs: [ - "llvm_mode/afl-llvm-rt.o.c", + "instrumentation/afl-llvm-rt.o.c", ], } diff --git a/GNUmakefile b/GNUmakefile index 0046a481..7455483c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -533,7 +533,7 @@ endif deepclean: clean rm -rf qemu_mode/qemu-3.1.1.tar.xz rm -rf unicorn_mode/unicornafl - git reset --hard >/dev/null 2>&1 || true + # NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true .PHONY: distrib distrib: all @@ -591,6 +591,7 @@ install: all $(MANPAGES) @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) @rm -f $${DESTDIR}$(BIN_PATH)/afl-plot.sh @rm -f $${DESTDIR}$(BIN_PATH)/afl-as + @rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-32.o $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt-64.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH) @if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi @if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index aeb1ef16..b73fcfda 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -158,6 +158,7 @@ vpath % .. install: all ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc-fast ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast + ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH) install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d76e0b28..1bb3d265 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -423,10 +423,12 @@ document: ./afl-compiler-rt-32.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e afl-compiler-rt-32.o && ln -sf afl-compiler-rt-32.o afl-llvm-rt-64.o ./afl-compiler-rt-64.o: instrumentation/afl-compiler-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @test -e afl-compiler-rt-64.o && ln -sf afl-compiler-rt-64.o afl-llvm-rt-64.o .PHONY: test_build test_build: $(PROGS) @@ -448,11 +450,11 @@ all_done: test_build install: all @install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(HELPER_PATH) $${DESTDIR}$(DOC_PATH) $${DESTDIR}$(MISC_PATH) @if [ -f ./afl-cc ]; then set -e; install -m 755 ./afl-cc $${DESTDIR}$(BIN_PATH); ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-c++; fi - @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-compiler-rt.o ]; then set -e; install -m 755 ./afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt.o ;fi @if [ -f ./afl-lto ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-lto++; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto++; install -m 755 ./afl-llvm-lto-instrumentation.so ./afl-llvm-rt-lto*.o ./afl-llvm-lto-instrumentlist.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./afl-ld-lto ]; then set -e; install -m 755 ./afl-ld-lto $${DESTDIR}$(BIN_PATH); fi - @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); fi - @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); fi + @if [ -f ./afl-compiler-rt-32.o ]; then set -e; install -m 755 ./afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-32.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt-32.o ;fi + @if [ -f ./afl-compiler-rt-64.o ]; then set -e; install -m 755 ./afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH); ln -sf afl-compiler-rt-64.o $${DESTDIR}$(HELPER_PATH)afl-llvm-rt-64.o ; fi @if [ -f ./compare-transform-pass.so ]; then set -e; install -m 755 ./*.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f ./compare-transform-pass.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-fast ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-fast++ ; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang++ ; fi @if [ -f ./SanitizerCoverageLTO.so ]; then set -e; ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-clang-lto ; ln -sf ./afl-c++ $${DESTDIR}$(BIN_PATH)/afl-clang-lto++ ; fi diff --git a/README.md b/README.md index 96b34260..c886489d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Release Version: [2.67c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 2.67d + Github Version: 3.00a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index 6321aee4..9de03e78 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,20 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++3.00a (develop) + - llvm_mode/ and gcc_plugin/ moved to instrumentation/ + - all compilers combined to afl-cc which emulates the previous ones + - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o + - afl-fuzz + - reading testcases from -i now descends into subdirectories + - allow up to 4 -x command line options + - loaded extras now have a duplicate protection + - instrumentation + - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz + -x dictionary of string comparisons found during compilation + - not overriding -Ox or -fno-unroll-loops anymore + + ### Version ++2.68c (release) - added the GSoC excellent afl++ grammar mutator by Shengtuo to our custom_mutators/ (see custom_mutators/README.md) - or get it here: diff --git a/docs/FAQ.md b/docs/FAQ.md index 064638f4..24942492 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -4,11 +4,11 @@ * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - * [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service) - * [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program) + * [How do I fuzz a network service?](#how-to-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) * [What is an edge?](#what-is-an-edge) * [Why is my stability below 100%?](#why-is-my-stability-below-100) - * [How can I improve the stability value?](#how-can-i-improve-the-stability-value) + * [How can I improve the stability value](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) @@ -18,52 +18,51 @@ If you find an interesting or important question missing, submit it via American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in 2013/2014, and when he left Google end of 2017 he stopped developing it. -At the end of 2019 the Google fuzzing team took over maintenance of AFL, however -it is only accepting PRs from the community and is not developing enhancements +At the end of 2019 the Google fuzzing team took over maintance of AFL, however +it is only accepting PR from the community and is not developing enhancements anymore. -In the second quarter of 2019, 1 1/2 year later when no further development of -AFL had happened and it became clear there would none be coming, afl++ -was born, where initially community patches were collected and applied -for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic +In the second quarter of 2019, 1 1/2 years after no further development of +AFL had happened and it became clear there would be none coming, afl++ +was born, where initially first community patches were collected and applied +for bugs and enhancements. Then from various AFL spin-offs - mostly academic research - features were integrated. This already resulted in a much advanced AFL. Until the end of 2019 the afl++ team had grown to four active developers which -then implemented their own research and features, making it now by far the most +then implemented their own research and feature, making it now by far the most flexible and feature rich guided fuzzer available as open source. And in independent fuzzing benchmarks it is one of the best fuzzers available, e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) -## How to improve the fuzzing speed? +## How to improve the fuzzing speed - 1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) - 2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) + 1. use [instrumentation](docs/README.llvm.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) + 2. Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase) 3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) - 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) - 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure) + 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) + 5. Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) ## How do I fuzz a network service? -The short answer is - you cannot, at least not "out of the box". +The short answer is - you cannot, at least "out of the box". -Using a network channel is inadequate for several reasons: -- it has a slow-down of x10-20 on the fuzzing speed -- it does not scale to fuzzing multiple instances easily, -- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers). +Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, +and finally usually it is more than one initial data packet but a back-and-forth +which is totally unsupported by most coverage aware fuzzers. The established method to fuzz network services is to modify the source code to read from a file or stdin (fd 0) (or even faster via shared memory, combine -this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) +this with persistent mode [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) and you have a performance gain of x10 instead of a performance loss of over -x10 - that is a x100 difference!). +x10 - that is a x100 difference! If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD -to emulate the network. This is also much faster than the real network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/). +to emulate the network. This is also much faster than network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/) There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - @@ -74,7 +73,7 @@ which allows you to define network state with different type of data packets. If the GUI program can read the fuzz data from a file (via the command line, a fixed location or via an environment variable) without needing any user -interaction then it would be suitable for fuzzing. +interaction then then yes. Otherwise it is not possible without modifying the source code - which is a very good idea anyway as the GUI functionality is a huge CPU/time overhead @@ -83,13 +82,13 @@ for the fuzzing. So create a new `main()` that just reads the test case and calls the functionality for processing the input that the GUI program is using. -## What is an "edge"? +## What is an "edge" A program contains `functions`, `functions` contain the compiled machine code. The compiled machine code in a `function` can be in a single or many `basic blocks`. A `basic block` is the largest possible number of subsequent machine code -instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks) -and runs linearly without branching or jumping to other addresses (except at the end). +instructions that runs independent, meaning it does not split up to different +locations nor is it jumped into it from a different location: ``` function() { A: @@ -99,7 +98,7 @@ function() { if (x) goto C; else goto D; C: some code - goto E + goto D D: some code goto B @@ -109,7 +108,7 @@ function() { ``` Every code block between two jump locations is a `basic block`. -An `edge` is then the unique relationship between two directly connected `basic blocks` (from the +An `edge` is then the unique relationship between two `basic blocks` (from the code example above): ``` Block A @@ -124,9 +123,8 @@ code example above): Block E ``` Every line between two blocks is an `edge`. -Note that a few basic block loop to itself, this too would be an edge. -## Why is my stability below 100%? +## Why is my stability below 100% Stability is measured by how many percent of the edges in the target are "stable". Sending the same input again and again should take the exact same @@ -134,37 +132,37 @@ path through the target every time. If that is the case, the stability is 100%. If however randomness happens, e.g. a thread reading other external data, reaction to timing, etc. then in some of the re-executions with the same data -the edge coverage result will be different accross runs. +the result in the edge information will be different accross runs. Those edges that change are then flagged "unstable". The more "unstable" edges, the more difficult for afl++ to identify valid new paths. A value above 90% is usually fine and a value above 80% is also still ok, and -even a value above 20% can still result in successful finds of bugs. -However, it is recommended that for values below 90% or 80% you should take -countermeasures to improve stability. +even above 20% can still result in successful finds of bugs. +However, it is recommended that below 90% or 80% you should take measures to +improve the stability. -## How can I improve the stability value? +## How can I improve the stability value -For fuzzing a 100% stable target that covers all edges is the best case. +For fuzzing a 100% stable target that covers all edges is the best. A 90% stable target that covers all edges is however better than a 100% stable target that ignores 10% of the edges. With instability you basically have a partial coverage loss on an edge, with -ignored functions you have a full loss on that edges. +ignore you have a full loss on that edge. There are functions that are unstable, but also provide value to coverage, eg init functions that use fuzz data as input for example. -If however a function that has nothing to do with the input data is the -source of instability, e.g. checking jitter, or is a hash map function etc. -then it should not be instrumented. +If however it is a function that has nothing to do with the input data is the +source, e.g. checking jitter, or is a hash map function etc. then it should +not be instrumented. -To be able to exclude these functions (based on AFL++'s measured stability) -the following process will allow to identify functions with variable edges. +To be able to make this decision the following process will allow you to +identify the functions with variable edges so you can make this decision. -Four steps are required to do this and it also requires quite some knowledge -of coding and/or disassembly and is effectively possible only with +Four steps are required to do this and requires quite some knowledge of +coding and/or disassembly and it is only effectively possible with afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -173,7 +171,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. The out/fuzzer_stats file will then show the edge IDs that were identified as unstable. - 2. Second step: Find the responsible function(s). + 2. Second step: Find the responsible function. a) For LTO instrumented binaries this can be documented during compile time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`. @@ -182,10 +180,10 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. b) For PCGUARD instrumented binaries it is much more difficult. Here you can either modify the __sanitizer_cov_trace_pc_guard function in - llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in + instrumentation/afl-llvm-rt.o.c to write a backtrace to a file if the ID in __afl_area_ptr[*guard] is one of the unstable edge IDs. (Example code is already there). - Then recompile and reinstall llvm_mode and rebuild your target. + Then recompile and reinstall instrumentation and rebuild your target. Run the recompiled target with afl-fuzz for a while and then check the file that you wrote with the backtrace information. Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init @@ -193,20 +191,20 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. and set a write breakpoint to that address (`watch 0x.....`). c) in all other instrumentation types this is not possible. So just - recompile with the two mentioned above. This is just for + recompile with the the two mentioned above. This is just for identifying the functions that have unstable edges. 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to remove from instrumentation, or just specify the functions you want to - skip for instrumentation. Note that optimization might inline functions! + skip instrumenting. Note that optimization might inline functions! - Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) + Simply follow this document on how to do this: [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) - Only exclude those functions from instrumentation that provide no value + Only deny those functions from instrumentation that provide no value for coverage - that is if it does not process any fuzz data directly or indirectly (e.g. hash maps, thread management etc.). If however a function directly or indirectly handles fuzz data then you diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 766f24d7..fb7b5642 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -24,7 +24,7 @@ There are no special dependencies to speak of; you will need GNU make and a working compiler (gcc or clang). Some of the optional scripts bundled with the program may depend on bash, gdb, and similar basic tools. -If you are using clang, please review llvm_mode/README.md; the LLVM +If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. @@ -52,10 +52,10 @@ sudo gmake install Keep in mind that if you are using csh as your shell, the syntax of some of the shell commands given in the README.md and other docs will be different. -The `llvm_mode` requires a dynamically linked, fully-operational installation of +The `llvm` requires a dynamically linked, fully-operational installation of clang. At least on FreeBSD, the clang binaries are static and do not include some of the essential tools, so if you want to make it work, you may need to -follow the instructions in llvm_mode/README.md. +follow the instructions in README.llvm.md. Beyond that, everything should work as advertised. @@ -97,27 +97,24 @@ and definitely don't look POSIX-compliant. This means two things: User emulation mode of QEMU does not appear to be supported on MacOS X, so black-box instrumentation mode (`-Q`) will not work. -The llvm_mode requires a fully-operational installation of clang. The one that +The llvm instrumentation requires a fully-operational installation of clang. The one that comes with Xcode is missing some of the essential headers and helper tools. -See llvm_mode/README.md for advice on how to build the compiler from scratch. +See README.llvm.md for advice on how to build the compiler from scratch. ## 4. Linux or *BSD on non-x86 systems Standard build will fail on non-x86 systems, but you should be able to leverage two other options: - - The LLVM mode (see llvm_mode/README.md), which does not rely on + - The LLVM mode (see README.llvm.md), which does not rely on x86-specific assembly shims. It's fast and robust, but requires a complete installation of clang. - The QEMU mode (see qemu_mode/README.md), which can be also used for fuzzing cross-platform binaries. It's slower and more fragile, but can be used even when you don't have the source for the tested app. -If you're not sure what you need, you need the LLVM mode. To get it, try: - -```bash -AFL_NO_X86=1 gmake && gmake -C llvm_mode -``` +If you're not sure what you need, you need the LLVM mode, which is built by +default. ...and compile your target program with afl-clang-fast or afl-clang-fast++ instead of the traditional afl-gcc or afl-clang wrappers. diff --git a/docs/env_variables.md b/docs/env_variables.md index c47d10e8..9d289f6d 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -5,13 +5,25 @@ users or for some types of custom fuzzing setups. See README.md for the general instruction manual. -## 1) Settings for afl-gcc, afl-clang, and afl-as - and gcc_plugin afl-gcc-fast +## 1) Settings for all compilers -Because they can't directly accept command-line options, the compile-time -tools make fairly broad use of environmental variables: +Starting with afl++ 3.0 there is only one compiler: afl-cc +To select the different instrumentation modes this can be done by + 1. passing --afl-MODE command line options to the compiler + 2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, + afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++, + afl-gcc-fast, afl-g++-fast + 3. using the environment variable AFL_CC_COMPILER with MODE - - Most afl tools do not print any output if stdout/stderr are redirected. - If you want to save the output in a file then set the AFL_DEBUG +MODE can one of LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN +(afl-g*-fast) or GCC (afl-gcc/afl-g++). + +Because beside the --afl-MODE command no afl specific command-line options +are accepted, the compile-time tools make fairly broad use of environmental +variables: + + - Most afl tools do not print any ouput if stout/stderr are redirected. + If you want to have the output into a file then set the AFL_DEBUG environment variable. This is sadly necessary for various build processes which fail otherwise. @@ -24,6 +36,8 @@ tools make fairly broad use of environmental variables: will cause problems in programs built with -Werror, simply because -O3 enables more thorough code analysis and can spew out additional warnings. To disable optimizations, set AFL_DONT_OPTIMIZE. + However if -O... and/or -fno-unroll-loops are set, these are not + overriden. - Setting AFL_USE_ASAN automatically enables ASAN, provided that your compiler supports that. Note that fuzzing with ASAN is mildly challenging @@ -44,7 +58,7 @@ tools make fairly broad use of environmental variables: you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) - - Setting AFL_INST_RATIO to a percentage between 0% and 100% controls the + - Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the probability of instrumenting every branch. This is (very rarely) useful when dealing with exceptionally complex programs that saturate the output bitmap. Examples include v8, ffmpeg, and perl. @@ -55,19 +69,16 @@ tools make fairly broad use of environmental variables: Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only the transitions between function entry points, but not individual branches. + Note that this is an outdated variable. A few instances (e.g. afl-gcc) + still support these, but state-of-the-art (e.g. LLVM LTO and LLVM PCGUARD) + do not need this. + - AFL_NO_BUILTIN causes the compiler to generate code suitable for use with libtokencap.so (but perhaps running a bit slower than without the flag). - TMPDIR is used by afl-as for temporary files; if this variable is not set, the tool defaults to /tmp. - - Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented - assembly files. Useful for troubleshooting problems or understanding how - the tool works. To get them in a predictable place, try something like: - - mkdir assembly_here - TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all - - If you are a weird person that wants to compile and instrument asm text files then use the AFL_AS_FORCE_INSTRUMENT variable: AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo @@ -78,19 +89,24 @@ tools make fairly broad use of environmental variables: - Setting AFL_CAL_FAST will speed up the initial calibration, if the application is very slow -## 2) Settings for afl-clang-fast / afl-clang-fast++ / afl-gcc-fast / afl-g++-fast +## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++ -The native instrumentation helpers (llvm_mode and gcc_plugin) accept a subset +The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset of the settings discussed in section #1, with the exception of: + - LLVM modes support `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` which will + write all constant string comparisons to this file to be used with + afl-fuzz' `-x` option. + - AFL_AS, since this toolchain does not directly invoke GNU as. - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are created. - - AFL_INST_RATIO, as we by default use collision free instrumentation. + - AFL_INST_RATIO, as we by default collision free instrumentation is used. + Not all passes support this option though as it is an outdated feature. -Then there are a few specific features that are only available in llvm_mode: +Then there are a few specific features that are only available in instrumentation: ### Select the instrumentation mode @@ -121,7 +137,7 @@ Then there are a few specific features that are only available in llvm_mode: None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). - These are used if several seperated instrumentations are performed which + These are used if several seperated instrumentation are performed which are then later combined. - AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given @@ -136,7 +152,7 @@ Then there are a few specific features that are only available in llvm_mode: - AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written into the instrumentation is set in a global variable - See llvm_mode/README.LTO.md for more information. + See instrumentation/README.LTO.md for more information. ### INSTRIM @@ -154,7 +170,7 @@ Then there are a few specific features that are only available in llvm_mode: afl-fuzz will only be able to see the path the loop took, but not how many times it was called (unless it is a complex loop). - See llvm_mode/README.instrim.md + See instrumentation/README.instrim.md ### NGRAM @@ -165,7 +181,7 @@ Then there are a few specific features that are only available in llvm_mode: config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See llvm_mode/README.ctx.md + See instrumentation/README.ctx.md ### CTX @@ -176,7 +192,7 @@ Then there are a few specific features that are only available in llvm_mode: config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See llvm_mode/README.ngram.md + See instrumentation/README.ngram.md ### LAF-INTEL @@ -196,17 +212,17 @@ Then there are a few specific features that are only available in llvm_mode: - Setting AFL_LLVM_LAF_ALL sets all of the above - See llvm_mode/README.laf-intel.md for more information. + See instrumentation/README.laf-intel.md for more information. ### INSTRUMENT LIST (selectively instrument files and functions) - This feature allows selective instrumentation of the source + This feature allows selectively instrumentation of the source - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or function will only instrument (or skip) those files that match the names listed in the specified file. - See llvm_mode/README.instrument_list.md for more information. + See instrumentation/README.instrument_list.md for more information. ### NOT_ZERO @@ -220,27 +236,34 @@ Then there are a few specific features that are only available in llvm_mode: test. If the target performs only few loops then this will give a small performance boost. - See llvm_mode/README.neverzero.md + See instrumentation/README.neverzero.md ### CMPLOG - Setting AFL_LLVM_CMPLOG=1 during compilation will tell afl-clang-fast to - produce a CmpLog binary. See llvm_mode/README.cmplog.md + produce a CmpLog binary. See instrumentation/README.cmplog.md - See llvm_mode/README.neverzero.md + See instrumentation/README.neverzero.md -Then there are a few specific features that are only available in the gcc_plugin: +## 3) Settings for GCC / GCC_PLUGIN modes -### INSTRUMENT_FILE +Then there are a few specific features that are only available in GCC and +GCC_PLUGIN mode. - This feature allows selective instrumentation of the source + - Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented + assembly files. Useful for troubleshooting problems or understanding how + the tool works. (GCC mode only) + To get them in a predictable place, try something like: - - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those - files that match the names listed in this file (one filename per line). + mkdir assembly_here + TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all + - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those + files that match the names listed in this file (one filename per line). See gcc_plugin/README.instrument_list.md for more information. + (GCC_PLUGIN mode only) -## 3) Settings for afl-fuzz +## 4) Settings for afl-fuzz The main fuzzer binary accepts several options that disable a couple of sanity checks or alter some of the more exotic semantics of the tool: @@ -278,14 +301,6 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. - - Setting AFL_FORKSRV_INIT_TMOUT allows yout to specify a different timeout - to wait for the forkserver to spin up. The default is the `-t` value times - `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the - default would wait `1000` milis. Setting a different time here is useful - if the target has a very slow startup time, for example when doing - full-system fuzzing or emulation, but you don't want the actual runs - to wait too long for timeouts. - - AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. @@ -377,22 +392,12 @@ checks or alter some of the more exotic semantics of the tool: Note that this setting inhibits some of the user-friendly diagnostics normally done when starting up the forkserver and causes a pretty significant performance drop. - - - Setting AFL_MAX_DET_EXTRAS changes the count of dictionary entries/extras - (default 200), after which the entries will be used probabilistically. - So, if the dict/extras file (`-x`) contains more tokens than this threshold, - not all of the tokens will be used in each fuzzing step, every time. - Instead, there is a chance that the entry will be skipped during fuzzing. - This makes sure that the fuzzer doesn't spend all its time only inserting - the extras, but will still do other mutations. However, it decreases the - likelihood for each token to be inserted, before the next queue entry is fuzzed. - Either way, all tokens will be used eventually, in a longer fuzzing campaign. - Outdated environment variables that are that not supported anymore: AFL_DEFER_FORKSRV AFL_PERSISTENT -## 4) Settings for afl-qemu-trace +## 5) Settings for afl-qemu-trace The QEMU wrapper used to instrument binary-only code supports several settings: @@ -446,7 +451,7 @@ The QEMU wrapper used to instrument binary-only code supports several settings: stack pointer in which QEMU can find the return address when `start addr` is hitted. -## 5) Settings for afl-cmin +## 6) Settings for afl-cmin The corpus minimization script offers very little customization: @@ -472,12 +477,12 @@ to match when minimizing crashes. This will make minimization less useful, but may prevent the tool from "jumping" from one crashing condition to another in very buggy software. You probably want to combine it with the -e flag. -## 7) Settings for afl-analyze +## 8) Settings for afl-analyze You can set AFL_ANALYZE_HEX to get file offsets printed as hexadecimal instead of decimal. -## 8) Settings for libdislocator +## 9) Settings for libdislocator The library honors these environmental variables: @@ -499,12 +504,12 @@ The library honors these environmental variables: - AFL_ALIGNED_ALLOC=1 will force the alignment of the allocation size to max_align_t to be compliant with the C standard. -## 9) Settings for libtokencap +## 10) Settings for libtokencap This library accepts AFL_TOKEN_FILE to indicate the location to which the discovered tokens should be written. -## 10) Third-party variables set by afl-fuzz & other tools +## 11) Third-party variables set by afl-fuzz & other tools Several variables are not directly interpreted by afl-fuzz, but are set to optimal values if not already present in the environment: diff --git a/docs/ideas.md b/docs/ideas.md index 65e2e8e6..a5d40963 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,49 +3,6 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. -For GSOC2020 interested students please see -[https://github.com/AFLplusplus/AFLplusplus/issues/208](https://github.com/AFLplusplus/AFLplusplus/issues/208) - -## Flexible Grammar Mutator (currently in development) - -Currently, AFL++'s mutation does not have deeper knowledge about the fuzzed -binary, apart from feedback, even though the developer may have insights -about the target. - -A developer may choose to provide dictionaries and implement own mutations -in python or C, but an easy mutator that behaves according to a given grammar, -does not exist. - -State-of-the-art research on grammar fuzzing has some problems in their -implementations like code quality, scalability, or ease of use and other -common issues of the academic code. - -We aim to develop a pluggable grammar mutator for afl++ that combines -various results. - -Mentor: andreafioraldi - -## perf-fuzz Linux Kernel Module - -Expand on [snapshot LKM](https://github.com/AFLplusplus/AFL-Snapshot-LKM) -To make it thread safe, can snapshot several processes at once and increase -overall performance. - -Mentor: any - -## QEMU 5-based Instrumentation - -First tests to use QEMU 4 for binary-only AFL++ showed that caching behavior -changed, which vastly decreases fuzzing speeds. - -In this task test if QEMU 5 performs better and port the afl++ QEMU 3.1 -patches to QEMU 5. - -Understanding the current instrumentation and fixing the current caching -issues will be needed. - -Mentor: andreafioraldi - ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. @@ -66,20 +23,6 @@ Either improve a single mutator thorugh learning of many different bugs Mentor: domenukk -## Reengineer `afl-fuzz` as Thread Safe, Embeddable Library (currently in development) - -Right now, afl-fuzz is single threaded, cannot safely be embedded in tools, -and not multi-threaded. It makes use of a large number of globals, must always -be the parent process and exec child processes. -Instead, afl-fuzz could be refactored to contain no global state and globals. -This allows for different use cases that could be implemented during this -project. -Note that in the mean time a lot has happened here already, but e.g. making -it all work and implement multithreading in afl-fuzz ... there is still quite -some work to do. - -Mentor: hexcoder- or vanhauser-thc - ## Collision-free Binary-Only Maps AFL++ supports collison-free maps using an LTO (link-time-optimization) pass. diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index a5bd7286..0004c297 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -30,10 +30,10 @@ Check out the `fuzzer_stats` file in the AFL output dir or try `afl-whatsup`. It could be important - consult docs/status_screen.md right away! ## Know your target? Convert it to persistent mode for a huge performance gain! -Consult section #5 in llvm_mode/README.md for tips. +Consult section #5 in README.llvm.md for tips. ## Using clang? -Check out llvm_mode/ for a faster alternative to afl-gcc! +Check out instrumentation/ for a faster alternative to afl-gcc! ## Did you know that AFL can fuzz closed-source or cross-platform binaries? Check out qemu_mode/README.md and unicorn_mode/README.md for more. diff --git a/docs/perf_tips.md b/docs/perf_tips.md index 731dc238..fbcb4d8d 100644 --- a/docs/perf_tips.md +++ b/docs/perf_tips.md @@ -51,7 +51,7 @@ a file. ## 3. Use LLVM instrumentation When fuzzing slow targets, you can gain 20-100% performance improvement by -using the LLVM-based instrumentation mode described in [the llvm_mode README](../llvm_mode/README.md). +using the LLVM-based instrumentation mode described in [the instrumentation README](../instrumentation/README.llvm.md). Note that this mode requires the use of clang and will not work with GCC. The LLVM mode also offers a "persistent", in-process fuzzing mode that can @@ -62,12 +62,12 @@ modes require you to edit the source code of the fuzzed program, but the changes often amount to just strategically placing a single line or two. If there are important data comparisons performed (e.g. `strcmp(ptr, MAGIC_HDR)`) -then using laf-intel (see llvm_mode/README.laf-intel.md) will help `afl-fuzz` a lot +then using laf-intel (see instrumentation/README.laf-intel.md) will help `afl-fuzz` a lot to get to the important parts in the code. If you are only interested in specific parts of the code being fuzzed, you can instrument_files the files that are actually relevant. This improves the speed and -accuracy of afl. See llvm_mode/README.instrument_list.md +accuracy of afl. See instrumentation/README.instrument_list.md Also use the InsTrim mode on larger binaries, this improves performance and coverage a lot. @@ -110,7 +110,7 @@ e.g.: https://launchpad.net/libeatmydata In programs that are slow due to unavoidable initialization overhead, you may -want to try the LLVM deferred forkserver mode (see llvm_mode/README.md), +want to try the LLVM deferred forkserver mode (see README.llvm.md), which can give you speed gains up to 10x, as mentioned above. Last but not least, if you are using ASAN and the performance is unacceptable, diff --git a/docs/sister_projects.md b/docs/sister_projects.md index a501ecbd..640e59f7 100644 --- a/docs/sister_projects.md +++ b/docs/sister_projects.md @@ -52,7 +52,7 @@ options. Provides an evolutionary instrumentation-guided fuzzing harness that allows some programs to be fuzzed without the fork / execve overhead. (Similar functionality is now available as the "persistent" feature described in -[the llvm_mode readme](../llvm_mode/README.md)) +[the llvm_mode readme](../instrumentation/README.llvm.md)) http://llvm.org/docs/LibFuzzer.html @@ -245,7 +245,7 @@ https://code.google.com/p/address-sanitizer/wiki/AsanCoverage#Coverage_counters ### AFL JS (Han Choongwoo) One-off optimizations to speed up the fuzzing of JavaScriptCore (now likely -superseded by LLVM deferred forkserver init - see llvm_mode/README.md). +superseded by LLVM deferred forkserver init - see README.llvm.md). https://github.com/tunz/afl-fuzz-js diff --git a/docs/status_screen.md b/docs/status_screen.md index b89468ce..2eeb8f3f 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -324,7 +324,7 @@ there are several things to look at: - Multiple threads executing at once in semi-random order. This is harmless when the 'stability' metric stays over 90% or so, but can become an issue if not. Here's what to try: - * Use afl-clang-fast from [llvm_mode](../llvm_mode/) - it uses a thread-local tracking + * Use afl-clang-fast from [instrumentation](../instrumentation/) - it uses a thread-local tracking model that is less prone to concurrency issues, * See if the target can be compiled or run without threads. Common `./configure` options include `--without-threads`, `--disable-pthreads`, or diff --git a/examples/README.md b/examples/README.md index d28aadbe..46a92c6e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -47,7 +47,7 @@ Here's a quick overview of the stuff you can find in this directory: Note that the minimize_corpus.sh tool has graduated from the examples/ directory and is now available as ../afl-cmin. The LLVM mode has likewise -graduated to ../llvm_mode/*. +graduated to ../instrumentation/*. Most of the tools in this directory are meant chiefly as examples that need to be tweaked for your specific needs. They come with some basic documentation, diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index ff5446e9..82e55fc4 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -27,7 +27,7 @@ EOF # Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c # Build afl-llvm-rt.o.c from the AFL distribution. -clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c +clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c # Build this file, link it with afl-llvm-rt.o.o and the target code. clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o # Run AFL: diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 4281c554..9e469864 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -935,6 +935,7 @@ u8 has_new_bits(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); +void dedup_extras(afl_state_t *); void add_extra(afl_state_t *afl, u8 *mem, u32 len); void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); @@ -972,7 +973,7 @@ u8 fuzz_one(afl_state_t *); void bind_to_free_cpu(afl_state_t *); #endif void setup_post(afl_state_t *); -void read_testcases(afl_state_t *); +void read_testcases(afl_state_t *, u8 *); void perform_dry_run(afl_state_t *); void pivot_inputs(afl_state_t *); u32 find_start_position(afl_state_t *); diff --git a/include/config.h b/include/config.h index 77407d50..8cc70075 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++2.68c" +#define VERSION "++3.00a" /****************************************************** * * @@ -195,7 +195,7 @@ steps; past this point, the "extras/user" step will be still carried out, but with proportionally lower odds: */ -#define MAX_DET_EXTRAS 200 +#define MAX_DET_EXTRAS 256 /* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing (first value), and to keep in memory as candidates. The latter should be much diff --git a/include/envs.h b/include/envs.h index 2dc1dbbf..d9968fcd 100644 --- a/include/envs.h +++ b/include/envs.h @@ -69,6 +69,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CMPLOG", "AFL_LLVM_INSTRIM", "AFL_LLVM_CTX", + "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index 63b7581d..0e38f38b 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -466,7 +466,7 @@ void afl_forkserver(CPUState *cpu) { } /* A simplified persistent mode handler, used as explained in - * llvm_mode/README.md. */ + * instrumentation/README.llvm.md */ void afl_persistent_loop(void) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 713849a1..1351d274 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -626,6 +626,7 @@ if (dir == NULL) { if (!access(fn1, F_OK)) { afl->in_dir = fn1; + subdirs = 0; } else { @@ -1063,7 +1064,7 @@ restart_outer_cull_loop: } - afl->q_prev100 = afl->queue = afl->queue_top = afl->queue; + afl->queue = afl->queue_top = afl->queue; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index db91813b..af52aa45 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -138,8 +138,7 @@ static u8 check_if_text(struct queue_entry *q) { } // non-overlong 2-byte - if (len - offset > 1 && - ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && + if (len - offset > 1 && ((0xC2 <= buf[offset + 0] && buf[offset + 0] <= 0xDF) && (0x80 <= buf[offset + 1] && buf[offset + 1] <= 0xBF))) { offset += 2; @@ -230,7 +229,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { } else { - afl->q_prev100 = afl->queue = afl->queue_top = q; + afl->queue = afl->queue_top = q; } @@ -274,15 +273,15 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { void destroy_queue(afl_state_t *afl) { - struct queue_entry *q = afl->queue, *n; + struct queue_entry *q; + u32 i; - while (q) { + for (i = 0; i < afl->queued_paths; i++) { - n = q->next; + q = afl->queue_buf[i]; ck_free(q->fname); ck_free(q->trace_mini); ck_free(q); - q = n; } diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 51eed14b..c60c65aa 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -982,10 +982,9 @@ void show_stats(afl_state_t *afl) { void show_init_stats(afl_state_t *afl) { struct queue_entry *q = afl->queue; - u32 min_bits = 0, max_bits = 0; + u32 min_bits = 0, max_bits = 0, max_len = 0, count = 0; u64 min_us = 0, max_us = 0; u64 avg_us = 0; - u32 max_len = 0; u8 val_bufs[4][STRINGIFY_VAL_SIZE_MAX]; #define IB(i) val_bufs[(i)], sizeof(val_bufs[(i)]) @@ -1006,6 +1005,7 @@ void show_init_stats(afl_state_t *afl) { if (q->len > max_len) { max_len = q->len; } + ++count; q = q->next; } @@ -1072,10 +1072,10 @@ void show_init_stats(afl_state_t *afl) { OKF("Here are some useful stats:\n\n" cGRA " Test case count : " cRST - "%u favored, %u variable, %u total\n" cGRA " Bitmap range : " cRST + "%u favored, %u variable, %u ignored, %u total\n" cGRA " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", - afl->queued_favored, afl->queued_variable, afl->queued_paths, min_bits, + afl->queued_favored, afl->queued_variable, afl->queued_paths - count, afl->queued_paths, min_bits, max_bits, ((double)afl->total_bitmap_size) / (afl->total_bitmap_entries ? afl->total_bitmap_entries : 1), diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index bfaa22e8..73ca6aaa 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1133,8 +1133,9 @@ int main(int argc, char **argv_orig, char **envp) { setup_cmdline_file(afl, argv + optind); - read_testcases(afl); + read_testcases(afl, NULL); // read_foreign_testcases(afl, 1); for the moment dont do this + OKF("Loaded a total of %u seeds.", afl->queued_paths); load_auto(afl); diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index 2ed10a72..8b8cbd8e 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -3,7 +3,7 @@ . ./test-pre.sh $ECHO "$BLUE[*] Testing: gcc_plugin" -test -e ../afl-gcc-fast -a -e ../afl-gcc-rt.o && { +test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { SAVE_AFL_CC=${AFL_CC} export AFL_CC=`command -v gcc` ../afl-gcc-fast -o test-instr.plain.gccpi ../test-instr.c > /dev/null 2>&1 diff --git a/test/test-unittests.sh b/test/test-unittests.sh index f540b5f8..58c2eea9 100755 --- a/test/test-unittests.sh +++ b/test/test-unittests.sh @@ -7,3 +7,5 @@ unset AFL_CC make -C .. unit || CODE=1 INCOMPLETE=1 : . ./test-post.sh + +rm -rf unittests/unit_hash unittests/unit_rand -- cgit 1.4.1 From 250892228888277262958d1b01b005e14440274e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 5 Sep 2020 15:49:10 +0200 Subject: cmp dict for LTO --- GNUmakefile | 2 +- docs/Changelog.md | 3 +- include/xxhash.h | 39 +++++---- instrumentation/SanitizerCoverageLTO.so.cc | 93 ++++++++++++++++++++++ instrumentation/afl-llvm-lto-instrumentation.so.cc | 93 ++++++++++++++++++++++ 5 files changed, 214 insertions(+), 16 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index 7455483c..bcdc10eb 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -533,7 +533,7 @@ endif deepclean: clean rm -rf qemu_mode/qemu-3.1.1.tar.xz rm -rf unicorn_mode/unicornafl - # NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true +# NEVER EVER ACTIVATE THAT!!!!! git reset --hard >/dev/null 2>&1 || true .PHONY: distrib distrib: all diff --git a/docs/Changelog.md b/docs/Changelog.md index 9de03e78..f86c0b61 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -18,9 +18,10 @@ sending a mail to . - allow up to 4 -x command line options - loaded extras now have a duplicate protection - instrumentation + - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - - not overriding -Ox or -fno-unroll-loops anymore + - LTO autodict now also collects interesting cmp comparisons ### Version ++2.68c (release) diff --git a/include/xxhash.h b/include/xxhash.h index 0472f881..006d3f3d 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -660,7 +660,7 @@ XXH128_hashFromCanonical(const XXH128_canonical_t *src); * These declarations should only be used with static linking. * Never use them in association with dynamic linking! ***************************************************************************** -*/ + */ /* * These definitions are only present to allow static allocation @@ -1189,7 +1189,7 @@ static int XXH_isLittleEndian(void) { return one.c[0]; } -\ + #define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() #endif #endif @@ -1397,7 +1397,9 @@ static xxh_u32 XXH32_avalanche(xxh_u32 h32) { static xxh_u32 XXH32_finalize(xxh_u32 h32, const xxh_u8 *ptr, size_t len, XXH_alignment align) { -\ + + /* dummy comment */ + #define XXH_PROCESS1 \ do { \ \ @@ -1950,16 +1952,21 @@ XXH_FORCE_INLINE xxh_u64 XXH_readLE64_align(const void * ptr, /******* xxh64 *******/ -static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 - */ -static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 - */ -static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 - */ -static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 - */ -static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 - */ +static const xxh_u64 XXH_PRIME64_1 = + 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 + */ +static const xxh_u64 XXH_PRIME64_2 = + 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 + */ +static const xxh_u64 XXH_PRIME64_3 = + 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 + */ +static const xxh_u64 XXH_PRIME64_4 = + 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 + */ +static const xxh_u64 XXH_PRIME64_5 = + 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 + */ #ifdef XXH_OLD_NAMES #define PRIME64_1 XXH_PRIME64_1 @@ -2002,7 +2009,9 @@ static xxh_u64 XXH64_avalanche(xxh_u64 h64) { static xxh_u64 XXH64_finalize(xxh_u64 h64, const xxh_u8 *ptr, size_t len, XXH_alignment align) { -\ + + /* dummy comment */ + #define XXH_PROCESS1_64 \ do { \ \ @@ -2752,6 +2761,7 @@ XXH64_hashFromCanonical(const XXH64_canonical_t *src) { (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \ \ } while (0) + #else #define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \ do { \ @@ -2760,6 +2770,7 @@ XXH64_hashFromCanonical(const XXH64_canonical_t *src) { (outHi) = vshrn_n_u64((in), 32); \ \ } while (0) + #endif #endif /* XXH_VECTOR == XXH_NEON */ diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 1dd65188..f4958d80 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/ADT/ArrayRef.h" @@ -249,6 +250,7 @@ class ModuleSanitizerCoverage { GlobalVariable * AFLMapPtr = NULL; Value * MapPtrFixed = NULL; FILE * documentFile = NULL; + size_t found = 0; // afl++ END }; @@ -513,6 +515,92 @@ bool ModuleSanitizerCoverage::instrumentModule( for (auto &IN : BB) { CallInst *callInst = nullptr; + CmpInst * cmpInst = nullptr; + + if ((cmpInst = dyn_cast(&IN))) { + + Value * op = cmpInst->getOperand(1); + ConstantInt *ilen = dyn_cast(op); + + if (ilen) { + + u64 val2 = 0, val = ilen->getZExtValue(); + u32 len = 0; + if (val > 0x10000 && val < 0xffffffff) len = 4; + if (val > 0x100000001 && val < 0xffffffffffffffff) len = 8; + + if (len) { + + auto c = cmpInst->getPredicate(); + + switch (c) { + + case CmpInst::FCMP_OGT: // fall through + case CmpInst::FCMP_OLE: // fall through + case CmpInst::ICMP_SLE: // fall through + case CmpInst::ICMP_SGT: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_UGT: // fall through + case CmpInst::FCMP_ULE: // fall through + case CmpInst::ICMP_UGT: // fall through + case CmpInst::ICMP_ULE: + if ((val & 0xffff) != 0xfffe) val2 = val + 1; + break; + + case CmpInst::FCMP_OLT: // fall through + case CmpInst::FCMP_OGE: // fall through + case CmpInst::ICMP_SLT: // fall through + case CmpInst::ICMP_SGE: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_ULT: // fall through + case CmpInst::FCMP_UGE: // fall through + case CmpInst::ICMP_ULT: // fall through + case CmpInst::ICMP_UGE: + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + default: + val2 = 0; + + } + + dictionary.push_back(std::string((char *)&val, len)); + found++; + + if (val2) { + + dictionary.push_back(std::string((char *)&val2, len)); + found++; + + } + + } + + } + + } if ((callInst = dyn_cast(&IN))) { @@ -917,6 +1005,11 @@ bool ModuleSanitizerCoverage::instrumentModule( size_t memlen = 0, count = 0, offset = 0; char * ptr; + // sort and unique the dictionary + std::sort(dictionary.begin(), dictionary.end()); + auto last = std::unique(dictionary.begin(), dictionary.end()); + dictionary.erase(last, dictionary.end()); + for (auto token : dictionary) { memlen += token.length(); diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 125db229..9632c319 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include "llvm/Config/llvm-config.h" #include "llvm/ADT/Statistic.h" @@ -106,6 +107,7 @@ bool AFLLTOPass::runOnModule(Module &M) { std::vector BlockList; char * ptr; FILE * documentFile = NULL; + size_t found = 0; srand((unsigned int)time(NULL)); @@ -284,6 +286,92 @@ bool AFLLTOPass::runOnModule(Module &M) { for (auto &IN : BB) { CallInst *callInst = nullptr; + CmpInst * cmpInst = nullptr; + + if ((cmpInst = dyn_cast(&IN))) { + + Value * op = cmpInst->getOperand(1); + ConstantInt *ilen = dyn_cast(op); + + if (ilen) { + + u64 val2 = 0, val = ilen->getZExtValue(); + u32 len = 0; + if (val > 0x10000 && val < 0xffffffff) len = 4; + if (val > 0x100000001 && val < 0xffffffffffffffff) len = 8; + + if (len) { + + auto c = cmpInst->getPredicate(); + + switch (c) { + + case CmpInst::FCMP_OGT: // fall through + case CmpInst::FCMP_OLE: // fall through + case CmpInst::ICMP_SLE: // fall through + case CmpInst::ICMP_SGT: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_UGT: // fall through + case CmpInst::FCMP_ULE: // fall through + case CmpInst::ICMP_UGT: // fall through + case CmpInst::ICMP_ULE: + if ((val & 0xffff) != 0xfffe) val2 = val + 1; + break; + + case CmpInst::FCMP_OLT: // fall through + case CmpInst::FCMP_OGE: // fall through + case CmpInst::ICMP_SLT: // fall through + case CmpInst::ICMP_SGE: + + // signed comparison and it is a negative constant + if ((len == 4 && (val & 80000000)) || + (len == 8 && (val & 8000000000000000))) { + + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + } + + // fall through + + case CmpInst::FCMP_ULT: // fall through + case CmpInst::FCMP_UGE: // fall through + case CmpInst::ICMP_ULT: // fall through + case CmpInst::ICMP_UGE: + if ((val & 0xffff) != 1) val2 = val - 1; + break; + + default: + val2 = 0; + + } + + dictionary.push_back(std::string((char *)&val, len)); + found++; + + if (val2) { + + dictionary.push_back(std::string((char *)&val2, len)); + found++; + + } + + } + + } + + } if ((callInst = dyn_cast(&IN))) { @@ -842,6 +930,11 @@ bool AFLLTOPass::runOnModule(Module &M) { size_t memlen = 0, count = 0, offset = 0; char * ptr; + // sort and unique the dictionary + std::sort(dictionary.begin(), dictionary.end()); + auto last = std::unique(dictionary.begin(), dictionary.end()); + dictionary.erase(last, dictionary.end()); + for (auto token : dictionary) { memlen += token.length(); -- cgit 1.4.1 From ded4d093ff59b4459b04aaae9b3b7bbcdaadcdef Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 5 Sep 2020 16:16:56 +0200 Subject: skip crashes but keep for splices --- docs/Changelog.md | 4 ++++ src/afl-fuzz-init.c | 35 +++++++++++++++++++++++++++++------ src/afl-fuzz.c | 3 +++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f86c0b61..a3c05ed3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,10 @@ sending a mail to . - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection + - If test cases are too large we do a partial read on the maximum + supported size + - longer seeds with the same trace information will now be ignored + for fuzzing but still be used for splicing - instrumentation - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index c834e5db..a5ebbcd8 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -713,11 +713,9 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (st.st_size > MAX_FILE) { - WARNF("Test case '%s' is too big (%s, limit is %s), skipping", fn2, + WARNF("Test case '%s' is too big (%s, limit is %s), partial reading", fn2, stringify_mem_size(val_buf[0], sizeof(val_buf[0]), st.st_size), stringify_mem_size(val_buf[1], sizeof(val_buf[1]), MAX_FILE)); - ck_free(fn2); - continue; } @@ -728,7 +726,8 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (!access(dfn, F_OK)) { passed_det = 1; } - add_to_queue(afl, fn2, st.st_size, passed_det); + add_to_queue(afl, fn2, st.st_size >= MAX_FILE ? MAX_FILE : st.st_size, + passed_det); } @@ -947,7 +946,31 @@ void perform_dry_run(afl_state_t *afl) { #undef MSG_ULIMIT_USAGE #undef MSG_FORK_ON_APPLE - FATAL("Test case '%s' results in a crash", fn); + WARNF("Test case '%s' results in a crash, skipping", fn); + + /* Remove from fuzzing queue but keep for splicing */ + + struct queue_entry *p = afl->queue; + while (p && p->next != q) + p = p->next; + + if (p) + p->next = q->next; + else + afl->queue = q->next; + + --afl->pending_not_fuzzed; + + afl->max_depth = 0; + p = afl->queue; + while (p) { + + if (p->depth > afl->max_depth) afl->max_depth = p->depth; + p = p->next; + + } + + break; case FSRV_RUN_ERROR: @@ -1067,7 +1090,7 @@ restart_outer_cull_loop: } - afl->queue = afl->queue_top = afl->queue; + afl->queue_top = afl->queue; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 73ca6aaa..a8816cb3 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1282,6 +1282,9 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); + if (!afl->pending_not_fuzzed) + FATAL("We need at least on valid input seed that does not crash!"); + show_init_stats(afl); seek_to = find_start_position(afl); -- cgit 1.4.1 From 163e5ffd10936e6e119f643495129ab05fa3e5ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 5 Sep 2020 17:40:39 +0200 Subject: -p seek is now the default --- docs/Changelog.md | 3 +++ src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 19 +++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index a3c05ed3..b4c575a6 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -21,6 +21,9 @@ sending a mail to . supported size - longer seeds with the same trace information will now be ignored for fuzzing but still be used for splicing + - crashing seeds are now not prohibiting a run anymore but are + skipped. They are used for splicing though. + - set the default power schedule to the superiour "seek" schedule - instrumentation - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 577fc34f..4e817843 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE)*/ + afl->schedule = SEEK; /* Power schedule (default: SEEK) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a8816cb3..5b96ef45 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -89,11 +89,10 @@ static void usage(u8 *argv0, int more_help) { " -o dir - output directory for fuzzer findings\n\n" "Execution control settings:\n" - " -p schedule - power schedules compute a seed's performance score. " - "\n" - " see docs/power_schedules.md\n" + " -p schedule - power schedules compute a seed's performance score:\n" + " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" @@ -349,15 +348,15 @@ int main(int argc, char **argv_orig, char **envp) { afl->schedule = RARE; - } else if (!stricmp(optarg, "seek")) { + } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl")) { - afl->schedule = SEEK; + afl->schedule = EXPLORE; - } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "default") || + } else if (!stricmp(optarg, "seek") || !stricmp(optarg, "default") || - !stricmp(optarg, "normal") || !stricmp(optarg, "afl")) { + !stricmp(optarg, "normal")) { - afl->schedule = EXPLORE; + afl->schedule = SEEK; } else { -- cgit 1.4.1 From 6114a48b89528ff3f24d91832d588aa8c05b672e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 6 Sep 2020 13:29:32 +0200 Subject: add std::string and bcmp to dictionary functions --- docs/Changelog.md | 3 ++- instrumentation/SanitizerCoverageLTO.so.cc | 29 ++++++++++++++++------ instrumentation/afl-llvm-dict2file.so.cc | 15 ++++++++--- instrumentation/afl-llvm-lto-instrumentation.so.cc | 16 +++++++++--- 4 files changed, 48 insertions(+), 15 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index b4c575a6..73613452 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -28,7 +28,8 @@ sending a mail to . - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - - LTO autodict now also collects interesting cmp comparisons + - LTO autodict now also collects interesting cmp comparisons, + std::string compare + find + ==, bcmp ### Version ++2.68c (release) diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 0a136d6f..b75776b8 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -197,8 +197,9 @@ class ModuleSanitizerCoverage { void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); -// std::pair CreateSecStartEnd(Module &M, const char *Section, -// Type *Ty); + // std::pair CreateSecStartEnd(Module &M, const char + // *Section, + // Type *Ty); void SetNoSanitizeMetadata(Instruction *I) { @@ -207,9 +208,9 @@ class ModuleSanitizerCoverage { } - std::string getSectionName(const std::string &Section) const; -// std::string getSectionStart(const std::string &Section) const; -// std::string getSectionEnd(const std::string &Section) const; + std::string getSectionName(const std::string &Section) const; + // std::string getSectionStart(const std::string &Section) const; + // std::string getSectionEnd(const std::string &Section) const; FunctionCallee SanCovTracePCIndir; FunctionCallee SanCovTracePC /*, SanCovTracePCGuard*/; Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, @@ -374,6 +375,7 @@ std::pair ModuleSanitizerCoverage::CreateSecStartEnd( return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEndPtr); } + */ bool ModuleSanitizerCoverage::instrumentModule( @@ -612,6 +614,7 @@ bool ModuleSanitizerCoverage::instrumentModule( bool isStrcasecmp = true; bool isStrncasecmp = true; bool isIntMemcpy = true; + bool isStdString = true; bool addedNull = false; size_t optLen = 0; @@ -624,7 +627,13 @@ bool ModuleSanitizerCoverage::instrumentModule( isStrncmp &= !FuncName.compare("strncmp"); isStrcasecmp &= !FuncName.compare("strcasecmp"); isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + isIntMemcpy &= (!FuncName.compare("llvm.memcpy.p0i8.p0i8.i64") || + !FuncName.compare("bcmp")); + isStdString &= + ((FuncName.find("basic_string") != std::string::npos && + FuncName.find("compare") != std::string::npos) || + (FuncName.find("basic_string") != std::string::npos && + FuncName.find("find") != std::string::npos)); /* we do something different here, putting this BB and the successors in a block map */ @@ -642,7 +651,7 @@ bool ModuleSanitizerCoverage::instrumentModule( } if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function @@ -676,9 +685,12 @@ bool ModuleSanitizerCoverage::instrumentModule( FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) && FT->getParamType(2)->isIntegerTy(); + isStdString &= FT->getNumParams() >= 2 && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* is a str{n,}{case,}cmp/memcmp, check if we have @@ -1556,6 +1568,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( return "__stop___" + Section; } + */ char ModuleSanitizerCoverageLegacyPass::ID = 0; diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index ef42756e..0ab97d5b 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -283,6 +283,7 @@ bool AFLdict2filePass::runOnModule(Module &M) { bool isStrcasecmp = true; bool isStrncasecmp = true; bool isIntMemcpy = true; + bool isStdString = true; bool addedNull = false; size_t optLen = 0; @@ -295,10 +296,15 @@ bool AFLdict2filePass::runOnModule(Module &M) { isStrncmp &= !FuncName.compare("strncmp"); isStrcasecmp &= !FuncName.compare("strcasecmp"); isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + isIntMemcpy &= (!FuncName.compare("llvm.memcpy.p0i8.p0i8.i64") || + !FuncName.compare("bcmp")); + isStdString &= ((FuncName.find("basic_string") != std::string::npos && + FuncName.find("compare") != std::string::npos) || + (FuncName.find("basic_string") != std::string::npos && + FuncName.find("find") != std::string::npos)); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function @@ -330,9 +336,12 @@ bool AFLdict2filePass::runOnModule(Module &M) { FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) && FT->getParamType(2)->isIntegerTy(); + isStdString &= FT->getNumParams() >= 2 && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* is a str{n,}{case,}cmp/memcmp, check if we have diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 9632c319..4f032ca0 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -381,6 +381,7 @@ bool AFLLTOPass::runOnModule(Module &M) { bool isStrcasecmp = true; bool isStrncasecmp = true; bool isIntMemcpy = true; + bool isStdString = true; bool addedNull = false; size_t optLen = 0; @@ -393,7 +394,13 @@ bool AFLLTOPass::runOnModule(Module &M) { isStrncmp &= !FuncName.compare("strncmp"); isStrcasecmp &= !FuncName.compare("strcasecmp"); isStrncasecmp &= !FuncName.compare("strncasecmp"); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); + isIntMemcpy &= (!FuncName.compare("llvm.memcpy.p0i8.p0i8.i64") || + !FuncName.compare("bcmp")); + isStdString &= + ((FuncName.find("basic_string") != std::string::npos && + FuncName.find("compare") != std::string::npos) || + (FuncName.find("basic_string") != std::string::npos && + FuncName.find("find") != std::string::npos)); /* we do something different here, putting this BB and the successors in a block map */ @@ -414,7 +421,7 @@ bool AFLLTOPass::runOnModule(Module &M) { } if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* Verify the strcmp/memcmp/strncmp/strcasecmp/strncasecmp function @@ -448,9 +455,12 @@ bool AFLLTOPass::runOnModule(Module &M) { FT->getParamType(0) == IntegerType::getInt8PtrTy(M.getContext()) && FT->getParamType(2)->isIntegerTy(); + isStdString &= FT->getNumParams() >= 2 && + FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy) + !isStrncasecmp && !isIntMemcpy && !isStdString) continue; /* is a str{n,}{case,}cmp/memcmp, check if we have -- cgit 1.4.1 From 7bcbfd48e54eba5a99d05b04f4f3d6bea29cde80 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 6 Sep 2020 15:12:23 +0200 Subject: update ideas --- docs/ideas.md | 13 +++++++++++++ src/README.md | 33 +++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/docs/ideas.md b/docs/ideas.md index a5d40963..aaa3eed1 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,6 +3,19 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. +## Analysis software + +Currently analysis is done by using afl-plot, which is rather outdated. +A GTK or browser tool to create run-time analysis based on fuzzer_stats, +queue/id* information and plot_data that allows for zooming in and out, +changing min/max display values etc. and doing that for a single run, +different runs and campaigns vs campaigns. +Interesting values are execs, and execs/s, edges discovered (total, when +each edge was discovered and which other fuzzer share finding that edge), +test cases executed. +It should be clickable which value is X and Y axis, zoom factor, log scaling +on-off, etc. + ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. diff --git a/src/README.md b/src/README.md index 6da534c3..35af6ab9 100644 --- a/src/README.md +++ b/src/README.md @@ -2,23 +2,28 @@ Quick explanation about the files here: -- `afl-analyze.c` - afl-analyze binary tool +- `afl-analyze.c` - afl-analyze binary tool - `afl-as.c` - afl-as binary tool -- `afl-gotcpu.c` - afl-gotcpu binary tool -- `afl-showmap.c` - afl-showmap binary tool -- `afl-tmin.c` - afl-tmin binary tool -- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-cc.c` - afl-cc binary tool +- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin +- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz afl-showmap, afl-tmin - `afl-fuzz-bitmap.c` - afl-fuzz bitmap handling +- `afl-fuzz.c` - afl-fuzz binary tool (just main() and usage()) +- `afl-fuzz-cmplog.c` - afl-fuzz cmplog functions - `afl-fuzz-extras.c` - afl-fuzz the *extra* function calls -- `afl-fuzz-state.c` - afl-fuzz state and globals -- `afl-fuzz-init.c` - afl-fuzz initialization -- `afl-fuzz-misc.c` - afl-fuzz misc functions -- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-init.c` - afl-fuzz initialization +- `afl-fuzz-misc.c` - afl-fuzz misc functions +- `afl-fuzz-mutators.c` - afl-fuzz custom mutator and python support +- `afl-fuzz-one.c` - afl-fuzz fuzzer_one big loop, this is where the mutation is happening +- `afl-fuzz-performance.c` - hash64 and rand functions - `afl-fuzz-python.c` - afl-fuzz the python mutator extension - `afl-fuzz-queue.c` - afl-fuzz handling the queue -- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-redqueen.c` - afl-fuzz redqueen implemention +- `afl-fuzz-run.c` - afl-fuzz running the target +- `afl-fuzz-state.c` - afl-fuzz state and globals - `afl-fuzz-stats.c` - afl-fuzz writing the statistics file -- `afl-gcc.c` - afl-gcc binary tool (deprecated) -- `afl-common.c` - common functions, used by afl-analyze, afl-fuzz, afl-showmap and afl-tmin -- `afl-forkserver.c` - forkserver implementation, used by afl-fuzz and afl-tmin -afl-sharedmem.c - sharedmem implementation, used by afl-fuzz and afl-tmin +- `afl-gotcpu.c` - afl-gotcpu binary tool +- `afl-ld-lto.c` - LTO linker helper +- `afl-sharedmem.c` - sharedmem implementation, used by afl-fuzz, afl-showmap, afl-tmin +- `afl-showmap.c` - afl-showmap binary tool +- `afl-tmin.c` - afl-tmin binary tool -- cgit 1.4.1 From 9544b3dbf22f1007f7d3f77593ec746a0345a587 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Mon, 7 Sep 2020 12:35:31 -0300 Subject: rewrite gcc plugin When we started using AFL, it did not have an integrated GCC plugin. There was one proposed by Austin Seipp, but for various reasons we ended up using some of its infrastructure (runtime and wrapper), but writing the GCC plugin proper from scratch. With AFL++'s renewed interest in a GCC plugin, we rebased ours, with some features that are or were missing in the one that was integrated: * efficient, fully-functional inline and out-of-line instrumentation Inline instrumentation was work in progress in the original plugin. Controlled by AFL_GCC_OUT_OF_LINE. * reproducible instrumentation Obey -frandom-seed for pseudorandom number generation. * licensing clarity and strict compliance GPLv3+ for the plugin, that uses GCC internals; add a copy of the license, as required. * allow/deny list support Copied and adjusted from the LLVM plugin implementation. * neverZero support Not as compact as the asm-wrapper version, but likely more efficient. Both are quite thread-unsafe, with different caveats. Controlled with AFL_GCC_SKIP_NEVERZERO. --- GNUmakefile.llvm | 2 +- docs/INSTALL.md | 5 +- include/envs.h | 5 + instrumentation/COPYING3 | 674 ++++++++++++++++++ instrumentation/README.out_of_line.md | 21 + instrumentation/afl-compiler-rt.o.c | 18 +- instrumentation/afl-gcc-pass.so.cc | 1218 +++++++++++++++++++++------------ src/afl-cc.c | 6 + test/test-performance.sh | 43 +- 9 files changed, 1545 insertions(+), 447 deletions(-) create mode 100644 instrumentation/COPYING3 create mode 100644 instrumentation/README.out_of_line.md (limited to 'docs') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index d432021b..3eefdf90 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -221,7 +221,7 @@ endif ifeq "$(shell echo 'int main() {return 0; }' | $(CLANG_BIN) -x c - -fdebug-prefix-map=$(CURDIR)=llvm_mode -o .test 2>/dev/null && echo 1 || echo 0 ; rm -f .test )" "1" AFL_CLANG_DEBUG_PREFIX = -fdebug-prefix-map="$(CURDIR)=llvm_mode" else - AFL_CLANG_DEBUG_PREFIX = "" + AFL_CLANG_DEBUG_PREFIX = endif CFLAGS ?= -O3 -funroll-loops -fPIC -D_FORTIFY_SOURCE=2 diff --git a/docs/INSTALL.md b/docs/INSTALL.md index fb7b5642..93a46caf 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,6 +28,8 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. +Likewise, if you are using GCC, please review gcc_plugin/README.md. + You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but afl-fuzz will guide you through that if necessary. @@ -157,7 +159,8 @@ instrumentation mode (`-Q`) will not work. ## 6. Everything else You're on your own. On POSIX-compliant systems, you may be able to compile and -run the fuzzer; and the LLVM mode may offer a way to instrument non-x86 code. +run the fuzzer; and the LLVM and GCC plugin modes may offer a way to instrument +non-x86 code. The fuzzer will run on Windows in WSL only. It will not work under Cygwin on in the normal Windows world. It could be ported to the latter platform fairly easily, but it's a pretty bad diff --git a/include/envs.h b/include/envs.h index d9968fcd..3a06aa2a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -45,7 +45,12 @@ static char *afl_environment_variables[] = { "AFL_EXIT_WHEN_DONE", "AFL_FAST_CAL", "AFL_FORCE_UI", + "AFL_GCC_ALLOWLIST", + "AFL_GCC_DENYLIST", + "AFL_GCC_BLOCKLIST", "AFL_GCC_INSTRUMENT_FILE", + "AFL_GCC_OUT_OF_LINE", + "AFL_GCC_SKIP_NEVERZERO", "AFL_GCJ", "AFL_HANG_TMOUT", "AFL_FORKSRV_INIT_TMOUT", diff --git a/instrumentation/COPYING3 b/instrumentation/COPYING3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/instrumentation/COPYING3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/instrumentation/README.out_of_line.md b/instrumentation/README.out_of_line.md new file mode 100644 index 00000000..aad215b6 --- /dev/null +++ b/instrumentation/README.out_of_line.md @@ -0,0 +1,21 @@ +=========================================== +Using afl++ without inlined instrumentation +=========================================== + + This file describes how you can disable inlining of instrumentation. + + +By default, the GCC plugin will duplicate the effects of calling +__afl_trace (see afl-gcc-rt.o.c) in instrumented code, instead of +issuing function calls. + +The calls are presumed to be slower, more so because the rt file +itself is not optimized by the compiler. + +Setting AFL_GCC_OUT_OF_LINE=1 in the environment while compiling code +with the plugin will disable this inlining, issuing calls to the +unoptimized runtime instead. + +You probably don't want to do this, but it might be useful in certain +AFL debugging scenarios, and it might work as a fallback in case +something goes wrong with the inlined instrumentation. diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index a3d75b15..05e2d50d 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -38,7 +38,9 @@ #include #include +#if ! __GNUC__ #include "llvm/Config/llvm-config.h" +#endif #ifdef __linux__ #include "snapshot-inl.h" @@ -109,14 +111,22 @@ static u8 _is_sancov; void __afl_trace(const u32 x) { + PREV_LOC_T prev = __afl_prev_loc[0]; + __afl_prev_loc[0] = (x >> 1); + + u8 *p = &__afl_area_ptr[prev ^ x]; + #if 1 /* enable for neverZero feature. */ - __afl_area_ptr[__afl_prev_loc[0] ^ x] += - 1 + ((u8)(1 + __afl_area_ptr[__afl_prev_loc[0] ^ x]) == 0); +# if __GNUC__ + u8 c = __builtin_add_overflow (*p, 1, p); + *p += c; +# else + *p += 1 + ((u8)(1 + *p == 0); +# endif #else - ++__afl_area_ptr[__afl_prev_loc[0] ^ x]; + ++*p; #endif - __afl_prev_loc[0] = (x >> 1); return; } diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c5614aca..6d6f4636 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -1,36 +1,23 @@ -// -// There are some TODOs in this file: -// - fix instrumentation via external call -// - fix inline instrumentation -// - implement instrument list feature -// - dont instrument blocks that are uninteresting -// - implement neverZero -// - -/* - american fuzzy lop++ - GCC instrumentation pass - --------------------------------------------- - - Written by Austin Seipp with bits from - Emese Revfy - - Fixed by Heiko Eißfeldt 2019-2020 for AFL++ - - GCC integration design is based on the LLVM design, which comes - from Laszlo Szekeres. Some of the boilerplate code below for - afl_pass to adapt to different GCC versions was taken from Emese - Revfy's Size Overflow plugin for GCC, licensed under the GPLv2/v3. - - (NOTE: this plugin code is under GPLv3, in order to comply with the - GCC runtime library exception, which states that you may distribute - "Target Code" from the compiler under a license of your choice, as - long as the "Compilation Process" is "Eligible", and contains no - GPL-incompatible software in GCC "during the process of - transforming high level code to target code". In this case, the - plugin will be used to generate "Target Code" during the - "Compilation Process", and thus it must be GPLv3 to be "eligible".) - - Copyright (C) 2015 Austin Seipp +/* GCC plugin for instrumentation of code for american fuzzy lop. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AdaCore + + Written by Alexandre Oliva , based on the AFL + LLVM pass by Laszlo Szekeres and Michal + Zalewski , and copying a little boilerplate + from GCC's libcc1 plugin and GCC proper. Aside from the + boilerplate, namely includes and the pass data structure, and pass + initialization code and output messages borrowed and adapted from + the LLVM pass into plugin_init and plugin_finalize, the + implementation of the GCC pass proper is written from scratch, + aiming at similar behavior and performance to that of the LLVM + pass, and also at compatibility with the out-of-line + instrumentation and run times of AFL++, as well as of an earlier + GCC plugin implementation by Austin Seipp . The + implementation of Allow/Deny Lists is adapted from that in the LLVM + plugin. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,555 +34,910 @@ */ -#define BUILD_INLINE_INST +/* This file implements a GCC plugin that introduces an + instrumentation pass for AFL. What follows is the specification + used to rewrite it, extracted from the functional llvm_mode pass + and from an implementation of the gcc_plugin started by Austin + Seipp . + + Declare itself as GPL-compatible. + + Define a 'plugin_init' function. + + Check version against the global gcc_version. + + Register a PLUGIN_INFO object with .version and .help. + + Initialize the random number generator seed with GCC's + random seed. + + Set quiet mode depending on whether stderr is a terminal and + AFL_QUIET is set. + + Output some identification message if not in quiet mode. + + Parse AFL_INST_RATIO, if set, as a number between 0 and 100. Error + out if it's not in range; set up an instrumentation ratio global + otherwise. + + Introduce a single instrumentation pass after SSA. + + The new pass is to be a GIMPLE_PASS. Given the sort of + instrumentation it's supposed to do, its todo_flags_finish will + certainly need TODO_update_ssa, and TODO_cleanup_cfg. + TODO_verify_il is probably desirable, at least during debugging. + TODO_rebuild_cgraph_edges is required only in the out-of-line + instrumentation mode. + + The instrumentation pass amounts to iterating over all basic blocks + and optionally inserting one of the instrumentation sequences below + after its labels, to indicate execution entered the block. + + A block should be skipped if R(100) (from ../types.h) is >= the + global instrumentation ratio. + + A block may be skipped for other reasons, such as if all of its + predecessors have a single successor. + + For an instrumented block, a R(MAP_SIZE) say should be + generated to be used as its location number. Let be a compiler + constant built out of it. + + Count instrumented blocks and print a message at the end of the + compilation, if not in quiet mode. + + Instrumentation in "dumb" or "out-of-line" mode requires calling a + function, passing it the location number. The function to be + called is __afl_trace, implemented in afl-gcc-rt.o.c. Its + declaration needs only be created once. + + Build the call statement (), then add it to the seq to be + inserted. + + Instrumentation in "fast" or "inline" mode performs the computation + of __afl_trace as part of the function. + + It needs to read and write __afl_prev_loc, a TLS u32 variable. Its + declaration

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

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

. + + Temporaries used above need only be created once per function. + + If any block was instrumented in a function, an initializer for + needs to be introduced, loading it from and inserting it in the + entry edge for the entry block. +*/ #include "../include/config.h" #include "../include/debug.h" -/* clear helper macros AFL types pull in, which intervene with gcc-plugin - * headers from GCC-8 */ +#include +#include +#include + #ifdef likely - #undef likely +# undef likely #endif #ifdef unlikely - #undef unlikely +# undef unlikely #endif -#include -#include -#include - #include #include #include +#include +#include + #include #include -#include -#include -#include +#include #include -#include -#include -#include +#include +#include +#include #include #include -#include -#include -#include +#include + #include -#include -#include -#include -#include -/* -------------------------------------------------------------------------- */ -/* -- AFL instrumentation pass ---------------------------------------------- */ +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; -static int be_quiet = 0; -static unsigned int inst_ratio = 100; -static bool inst_ext = true; -static std::list myInstrumentList; +namespace { -static unsigned int ext_call_instrument(function *fun) { +static const struct pass_data afl_pass_data = + { + .type = GIMPLE_PASS, + .name = "afl", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa + | TODO_cleanup_cfg + | TODO_verify_il), + }; + +struct afl_pass : gimple_opt_pass { + afl_pass (bool quiet, unsigned int ratio) + : gimple_opt_pass (afl_pass_data, g), + be_quiet (quiet), + debug (!!getenv ("AFL_DEBUG")), + inst_ratio (ratio), +#ifdef AFL_GCC_OUT_OF_LINE + out_of_line (!!(AFL_GCC_OUT_OF_LINE)), +#else + out_of_line (getenv ("AFL_GCC_OUT_OF_LINE")), +#endif + neverZero (!getenv ("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks (0) + { + initInstrumentList (); + } - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + + /* How likely (%) is a block to be instrumented? */ + const unsigned int inst_ratio; + + /* Should we use slow, out-of-line call-based instrumentation? */ + const bool out_of_line; + + /* Should we make sure the map edge-crossing counters never wrap + around to zero? */ + const bool neverZero; + + /* Count instrumented blocks. */ + int inst_blocks; + + virtual unsigned int + execute (function *fn) + { + if (!isInInstrumentList(fn)) + return 0; + + int blocks = 0; + + /* These are temporaries used by inline instrumentation only, that + are live throughout the function. */ + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, + ntry = NULL, cntr = NULL, xaddc = NULL, xincr = NULL; + + basic_block bb; + FOR_EACH_BB_FN (bb, fn) + { + if (!instrument_block_p (bb)) + continue; + + /* Generate the block identifier. */ + unsigned bid = R (MAP_SIZE); + tree bidt = build_int_cst (sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) + { + static tree afl_trace = get_afl_trace_decl (); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call (afl_trace, 1, bidt); + gimple_seq_add_stmt (&seq, call); + } + else + { + static tree afl_prev_loc = get_afl_prev_loc_decl (); + static tree afl_area_ptr = get_afl_area_ptr_decl (); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var (TREE_TYPE (afl_prev_loc), ".afl_prev_loc"); + gimple *load_loc = gimple_build_assign (ploc, afl_prev_loc); + gimple_seq_add_stmt (&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) + indx = create_tmp_var (TREE_TYPE (bidt), ".afl_index"); + gimple *conv_ploc + = gimple_build_assign (indx, + fold_convert (TREE_TYPE (indx), + ploc)); + gimple_seq_add_stmt (&seq, conv_ploc); + gimple *xor_loc = gimple_build_assign (indx, BIT_XOR_EXPR, + indx, bidt); + gimple_seq_add_stmt (&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) + { + map = afl_area_ptr; + map_ptr = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_ptr"); + ntry = create_tmp_var (TREE_TYPE (afl_area_ptr), + ".afl_map_entry"); + } + gimple *idx_map = gimple_build_assign (ntry, POINTER_PLUS_EXPR, + map_ptr, indx); + gimple_seq_add_stmt (&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (ntry)), + ntry, build_zero_cst (TREE_TYPE (ntry))); + if (blocks == 0) + cntr = create_tmp_var (TREE_TYPE (memref), ".afl_edge_count"); + + gimple *load_cntr = gimple_build_assign (cntr, memref); + gimple_seq_add_stmt (&seq, load_cntr); + + tree cntrb = cntr; + tree incrv = build_one_cst (TREE_TYPE (cntr)); + + if (neverZero) + { + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) + { + xaddc = create_tmp_var (build_complex_type + (TREE_TYPE (memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var (TREE_TYPE (memref), + ".afl_edge_xincr"); + } + + auto_vec vargs (2); + vargs.quick_push (cntr); + vargs.quick_push (incrv); + gcall *add1_cntr + = gimple_build_call_internal_vec (IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs (add1_cntr, xaddc); + gimple_seq_add_stmt (&seq, add1_cntr); + + cntrb = build1 (REALPART_EXPR, TREE_TYPE (cntr), xaddc); + incrv = build1 (IMAGPART_EXPR, TREE_TYPE (xincr), xaddc); + } + + gimple *incr_cntr = gimple_build_assign (cntr, PLUS_EXPR, + cntrb, incrv); + gimple_seq_add_stmt (&seq, incr_cntr); + + gimple *store_cntr = gimple_build_assign (unshare_expr (memref), + cntr); + gimple_seq_add_stmt (&seq, store_cntr); + + /* Store bid >> 1 in __afl_prev_loc. */ + gimple *shift_loc = gimple_build_assign (ploc, + build_int_cst + (TREE_TYPE (ploc), + bid >> 1)); + gimple_seq_add_stmt (&seq, shift_loc); + gimple *store_loc = gimple_build_assign (afl_prev_loc, ploc); + gimple_seq_add_stmt (&seq, store_loc); + } + + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels (bb); + gsi_insert_seq_before (&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + } - tree fntype = build_function_type_list(void_type_node, /* return */ - uint32_type_node, /* args */ - NULL_TREE); /* done */ - tree fndecl = build_fn_decl("__afl_trace", fntype); - TREE_STATIC(fndecl) = 1; /* Defined elsewhere */ - TREE_PUBLIC(fndecl) = 1; /* Public */ - DECL_EXTERNAL(fndecl) = 1; /* External linkage */ - DECL_ARTIFICIAL(fndecl) = 1; /* Injected by compiler */ + /* Aggregate the instrumented block count. */ + inst_blocks += blocks; - FOR_EACH_BB_FN(bb, fun) { + if (blocks) + { + if (out_of_line) + return TODO_rebuild_cgraph_edges; - gimple_seq fcall; - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; + gimple_seq seq = NULL; - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + gimple *load_ptr = gimple_build_assign (map_ptr, map); + gimple_seq_add_stmt (&seq, load_ptr); - int more_than_one = -1; - edge ep; - edge_iterator eip; + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (fn)); + gsi_insert_seq_on_edge_immediate (e, seq); + } - FOR_EACH_EDGE(ep, eip, bb->preds) { + return 0; + } - int count = 0; - if (more_than_one == -1) more_than_one = 0; + /* Decide whether to instrument block BB. Skip it due to the random + distribution, or if it's the single successor of all its + predecessors. */ + inline bool + instrument_block_p (basic_block bb) + { + if (R (100) >= inst_ratio) + return false; + + edge e; edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->preds) + if (!single_succ_p (e->src)) + return true; + + return false; + } - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { + /* Create and return a declaration for the __afl_trace rt function. */ + static inline tree + get_afl_trace_decl () + { + tree type = build_function_type_list (void_type_node, + uint16_type_node, + NULL_TREE); + tree decl = build_fn_decl ("__afl_trace", type); - basic_block Succ = es->dest; - if (Succ != NULL) count++; + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; - } + return decl; + } - if (count > 1) more_than_one = 1; + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_prev_loc_decl () + { + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_prev_loc"), + uint32_type_node); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + set_decl_tls_model (decl, + (flag_pic + ? TLS_MODEL_INITIAL_EXEC + : TLS_MODEL_LOCAL_EXEC)); + return decl; + } + + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree + get_afl_area_ptr_decl () + { + tree type = build_pointer_type (unsigned_char_type_node); + tree decl = build_decl (BUILTINS_LOCATION, VAR_DECL, + get_identifier ("__afl_area_ptr"), + type); + TREE_PUBLIC (decl) = 1; + DECL_EXTERNAL (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + TREE_STATIC (decl) = 1; + + return decl; + } + /* This is registered as a plugin finalize callback, to print an + instrumentation summary unless in quiet mode. */ + static void + plugin_finalize (void *, void *p) + { + opt_pass *op = (opt_pass *)p; + afl_pass &self = (afl_pass &)*op; + + if (!self.be_quiet) { + if (!self.inst_blocks) + WARNF ("No instrumentation targets found."); + else + OKF ("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); } + } - if (more_than_one != 1) continue; +#define report_fatal_error(msg) BADF(msg) - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; + std::list allowListFiles; + std::list allowListFunctions; + std::list denyListFiles; + std::list denyListFunctions; - /* Make up cur_loc */ - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); + /* Note: this ignore check is also called in isInInstrumentList() */ + bool isIgnoreFunction(function *F) { - /* Update bitmap via external call */ - /* to quote: - * /+ Trace a basic block with some ID +/ - * void __afl_trace(u32 x); - */ + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz - fcall = gimple_build_call( - fndecl, 1, - cur_loc); /* generate the function _call_ to above built reference, with - *1* parameter -> the random const for the location */ - gimple_seq_add_stmt(&seq, fcall); /* and insert into a sequence */ + static const char *ignoreList[] = { - /* Done - grab the entry to the block and insert sequence */ - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_SAME_STMT); + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" - ++finst_blocks; + }; - } + const char *name = IDENTIFIER_POINTER (DECL_NAME (F->decl)); + int len = IDENTIFIER_LENGTH (DECL_NAME (F->decl)); + + for (auto const &ignoreListFunc : ignoreList) { - /* Say something nice. */ - if (!be_quiet) { + if (strncmp (name, ignoreListFunc, len) == 0) { return true; } + + } - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + return false; } - return 0; + void initInstrumentList() { + + char *allowlist = getenv("AFL_GCC_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_GCC_DENYLIST"); + if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); + getline(fileStream, line); -} + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + + } -static unsigned int inline_instrument(function *fun) { - - /* Instrument all the things! */ - basic_block bb; - unsigned finst_blocks = 0; - unsigned fcnt_blocks = 0; - tree one = build_int_cst(unsigned_char_type_node, 1); - // tree zero = build_int_cst(unsigned_char_type_node, 0); - - /* Set up global type declarations */ - tree map_type = build_pointer_type(unsigned_char_type_node); - tree map_ptr_g = - build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_area_ptr", 14), map_type); - TREE_USED(map_ptr_g) = 1; - TREE_STATIC(map_ptr_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(map_ptr_g) = 1; /* External linkage */ - DECL_PRESERVE_P(map_ptr_g) = 1; - DECL_ARTIFICIAL(map_ptr_g) = 1; /* Injected by compiler */ - rest_of_decl_compilation(map_ptr_g, 1, 0); - - tree prev_loc_g = build_decl(UNKNOWN_LOCATION, VAR_DECL, - get_identifier_with_length("__afl_prev_loc", 14), - uint32_type_node); - TREE_USED(prev_loc_g) = 1; - TREE_STATIC(prev_loc_g) = 1; /* Defined elsewhere */ - DECL_EXTERNAL(prev_loc_g) = 1; /* External linkage */ - DECL_PRESERVE_P(prev_loc_g) = 1; - DECL_ARTIFICIAL(prev_loc_g) = 1; /* Injected by compiler */ - set_decl_tls_model(prev_loc_g, TLS_MODEL_REAL); /* TLS attribute */ - rest_of_decl_compilation(prev_loc_g, 1, 0); - - FOR_EACH_BB_FN(bb, fun) { - - gimple_seq seq = NULL; - gimple_stmt_iterator bentry; - ++fcnt_blocks; - - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - - int more_than_one = -1; - edge ep; - edge_iterator eip; - FOR_EACH_EDGE(ep, eip, bb->preds) { - - int count = 0; - if (more_than_one == -1) more_than_one = 0; - - basic_block Pred = ep->src; - edge es; - edge_iterator eis; - FOR_EACH_EDGE(es, eis, Pred->succs) { - - basic_block Succ = es->dest; - if (Succ != NULL) count++; + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } - if (count > 1) more_than_one = 1; + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); } - if (more_than_one != 1) continue; - - /* Bail on this block if we trip the specified ratio */ - if (R(100) >= inst_ratio) continue; - - /* Make up cur_loc */ - - unsigned int rand_loc = R(MAP_SIZE); - tree cur_loc = build_int_cst(uint32_type_node, rand_loc); - - /* Load prev_loc, xor with cur_loc */ - // gimple_assign - tree prev_loc = create_tmp_var_raw(uint32_type_node, "prev_loc"); - gassign *g = gimple_build_assign(prev_loc, VAR_DECL, prev_loc_g); - gimple_seq_add_stmt(&seq, g); // load prev_loc - update_stmt(g); - - // gimple_assign - tree area_off = create_tmp_var_raw(uint32_type_node, "area_off"); - g = gimple_build_assign(area_off, BIT_XOR_EXPR, prev_loc, cur_loc); - gimple_seq_add_stmt(&seq, g); // area_off = prev_loc ^ cur_loc - update_stmt(g); - - /* Update bitmap */ - - // gimple_assign - tree map_ptr = create_tmp_var(map_type, "map_ptr"); - tree map_ptr2 = create_tmp_var(map_type, "map_ptr2"); - - g = gimple_build_assign(map_ptr, map_ptr_g); - gimple_seq_add_stmt(&seq, g); // map_ptr = __afl_area_ptr - update_stmt(g); - -#if 1 - #if 0 - tree addr = build2(ADDR_EXPR, map_type, map_ptr, area_off); - g = gimple_build_assign(map_ptr2, MODIFY_EXPR, addr); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #else - g = gimple_build_assign(map_ptr2, PLUS_EXPR, map_ptr, area_off); - gimple_seq_add_stmt(&seq, g); // map_ptr2 = map_ptr + area_off - update_stmt(g); - #endif - - // gimple_assign - tree tmp1 = create_tmp_var_raw(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, MEM_REF, map_ptr2); - gimple_seq_add_stmt(&seq, g); // tmp1 = *map_ptr2 - update_stmt(g); -#else - tree atIndex = build2(PLUS_EXPR, uint32_type_node, map_ptr, area_off); - tree array_address = build1(ADDR_EXPR, map_type, atIndex); - tree array_access = build1(INDIRECT_REF, map_type, array_address); - tree tmp1 = create_tmp_var(unsigned_char_type_node, "tmp1"); - g = gimple_build_assign(tmp1, array_access); - gimple_seq_add_stmt(&seq, g); // tmp1 = *(map_ptr + area_off) - update_stmt(g); -#endif - // gimple_assign - tree tmp2 = create_tmp_var_raw(unsigned_char_type_node, "tmp2"); - g = gimple_build_assign(tmp2, PLUS_EXPR, tmp1, one); - gimple_seq_add_stmt(&seq, g); // tmp2 = tmp1 + 1 - update_stmt(g); + if (denylist) { - // TODO: neverZero: here we have to check if tmp3 == 0 - // and add 1 if so + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); + getline(fileStream, line); - // gimple_assign - // tree map_ptr3 = create_tmp_var_raw(map_type, "map_ptr3"); - g = gimple_build_assign(map_ptr2, INDIRECT_REF, tmp2); - gimple_seq_add_stmt(&seq, g); // *map_ptr2 = tmp2 - update_stmt(g); + while (fileStream) { - /* Set prev_loc to cur_loc >> 1 */ + int is_file = -1; + std::size_t npos; + std::string original_line = line; - // gimple_assign - tree shifted_loc = build_int_cst(TREE_TYPE(prev_loc_g), rand_loc >> 1); - tree prev_loc2 = create_tmp_var_raw(uint32_type_node, "prev_loc2"); - g = gimple_build_assign(prev_loc2, shifted_loc); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); - g = gimple_build_assign(prev_loc_g, prev_loc2); - gimple_seq_add_stmt(&seq, g); // __afl_prev_loc = cur_loc >> 1 - update_stmt(g); + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); - /* Done - grab the entry to the block and insert sequence */ + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); - bentry = gsi_after_labels(bb); - gsi_insert_seq_before(&bentry, seq, GSI_NEW_STMT); + if (line.compare(0, 4, "fun:") == 0) { - ++finst_blocks; + is_file = 0; + line = line.substr(4); - } + } else if (line.compare(0, 9, "function:") == 0) { - /* Say something nice. */ - if (!be_quiet) { + is_file = 0; + line = line.substr(9); - if (!finst_blocks) - WARNF(G_("No instrumentation targets found in " cBRI "%s" cRST), - function_name(fun)); - else if (finst_blocks < fcnt_blocks) - OKF(G_("Instrumented %2u /%2u locations in " cBRI "%s" cRST), - finst_blocks, fcnt_blocks, function_name(fun)); - else - OKF(G_("Instrumented %2u locations in " cBRI "%s" cRST), finst_blocks, - function_name(fun)); + } else if (line.compare(0, 4, "src:") == 0) { - } + is_file = 1; + line = line.substr(4); - return 0; + } else if (line.compare(0, 7, "source:") == 0) { -} + is_file = 1; + line = line.substr(7); -/* -------------------------------------------------------------------------- */ -/* -- Boilerplate and initialization ---------------------------------------- */ + } -static const struct pass_data afl_pass_data = { + if (line.find(":") != std::string::npos) { - .type = GIMPLE_PASS, - .name = "afl-inst", - .optinfo_flags = OPTGROUP_NONE, + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - .tv_id = TV_NONE, - .properties_required = 0, - .properties_provided = 0, - .properties_destroyed = 0, - .todo_flags_start = 0, - // NOTE(aseipp): it's very, very important to include - // at least 'TODO_update_ssa' here so that GCC will - // properly update the resulting SSA form, e.g., to - // include new PHI nodes for newly added symbols or - // names. Do not remove this. Do not taunt Happy Fun - // Ball. - .todo_flags_finish = TODO_update_ssa | TODO_verify_il | TODO_cleanup_cfg, + } -}; + if (line.length() > 0) { -namespace { + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function -class afl_pass : public gimple_opt_pass { + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); - private: - bool do_ext_call; + } - public: - afl_pass(bool ext_call, gcc::context *g) - : gimple_opt_pass(afl_pass_data, g), do_ext_call(ext_call) { + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } - unsigned int execute(function *fun) override { + std::string getSourceName(function *F) { + + return DECL_SOURCE_FILE(F->decl); + + } - if (!myInstrumentList.empty()) { + bool isInInstrumentList(function *F) { - bool instrumentBlock = false; - std::string instFilename; - unsigned int instLine = 0; + bool return_default = true; - /* EXPR_FILENAME - This macro returns the name of the file in which the entity was declared, - as a char*. For an entity declared implicitly by the compiler (like - __builtin_ memcpy), this will be the string "". - */ - const char *fname = DECL_SOURCE_FILE(fun->decl); + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (isIgnoreFunction(F)) return false; - if (0 != strncmp("", fname, 10) && - 0 != strncmp("", fname, 10)) { + if (!denyListFiles.empty() || !denyListFunctions.empty()) { - instFilename = fname; - instLine = DECL_SOURCE_LINE(fun->decl); + if (!denyListFunctions.empty()) { - /* Continue only if we know where we actually are */ - if (!instFilename.empty()) { + std::string instFunction = IDENTIFIER_POINTER (DECL_NAME (F->decl)); - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.length() >= it->length()) { + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - if (instFilename.compare(instFilename.length() - it->length(), - it->length(), *it) == 0) { + if (instFunction.length() >= it->length()) { - instrumentBlock = true; - break; + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - } + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; - } + } - } + } - } + } } - /* Either we couldn't figure out our location or the location is - * not in the instrument list, so we skip instrumentation. */ - if (!instrumentBlock) { + if (!denyListFiles.empty()) { - if (!be_quiet) { + std::string source_file = getSourceName(F); - if (!instFilename.empty()) - SAYF(cYEL "[!] " cBRI - "Not in instrument list, skipping %s line %u...\n", - instFilename.c_str(), instLine); - else - SAYF(cYEL "[!] " cBRI "No filename information found, skipping it"); + if (!source_file.empty()) { - } + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { - return 0; + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - return do_ext_call ? ext_call_instrument(fun) : inline_instrument(fun); + return false; - } + } -}; /* class afl_pass */ + } -} // namespace + } -static struct opt_pass *make_afl_pass(bool ext_call, gcc::context *ctxt) { + } else { - return new afl_pass(ext_call, ctxt); + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); -} + } -/* -------------------------------------------------------------------------- */ -/* -- Initialization -------------------------------------------------------- */ + } -int plugin_is_GPL_compatible = 1; + } -static struct plugin_info afl_plugin_info = { + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { - .version = "20200519", - .help = "AFL++ gcc plugin\n", + return_default = false; -}; + if (!allowListFunctions.empty()) { -int plugin_init(struct plugin_name_args * plugin_info, - struct plugin_gcc_version *version) { + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - struct register_pass_info afl_pass_info; - struct timeval tv; - struct timezone tz; - u32 rand_seed; + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { - /* Setup random() so we get Actually Random(TM) outputs from R() */ - gettimeofday(&tv, &tz); - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - SR(rand_seed); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - /* Pass information */ - afl_pass_info.pass = make_afl_pass(inst_ext, g); - afl_pass_info.reference_pass_name = "ssa"; - afl_pass_info.ref_pass_instance_number = 1; - afl_pass_info.pos_op = PASS_POS_INSERT_AFTER; + if (instFunction.length() >= it->length()) { - if (!plugin_default_version_check(version, &gcc_version)) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - FATAL(G_("Incompatible gcc/plugin versions! Expected GCC %d.%d"), - GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; - } + } - /* Show a banner */ - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + } - SAYF(G_(cCYA "afl-gcc-pass" VERSION cRST - " initially by , maintainer: hexcoder-\n")); + } - } else + } - be_quiet = 1; + if (!allowListFiles.empty()) { - /* Decide instrumentation ratio */ - char *inst_ratio_str = getenv("AFL_INST_RATIO"); + std::string source_file = getSourceName(F); - if (inst_ratio_str) { + if (!source_file.empty()) { - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || - inst_ratio > 100) - FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); - else { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - if (!be_quiet) - ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), - inst_ext ? G_("Call-based") : G_("Inline"), inst_ratio, - getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - } + if (source_file.length() >= it->length()) { - } + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - char *instInstrumentListFilename = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!instInstrumentListFilename) - instInstrumentListFilename = getenv("AFL_GCC_WHITELIST"); - if (instInstrumentListFilename) { + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER (DECL_NAME (F->decl)), + source_file.c_str()); + return true; - std::string line; - std::ifstream fileStream; - fileStream.open(instInstrumentListFilename); - if (!fileStream) PFATAL("Unable to open AFL_GCC_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { + } - myInstrumentList.push_back(line); - getline(fileStream, line); + } - } + } - } else if (!be_quiet && (getenv("AFL_LLVM_WHITELIST") || + } else { - getenv("AFL_LLVM_INSTRUMENT_FILE"))) { + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER (DECL_NAME (F->decl))); + return false; - SAYF(cYEL "[-] " cRST - "AFL_LLVM_INSTRUMENT_FILE environment variable detected - did " - "you mean AFL_GCC_INSTRUMENT_FILE?\n"); + } + + } + + } + + return return_default; } - /* Go go gadget */ - register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, - &afl_plugin_info); - register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, - &afl_pass_info); - return 0; + +}; + +static struct plugin_info afl_plugin = + { + .version = "20200907", + .help = G_("AFL gcc plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +\n\ +Set AFL_INST_RATIO in the environment to a number from 0 to 100\n\ +to control how likely a block will be chosen for instrumentation.\n\ +\n\ +Specify -frandom-seed for reproducible instrumentation.\n\ +"), + }; } +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int +plugin_init (struct plugin_name_args *info, + struct plugin_gcc_version *version) +{ + if (!plugin_default_version_check (version, &gcc_version)) + FATAL (G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + + /* Show a banner. */ + bool quiet = false; + if (isatty (2) && !getenv ("AFL_QUIET")) + SAYF (cCYA "afl-gcc-pass " cBRI VERSION cRST " by \n"); + else + quiet = true; + + /* Decide instrumentation ratio. */ + int inst_ratio = 100; + if (char *inst_ratio_str = getenv ("AFL_INST_RATIO")) + if (sscanf (inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL (G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + + /* Initialize the random number generator with GCC's random seed, in + case it was specified in the command line's -frandom-seed for + reproducible instrumentation. */ + srandom (get_random_seed (false)); + + const char *name = info->base_name; + register_callback (name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass *aflp = new afl_pass (quiet, inst_ratio); + struct register_pass_info pass_info = + { + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + }; + register_callback (name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback (name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); + + if (!quiet) + ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + + return 0; +} diff --git a/src/afl-cc.c b/src/afl-cc.c index ddda3845..78245d4b 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1335,6 +1335,12 @@ int main(int argc, char **argv, char **envp) { AFL_REAL_LD, AFL_CLANG_FLTO); #endif + SAYF( + "\nGCC Plugin-specific environment variables:\n" + "AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + "AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + "AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); } SAYF( diff --git a/test/test-performance.sh b/test/test-performance.sh index cee46060..cd6eea64 100755 --- a/test/test-performance.sh +++ b/test/test-performance.sh @@ -117,6 +117,30 @@ test -e ../afl-clang-fast -a -e ../afl-fuzz && { } || $ECHO "$RED[!] llvm_mode instrumentation failed" } || $ECHO "$YELLOW[-] llvm_mode is not compiled, cannot test" +$ECHO "$BLUE[*] Testing: gcc_plugin" +GCCP=x +test -e ../afl-gcc-fast -a -e ../afl-fuzz && { + ../afl-gcc-fast -o test-instr.gccp ../test-instr.c > /dev/null 2>&1 + test -e test-instr.gccp && { + $ECHO "$GREEN[+] gcc_plugin compilation succeeded" + mkdir -p in + echo 0 > in/in + $ECHO "$GREY[*] running afl-fuzz for gcc_plugin for 30 seconds" + { + ../afl-fuzz -V 30 -s 123 -m ${MEM_LIMIT} -i in -o out-gccp -- ./test-instr.gccp + } >>errors 2>&1 + test -n "$( ls out-gccp/queue/id:000002* 2> /dev/null )" && { + GCCP=`grep execs_done out-gccp/fuzzer_stats | awk '{print$3}'` + } || { + echo CUT---------------------------------------------------------------- + cat errors + echo CUT---------------------------------------------------------------- + $ECHO "$RED[!] afl-fuzz is not working correctly with gcc_plugin" + } + rm -rf in out-gccp errors test-instr.gccp + } || $ECHO "$RED[!] gcc_plugin instrumentation failed" +} || $ECHO "$YELLOW[-] gcc_plugin is not compiled, cannot test" + $ECHO "$BLUE[*] Testing: qemu_mode" QEMU=x test -e ../afl-qemu-trace -a -e ../afl-fuzz && { @@ -147,6 +171,9 @@ LAST_GCC= LOW_LLVM= HIGH_LLVM= LAST_LLVM= +LOW_GCCP= +HIGH_GCCP= +LAST_GCCP= LOW_QEMU= HIGH_QEMU= LAST_QEMU= @@ -155,12 +182,15 @@ test -s $FILE && { while read LINE; do G=`echo $LINE | awk '{print$1}'` L=`echo $LINE | awk '{print$2}'` - Q=`echo $LINE | awk '{print$3}'` + P=`echo $LINE | awk '{print$3}'` + Q=`echo $LINE | awk '{print$4}'` test "$G" = x && G= test "$L" = x && L= + test "$P" = x && P= test "$Q" = x && Q= test -n "$G" && LAST_GCC=$G test -n "$L" && LAST_LLVM=$L + test -n "$P" && LAST_GCCP=$P test -n "$Q" && LAST_QEMU=$Q test -n "$G" -a -z "$LOW_GCC" && LOW_GCC=$G || { test -n "$G" -a "$G" -lt "$LOW_GCC" 2> /dev/null && LOW_GCC=$G @@ -168,6 +198,9 @@ test -s $FILE && { test -n "$L" -a -z "$LOW_LLVM" && LOW_LLVM=$L || { test -n "$L" -a "$L" -lt "$LOW_LLVM" 2> /dev/null && LOW_LLVM=$L } + test -n "$P" -a -z "$LOW_GCCP" && LOW_GCCP=$P || { + test -n "$P" -a "$P" -lt "$LOW_GCCP" 2> /dev/null && LOW_GCCP=$P + } test -n "$Q" -a -z "$LOW_QEMU" && LOW_QEMU=$Q || { test -n "$Q" -a "$Q" -lt "$LOW_QEMU" 2> /dev/null && LOW_QEMU=$Q } @@ -177,6 +210,9 @@ test -s $FILE && { test -n "$L" -a -z "$HIGH_LLVM" && HIGH_LLVM=$L || { test -n "$L" -a "$L" -gt "$HIGH_LLVM" 2> /dev/null && HIGH_LLVM=$L } + test -n "$P" -a -z "$HIGH_GCCP" && HIGH_GCCP=$P || { + test -n "$P" -a "$P" -gt "$HIGH_GCCP" 2> /dev/null && HIGH_GCCP=$P + } test -n "$Q" -a -z "$HIGH_QEMU" && HIGH_QEMU=$Q || { test -n "$Q" -a "$Q" -gt "$HIGH_QEMU" 2> /dev/null && HIGH_QEMU=$Q } @@ -184,11 +220,12 @@ test -s $FILE && { $ECHO "$YELLOW[!] Reading saved data from $FILE completed, please compare the results:" $ECHO "$BLUE[!] afl-cc: lowest=$LOW_GCC highest=$HIGH_GCC last=$LAST_GCC current=$GCC" $ECHO "$BLUE[!] llvm_mode: lowest=$LOW_LLVM highest=$HIGH_LLVM last=$LAST_LLVM current=$LLVM" + $ECHO "$BLUE[!] gcc_plugin: lowest=$LOW_GCCP highest=$HIGH_GCCP last=$LAST_GCCP current=$GCCP" $ECHO "$BLUE[!] qemu_mode: lowest=$LOW_QEMU highest=$HIGH_QEMU last=$LAST_QEMU current=$QEMU" } || { $ECHO "$YELLOW[!] First run, just saving data" - $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM qemu_mode=$QEMU" + $ECHO "$BLUE[!] afl-gcc=$GCC llvm_mode=$LLVM gcc_plugin=$GCCP qemu_mode=$QEMU" } -echo "$GCC $LLVM $QEMU" >> $FILE +echo "$GCC $LLVM $GCCP $QEMU" >> $FILE $ECHO "$GREY[*] done." $ECHO "$RESET" -- cgit 1.4.1 From c091340a85694c5de1125a93366f2733959487f5 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 8 Sep 2020 16:15:31 +0200 Subject: new gcc_plugin integration --- GNUmakefile.llvm | 2 +- README.md | 2 ++ docs/Changelog.md | 2 ++ docs/INSTALL.md | 2 +- src/afl-cc.c | 76 +++++++++++++++++++++++++++---------------------------- 5 files changed, 44 insertions(+), 40 deletions(-) (limited to 'docs') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 3eefdf90..604fb291 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -229,7 +229,7 @@ CFLAGS_SAFE := -Wall -g -Wno-pointer-sign -I ./include/ -I ./instrumentation -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" \ -DLLVM_BINDIR=\"$(LLVM_BINDIR)\" -DVERSION=\"$(VERSION)\" \ -DLLVM_LIBDIR=\"$(LLVM_LIBDIR)\" -DLLVM_VERSION=\"$(LLVMVER)\" \ - -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ + -Wno-deprecated -DAFL_CLANG_FLTO=\"$(AFL_CLANG_FLTO)\" \ -DAFL_REAL_LD=\"$(AFL_REAL_LD)\" \ -DAFL_CLANG_LDPATH=\"$(AFL_CLANG_LDPATH)\" \ -DAFL_CLANG_FUSELD=\"$(AFL_CLANG_FUSELD)\" \ diff --git a/README.md b/README.md index c886489d..fb59835c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ behaviours: only one compiler: afl-cc. All previous compilers now symlink to this one compiler. All instrumentation source code is now in the `instrumentation/` folder. + * The gcc_plugin was replaced with a new version submitted by AdaCore, that + supports more features, thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current ninja build tool version and python3 setuptools are required. qemu_mode also got new options like snapshotting, instrumenting specific diff --git a/docs/Changelog.md b/docs/Changelog.md index 73613452..c42ab629 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -25,6 +25,8 @@ sending a mail to . skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule - instrumentation + - We received an enhanced gcc_plugin module from AdaCore, thank you + very much!! - not overriding -Ox or -fno-unroll-loops anymore - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 93a46caf..8e1e266f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -28,7 +28,7 @@ If you are using clang, please review README.llvm.md; the LLVM integration mode can offer substantial performance gains compared to the traditional approach. -Likewise, if you are using GCC, please review gcc_plugin/README.md. +Likewise, if you are using GCC, please review instrumentation/README.gcc_plugin.md. You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but diff --git a/src/afl-cc.c b/src/afl-cc.c index 78245d4b..47a33cd0 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1196,7 +1196,7 @@ int main(int argc, char **argv, char **envp) { " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " - " simple\n" + " yes\n" " [GCC] simple gcc: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", @@ -1270,8 +1270,29 @@ int main(int argc, char **argv, char **envp) { " AFL_CXX: path to the C++ compiler to use\n" " AFL_DEBUG: enable developer debugging output\n" " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" " AFL_INST_RATIO: percentage of branches to instrument\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); + + if (have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by filename\n"); + + if (have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" #if LLVM_MAJOR < 9 " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #else @@ -1288,25 +1309,13 @@ int main(int argc, char **argv, char **envp) { "functions\n" " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " "instrument allow/\n" - " deny listing (selective instrumentation)\n" - " AFL_NO_BUILTIN: no builtins for string compare functions (for " - "libtokencap.so)\n" - " AFL_PATH: path to instrumenting pass and runtime " - "(afl-compiler-rt.*o)\n" - " AFL_LLVM_DOCUMENT_IDS: document edge IDs given to which function " - "(LTO only)\n" - " AFL_QUIET: suppress verbose output\n" - " AFL_USE_ASAN: activate address sanitizer\n" - " AFL_USE_CFISAN: activate control flow sanitizer\n" - " AFL_USE_MSAN: activate memory sanitizer\n" - " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n"); + " deny listing (selective instrumentation)\n"); + if (have_llvm) SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - " AFL_LLVM_INSTRUMENT: set instrumentation mode: CLASSIC, INSTRIM, " - "PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" @@ -1315,36 +1324,27 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " "INSTRIM)\n" " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC and INSTRIM)\n"); + "CLASSIC & INSTRIM)\n"); #ifdef AFL_CLANG_FLTO - SAYF( + if (have_lto) + SAYF( "\nLTO/afl-clang-lto specific environment variables:\n" - "AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), e.g. " "0x10000\n" - "AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " - "functions they are in into this file\n" - "AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding functions\n" + " into this file\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " "global var\n" - "AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for a " "bb\n" - "AFL_REAL_LD: use this lld linker instead of the compiled in path\n" - "\nafl-clang-lto was built with linker target \"%s\" and LTO flags " - "\"%s\"\n" - "If anything fails - be sure to read README.lto.md!\n", - AFL_REAL_LD, AFL_CLANG_FLTO); + " AFL_REAL_LD: use this lld linker instead of the compiled in path\n" + "If anything fails - be sure to read README.lto.md!\n"); #endif - - SAYF( - "\nGCC Plugin-specific environment variables:\n" - "AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" - "AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" - "AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " - "filename\n"); } SAYF( - "For any information on the available instrumentations and options " + "\nFor any information on the available instrumentations and options " "please \n" "consult the README.md, especially section 3.1 about instrumenting " "targets.\n\n"); -- cgit 1.4.1 From a4cac3fce58c39e13ab120df7341b03781603b34 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 9 Sep 2020 17:49:43 +0200 Subject: new custom mutator: symcc --- custom_mutators/symcc/Makefile | 14 +++ custom_mutators/symcc/README.md | 15 +++ custom_mutators/symcc/symcc.c | 233 ++++++++++++++++++++++++++++++++++++++++ docs/Changelog.md | 1 + 4 files changed, 263 insertions(+) create mode 100644 custom_mutators/symcc/Makefile create mode 100644 custom_mutators/symcc/README.md create mode 100644 custom_mutators/symcc/symcc.c (limited to 'docs') diff --git a/custom_mutators/symcc/Makefile b/custom_mutators/symcc/Makefile new file mode 100644 index 00000000..7e2f7b4d --- /dev/null +++ b/custom_mutators/symcc/Makefile @@ -0,0 +1,14 @@ + +ifdef DEBUG + CFLAGS += -DDEBUG +endif + +all: symcc-mutator.so + +CFLAGS += -O3 -funroll-loops + +symcc-mutator.so: symcc.c + $(CC) $(CFLAGS) $(CPPFLAGS) -g -I../../include -shared -fPIC -o symcc-mutator.so symcc.c + +clean: + rm -f symcc-mutator.so *.o *~ core diff --git a/custom_mutators/symcc/README.md b/custom_mutators/symcc/README.md new file mode 100644 index 00000000..337362ae --- /dev/null +++ b/custom_mutators/symcc/README.md @@ -0,0 +1,15 @@ +# custum mutator: symcc + +This uses the excellent symcc to find new paths into the target. + +To use this custom mutator follow the steps in the symcc repository +[https://github.com/eurecom-s3/symcc/](https://github.com/eurecom-s3/symcc/) +on how to build symcc and how to instrument a target binary (the same target +that you are fuzzing). + +The target program compiled with symcc has to be pointed to with the +`SYMCC_TARGET` environment variable. + +just type `make` to build this custom mutator. + +```SYMCC_TARGET=/prg/to/symcc/compiled/target AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/symcc/symcc-mutator.so afl-fuzz ...``` diff --git a/custom_mutators/symcc/symcc.c b/custom_mutators/symcc/symcc.c new file mode 100644 index 00000000..d0572f4e --- /dev/null +++ b/custom_mutators/symcc/symcc.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include "config.h" +#include "debug.h" +#include "afl-fuzz.h" +#include "common.h" + +afl_state_t *afl_struct; + +#ifdef DEBUG + #define DBG(x...) fprintf(stderr, x) +#else + #define DBG(x...) {} +#endif + +typedef struct my_mutator { + + afl_state_t *afl; + u8 * mutator_buf; + u8 * out_dir; + u8 * target; + uint32_t seed; + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + if (getenv("AFL_CUSTOM_MUTATOR_ONLY")) + FATAL("the symcc module cannot be used with AFL_CUSTOM_MUTATOR_ONLY."); + + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) { + + perror("mutator_buf alloc"); + return NULL; + + } + + if (!(data->target = getenv("SYMCC_TARGET"))) + FATAL( + "SYMCC_TARGET not defined, this should point to the full path of the " + "symcc compiled binary."); + + if (!(data->out_dir = getenv("SYMCC_OUTPUT_DIR"))) { + + data->out_dir = alloc_printf("%s/symcc", afl->out_dir); + setenv("SYMCC_OUTPUT_DIR", data->out_dir, 1); + + } + + int pid = fork(); + + if (pid == -1) return NULL; + + if (pid) pid = waitpid(pid, NULL, 0); + + if (pid == 0) { + + char *args[4]; + args[0] = "/bin/rm"; + args[1] = "-rf"; + args[2] = data->out_dir; + args[3] = NULL; + execvp(args[0], args); + DBG("exec:FAIL\n"); + exit(-1); + + } + + data->afl = afl; + data->seed = seed; + afl_struct = afl; + + if (mkdir(data->out_dir, 0755)) + PFATAL("Could not create directory %s", data->out_dir); + DBG("out_dir=%s, target=%s\n", data->out_dir, data->target); + + return data; + +} + +/* When a new queue entry is added we run this input with the symcc + instrumented binary */ +void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + int pid = fork(); + + if (pid == -1) return; + + if (pid) pid = waitpid(pid, NULL, 0); + + if (pid == 0) { + + setenv("SYMCC_INPUT_FILE", afl_struct->fsrv.out_file, 1); + + if (afl_struct->fsrv.use_stdin) { + + u8 *fn = alloc_printf("%s/%s", afl_struct->out_dir, filename_new_queue); + int fd = open(fn, O_RDONLY); + + if (fd >= 0) { + + ssize_t r = read(fd, data->mutator_buf, MAX_FILE); + close(fd); + DBG("fn=%s, fd=%d, size=%ld\n", fn, fd, r); + if (r <= 0) return; + close(0); + ck_write(0, data->mutator_buf, r, fn); + ck_free(fn); + + } + + } + + DBG("exec=%s\n", data->target); + close(1); + close(2); + dup2(afl_struct->fsrv.dev_null_fd, 1); + dup2(afl_struct->fsrv.dev_null_fd, 2); + execvp(data->target, afl_struct->argv); + DBG("exec=FAIL\n"); + exit(-1); + + } + +} + +uint32_t afl_custom_fuzz_count(my_mutator_t *data, const u8 *buf, + size_t buf_size) { + + uint32_t count = 0, i; + struct dirent **nl; + int32_t items = scandir(data->out_dir, &nl, NULL, NULL); + + if (items > 0) { + + for (i = 0; i < (u32)items; ++i) { + + struct stat st; + u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name); + DBG("test=%s\n", fn); + if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) { + + DBG("found=%s\n", fn); + count++; + + } + + ck_free(fn); + free(nl[i]); + + } + + free(nl); + + } + + DBG("dir=%s, count=%u\n", data->out_dir, count); + return count; + +} + +/* here we actualy just read the files generated from symcc */ +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, + u8 **out_buf, uint8_t *add_buf, size_t add_buf_size, + size_t max_size) { + + struct dirent **nl; + int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); + size_t size = 0; + + if (items <= 0) return 0; + + for (i = 0; i < (u32)items; ++i) { + + struct stat st; + u8 * fn = alloc_printf("%s/%s", data->out_dir, nl[i]->d_name); + + if (done == 0) { + + if (stat(fn, &st) == 0 && S_ISREG(st.st_mode) && st.st_size) { + + int fd = open(fn, O_RDONLY); + + if (fd >= 0) { + + size = read(fd, data->mutator_buf, max_size); + *out_buf = data->mutator_buf; + close(fd); + done = 1; + + } + + } + + unlink(fn); + + } + + ck_free(fn); + free(nl[i]); + + } + + free(nl); + DBG("FUZZ size=%lu\n", size); + return size; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->mutator_buf); + free(data); + +} + diff --git a/docs/Changelog.md b/docs/Changelog.md index c42ab629..1e5c1b95 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,7 @@ sending a mail to . -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp + - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ ### Version ++2.68c (release) -- cgit 1.4.1 From 380051868a7531830d94d312f0f11b0e19e3284f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Sep 2020 15:26:46 +0200 Subject: add libfuzzer custom mutator, minor enhancements and fixes --- GNUmakefile | 4 +- README.md | 5 + custom_mutators/README.md | 4 +- custom_mutators/grammar_mutator/README.md | 6 + .../grammar_mutator/build_grammar_mutator.sh | 17 + custom_mutators/honggfuzz/Makefile | 6 +- custom_mutators/honggfuzz/README.md | 4 +- custom_mutators/libfuzzer/FuzzerBuiltins.h | 35 + custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h | 72 ++ custom_mutators/libfuzzer/FuzzerCommand.h | 178 ++++ custom_mutators/libfuzzer/FuzzerCorpus.h | 581 ++++++++++ custom_mutators/libfuzzer/FuzzerCrossOver.cpp | 60 ++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp | 344 ++++++ custom_mutators/libfuzzer/FuzzerDataFlowTrace.h | 135 +++ custom_mutators/libfuzzer/FuzzerDefs.h | 75 ++ custom_mutators/libfuzzer/FuzzerDictionary.h | 118 ++ custom_mutators/libfuzzer/FuzzerDriver.cpp | 1122 ++++++++++++++++++++ custom_mutators/libfuzzer/FuzzerExtFunctions.def | 50 + custom_mutators/libfuzzer/FuzzerExtFunctions.h | 34 + .../libfuzzer/FuzzerExtFunctionsDlsym.cpp | 60 ++ .../libfuzzer/FuzzerExtFunctionsWeak.cpp | 63 ++ .../libfuzzer/FuzzerExtFunctionsWindows.cpp | 95 ++ custom_mutators/libfuzzer/FuzzerExtraCounters.cpp | 71 ++ custom_mutators/libfuzzer/FuzzerFlags.def | 197 ++++ custom_mutators/libfuzzer/FuzzerFork.cpp | 501 +++++++++ custom_mutators/libfuzzer/FuzzerFork.h | 24 + custom_mutators/libfuzzer/FuzzerIO.cpp | 248 +++++ custom_mutators/libfuzzer/FuzzerIO.h | 112 ++ custom_mutators/libfuzzer/FuzzerIOPosix.cpp | 223 ++++ custom_mutators/libfuzzer/FuzzerIOWindows.cpp | 513 +++++++++ custom_mutators/libfuzzer/FuzzerInterface.h | 79 ++ custom_mutators/libfuzzer/FuzzerInternal.h | 173 +++ custom_mutators/libfuzzer/FuzzerLoop.cpp | 1087 +++++++++++++++++++ custom_mutators/libfuzzer/FuzzerMerge.cpp | 485 +++++++++ custom_mutators/libfuzzer/FuzzerMerge.h | 87 ++ custom_mutators/libfuzzer/FuzzerMutate.cpp | 720 +++++++++++++ custom_mutators/libfuzzer/FuzzerMutate.h | 158 +++ custom_mutators/libfuzzer/FuzzerOptions.h | 90 ++ custom_mutators/libfuzzer/FuzzerPlatform.h | 163 +++ custom_mutators/libfuzzer/FuzzerRandom.h | 38 + custom_mutators/libfuzzer/FuzzerSHA1.cpp | 269 +++++ custom_mutators/libfuzzer/FuzzerSHA1.h | 32 + custom_mutators/libfuzzer/FuzzerTracePC.cpp | 819 ++++++++++++++ custom_mutators/libfuzzer/FuzzerTracePC.h | 291 +++++ custom_mutators/libfuzzer/FuzzerUtil.cpp | 314 ++++++ custom_mutators/libfuzzer/FuzzerUtil.h | 117 ++ custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp | 205 ++++ custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp | 658 ++++++++++++ custom_mutators/libfuzzer/FuzzerUtilLinux.cpp | 43 + custom_mutators/libfuzzer/FuzzerUtilPosix.cpp | 239 +++++ custom_mutators/libfuzzer/FuzzerUtilWindows.cpp | 279 +++++ custom_mutators/libfuzzer/FuzzerValueBitMap.h | 73 ++ custom_mutators/libfuzzer/Makefile | 81 ++ custom_mutators/libfuzzer/README.md | 24 + custom_mutators/libfuzzer/libfuzzer.cpp | 147 +++ custom_mutators/libfuzzer/libfuzzer.inc | 36 + custom_mutators/symcc/symcc.c | 7 +- docs/Changelog.md | 2 + include/afl-prealloc.h | 2 +- include/alloc-inl.h | 4 +- include/list.h | 1 + src/afl-cc.c | 8 +- 62 files changed, 11668 insertions(+), 20 deletions(-) create mode 100644 custom_mutators/grammar_mutator/README.md create mode 100755 custom_mutators/grammar_mutator/build_grammar_mutator.sh create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltins.h create mode 100644 custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h create mode 100644 custom_mutators/libfuzzer/FuzzerCommand.h create mode 100644 custom_mutators/libfuzzer/FuzzerCorpus.h create mode 100644 custom_mutators/libfuzzer/FuzzerCrossOver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerDataFlowTrace.h create mode 100644 custom_mutators/libfuzzer/FuzzerDefs.h create mode 100644 custom_mutators/libfuzzer/FuzzerDictionary.h create mode 100644 custom_mutators/libfuzzer/FuzzerDriver.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.def create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctions.h create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerExtraCounters.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFlags.def create mode 100644 custom_mutators/libfuzzer/FuzzerFork.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerFork.h create mode 100644 custom_mutators/libfuzzer/FuzzerIO.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIO.h create mode 100644 custom_mutators/libfuzzer/FuzzerIOPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerIOWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerInterface.h create mode 100644 custom_mutators/libfuzzer/FuzzerInternal.h create mode 100644 custom_mutators/libfuzzer/FuzzerLoop.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMerge.h create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerMutate.h create mode 100644 custom_mutators/libfuzzer/FuzzerOptions.h create mode 100644 custom_mutators/libfuzzer/FuzzerPlatform.h create mode 100644 custom_mutators/libfuzzer/FuzzerRandom.h create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerSHA1.h create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerTracePC.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtil.h create mode 100644 custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilLinux.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilPosix.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerUtilWindows.cpp create mode 100644 custom_mutators/libfuzzer/FuzzerValueBitMap.h create mode 100644 custom_mutators/libfuzzer/Makefile create mode 100644 custom_mutators/libfuzzer/README.md create mode 100644 custom_mutators/libfuzzer/libfuzzer.cpp create mode 100644 custom_mutators/libfuzzer/libfuzzer.inc (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index b9a017d2..d47f8247 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -465,9 +465,9 @@ code-format: ./.custom-format.py -i instrumentation/*.h ./.custom-format.py -i instrumentation/*.cc ./.custom-format.py -i instrumentation/*.c - ./.custom-format.py -i custom_mutators/*/*.c + ./.custom-format.py -i custom_mutators/*/*.c* @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( - ./.custom-format.py -i examples/*/*.c + ./.custom-format.py -i examples/*/*.c* ./.custom-format.py -i examples/*/*.h ./.custom-format.py -i test/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.c diff --git a/README.md b/README.md index 2fc9d807..c2108e93 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,11 @@ How to do this is described below. Then build the target. (Usually with `make`) +**NOTE**: sometimes configure and build systems are fickle and do not like +stderr output (and think this means a test failure) - which is something +afl++ like to do to show statistics. It is recommended to disable them via +`export AFL_QUIET=1`. + ##### configure For `configure` build systems this is usually done by: diff --git a/custom_mutators/README.md b/custom_mutators/README.md index 993ccaa1..0cf52746 100644 --- a/custom_mutators/README.md +++ b/custom_mutators/README.md @@ -12,9 +12,7 @@ git submodule init git submodule update ``` -otherwise just checkout the repository here with either -`git clone https://github.com/AFLplusplus/Grammar-Mutator` or -`svn co https://github.com/AFLplusplus/Grammar-Mutator`. +otherwise just use the script: `grammar_mutator/build_grammar_mutator.sh` Read the [Grammar-Mutator/README.md](Grammar-Mutator/README.md) on how to use it. diff --git a/custom_mutators/grammar_mutator/README.md b/custom_mutators/grammar_mutator/README.md new file mode 100644 index 00000000..a015744c --- /dev/null +++ b/custom_mutators/grammar_mutator/README.md @@ -0,0 +1,6 @@ +# Grammar-Mutator + +This is just a stub directory that will clone the real grammar mutator +directory. + +Execute `./build_grammar_mutator.sh` to set everything up. diff --git a/custom_mutators/grammar_mutator/build_grammar_mutator.sh b/custom_mutators/grammar_mutator/build_grammar_mutator.sh new file mode 100755 index 00000000..f3f5e164 --- /dev/null +++ b/custom_mutators/grammar_mutator/build_grammar_mutator.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test -d Grammar-Mutator || git clone --depth=1 https://github.com/AFLplusplus/Grammar-Mutator + +cd Grammar-Mutator || exit 1 +git stash ; git pull + +wget -c https://www.antlr.org/download/antlr-4.8-complete.jar + +echo +echo +echo "All successfully prepared!" +echo "To build for your grammar just do:" +echo " cd Grammar_Mutator" +echo " make GRAMMAR_FILE=/path/to/your/grammar" +echo "You will find a JSON and RUBY grammar in Grammar_Mutator/grammars to play with." +echo diff --git a/custom_mutators/honggfuzz/Makefile b/custom_mutators/honggfuzz/Makefile index 1d46f163..5c2fcddb 100644 --- a/custom_mutators/honggfuzz/Makefile +++ b/custom_mutators/honggfuzz/Makefile @@ -1,10 +1,10 @@ CFLAGS = -O3 -funroll-loops -fPIC -Wl,-Bsymbolic -all: honggfuzz.so +all: honggfuzz-mutator.so -honggfuzz.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c - $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz.so honggfuzz.c mangle.c ../../src/afl-performance.c +honggfuzz-mutator.so: honggfuzz.c input.h mangle.c ../../src/afl-performance.c + $(CC) $(CFLAGS) -I../../include -I. -shared -o honggfuzz-mutator.so honggfuzz.c mangle.c ../../src/afl-performance.c update: @# seriously? --unlink is a dud option? sigh ... diff --git a/custom_mutators/honggfuzz/README.md b/custom_mutators/honggfuzz/README.md index 8824976f..e1cab281 100644 --- a/custom_mutators/honggfuzz/README.md +++ b/custom_mutators/honggfuzz/README.md @@ -1,12 +1,12 @@ # custum mutator: honggfuzz mangle -this is the very good honggfuzz mutator in mangle.c as a custom mutator +this is the honggfuzz mutator in mangle.c as a custom mutator module for afl++. It is the original mangle.c, mangle.h and honggfuzz.h with a lot of mocking around it :-) just type `make` to build -```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz.so afl-fuzz ...``` +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/honggfuzz/honggfuzz-mutator.so afl-fuzz ...``` > Original repository: https://github.com/google/honggfuzz > Source commit: d0fbcb0373c32436b8fb922e6937da93b17291f5 diff --git a/custom_mutators/libfuzzer/FuzzerBuiltins.h b/custom_mutators/libfuzzer/FuzzerBuiltins.h new file mode 100644 index 00000000..4c0ada82 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltins.h @@ -0,0 +1,35 @@ +//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos around builtin functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_H +#define LLVM_FUZZER_BUILTINS_H + +#include "FuzzerPlatform.h" + +#if !LIBFUZZER_MSVC +#include + +#define GET_CALLER_PC() __builtin_return_address(0) + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); } +inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); } +inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); } + +inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); } +inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); } +inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); } + +} // namespace fuzzer + +#endif // !LIBFUZZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_H diff --git a/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h new file mode 100644 index 00000000..c5bec978 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerBuiltinsMsvc.h @@ -0,0 +1,72 @@ +//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Wrapper functions and marcos that use intrinsics instead of builtin functions +// which cannot be compiled by MSVC. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_BUILTINS_MSVC_H +#define LLVM_FUZZER_BUILTINS_MSVC_H + +#include "FuzzerPlatform.h" + +#if LIBFUZZER_MSVC +#include +#include +#include + +// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent +// from +#define GET_CALLER_PC() _ReturnAddress() + +namespace fuzzer { + +inline uint8_t Bswap(uint8_t x) { return x; } +// Use alternatives to __builtin functions from and on +// Windows since the builtins are not supported by MSVC. +inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); } +inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); } +inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); } + +// The functions below were mostly copied from +// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used +// outside of Windows. +inline uint32_t Clzll(uint64_t X) { + unsigned long LeadZeroIdx = 0; + +#if !defined(_M_ARM) && !defined(_M_X64) + // Scan the high 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X >> 32))) + return static_cast(63 - (LeadZeroIdx + 32)); // Create a bit offset from the MSB. + // Scan the low 32 bits. + if (_BitScanReverse(&LeadZeroIdx, static_cast(X))) + return static_cast(63 - LeadZeroIdx); + +#else + if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx; +#endif + return 64; +} + +inline uint32_t Clz(uint32_t X) { + unsigned long LeadZeroIdx = 0; + if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx; + return 32; +} + +inline int Popcountll(unsigned long long X) { +#if !defined(_M_ARM) && !defined(_M_X64) + return __popcnt(X) + __popcnt(X >> 32); +#else + return __popcnt64(X); +#endif +} + +} // namespace fuzzer + +#endif // LIBFUZER_MSVC +#endif // LLVM_FUZZER_BUILTINS_MSVC_H diff --git a/custom_mutators/libfuzzer/FuzzerCommand.h b/custom_mutators/libfuzzer/FuzzerCommand.h new file mode 100644 index 00000000..87308864 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCommand.h @@ -0,0 +1,178 @@ +//===- FuzzerCommand.h - Interface representing a process -------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerCommand represents a command to run in a subprocess. It allows callers +// to manage command line arguments and output and error streams. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_COMMAND_H +#define LLVM_FUZZER_COMMAND_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +class Command final { +public: + // This command line flag is used to indicate that the remaining command line + // is immutable, meaning this flag effectively marks the end of the mutable + // argument list. + static inline const char *ignoreRemainingArgs() { + return "-ignore_remaining_args=1"; + } + + Command() : CombinedOutAndErr(false) {} + + explicit Command(const Vector &ArgsToAdd) + : Args(ArgsToAdd), CombinedOutAndErr(false) {} + + explicit Command(const Command &Other) + : Args(Other.Args), CombinedOutAndErr(Other.CombinedOutAndErr), + OutputFile(Other.OutputFile) {} + + Command &operator=(const Command &Other) { + Args = Other.Args; + CombinedOutAndErr = Other.CombinedOutAndErr; + OutputFile = Other.OutputFile; + return *this; + } + + ~Command() {} + + // Returns true if the given Arg is present in Args. Only checks up to + // "-ignore_remaining_args=1". + bool hasArgument(const std::string &Arg) const { + auto i = endMutableArgs(); + return std::find(Args.begin(), i, Arg) != i; + } + + // Gets all of the current command line arguments, **including** those after + // "-ignore-remaining-args=1". + const Vector &getArguments() const { return Args; } + + // Adds the given argument before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArgument(const std::string &Arg) { + Args.insert(endMutableArgs(), Arg); + } + + // Adds all given arguments before "-ignore_remaining_args=1", or at the end + // if that flag isn't present. + void addArguments(const Vector &ArgsToAdd) { + Args.insert(endMutableArgs(), ArgsToAdd.begin(), ArgsToAdd.end()); + } + + // Removes the given argument from the command argument list. Ignores any + // occurrences after "-ignore_remaining_args=1", if present. + void removeArgument(const std::string &Arg) { + auto i = endMutableArgs(); + Args.erase(std::remove(Args.begin(), i, Arg), i); + } + + // Like hasArgument, but checks for "-[Flag]=...". + bool hasFlag(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + return std::any_of(Args.begin(), endMutableArgs(), IsMatch); + } + + // Returns the value of the first instance of a given flag, or an empty string + // if the flag isn't present. Ignores any occurrences after + // "-ignore_remaining_args=1", if present. + std::string getFlagValue(const std::string &Flag) const { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + auto j = std::find_if(Args.begin(), i, IsMatch); + std::string result; + if (j != i) { + result = j->substr(Arg.length()); + } + return result; + } + + // Like AddArgument, but adds "-[Flag]=[Value]". + void addFlag(const std::string &Flag, const std::string &Value) { + addArgument("-" + Flag + "=" + Value); + } + + // Like RemoveArgument, but removes "-[Flag]=...". + void removeFlag(const std::string &Flag) { + std::string Arg("-" + Flag + "="); + auto IsMatch = [&](const std::string &Other) { + return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0; + }; + auto i = endMutableArgs(); + Args.erase(std::remove_if(Args.begin(), i, IsMatch), i); + } + + // Returns whether the command's stdout is being written to an output file. + bool hasOutputFile() const { return !OutputFile.empty(); } + + // Returns the currently set output file. + const std::string &getOutputFile() const { return OutputFile; } + + // Configures the command to redirect its output to the name file. + void setOutputFile(const std::string &FileName) { OutputFile = FileName; } + + // Returns whether the command's stderr is redirected to stdout. + bool isOutAndErrCombined() const { return CombinedOutAndErr; } + + // Sets whether to redirect the command's stderr to its stdout. + void combineOutAndErr(bool combine = true) { CombinedOutAndErr = combine; } + + // Returns a string representation of the command. On many systems this will + // be the equivalent command line. + std::string toString() const { + std::stringstream SS; + for (auto arg : getArguments()) + SS << arg << " "; + if (hasOutputFile()) + SS << ">" << getOutputFile() << " "; + if (isOutAndErrCombined()) + SS << "2>&1 "; + std::string result = SS.str(); + if (!result.empty()) + result = result.substr(0, result.length() - 1); + return result; + } + +private: + Command(Command &&Other) = delete; + Command &operator=(Command &&Other) = delete; + + Vector::iterator endMutableArgs() { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + Vector::const_iterator endMutableArgs() const { + return std::find(Args.begin(), Args.end(), ignoreRemainingArgs()); + } + + // The command arguments. Args[0] is the command name. + Vector Args; + + // True indicates stderr is redirected to stdout. + bool CombinedOutAndErr; + + // If not empty, stdout is redirected to the named file. + std::string OutputFile; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_COMMAND_H diff --git a/custom_mutators/libfuzzer/FuzzerCorpus.h b/custom_mutators/libfuzzer/FuzzerCorpus.h new file mode 100644 index 00000000..daea4f52 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCorpus.h @@ -0,0 +1,581 @@ +//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::InputCorpus +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_CORPUS +#define LLVM_FUZZER_CORPUS + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct InputInfo { + Unit U; // The actual input data. + std::chrono::microseconds TimeOfUnit; + uint8_t Sha1[kSHA1NumBytes]; // Checksum. + // Number of features that this input has and no smaller input has. + size_t NumFeatures = 0; + size_t Tmp = 0; // Used by ValidateFeatureSet. + // Stats. + size_t NumExecutedMutations = 0; + size_t NumSuccessfullMutations = 0; + bool NeverReduce = false; + bool MayDeleteFile = false; + bool Reduced = false; + bool HasFocusFunction = false; + Vector UniqFeatureSet; + Vector DataFlowTraceForFocusFunction; + // Power schedule. + bool NeedsEnergyUpdate = false; + double Energy = 0.0; + size_t SumIncidence = 0; + Vector> FeatureFreqs; + + // Delete feature Idx and its frequency from FeatureFreqs. + bool DeleteFeatureFreq(uint32_t Idx) { + if (FeatureFreqs.empty()) + return false; + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + FeatureFreqs.erase(Lower); + return true; + } + return false; + } + + // Assign more energy to a high-entropy seed, i.e., that reveals more + // information about the globally rare features in the neighborhood of the + // seed. Since we do not know the entropy of a seed that has never been + // executed we assign fresh seeds maximum entropy and let II->Energy approach + // the true entropy from above. If ScalePerExecTime is true, the computed + // entropy is scaled based on how fast this input executes compared to the + // average execution time of inputs. The faster an input executes, the more + // energy gets assigned to the input. + void UpdateEnergy(size_t GlobalNumberOfFeatures, bool ScalePerExecTime, + std::chrono::microseconds AverageUnitExecutionTime) { + Energy = 0.0; + SumIncidence = 0; + + // Apply add-one smoothing to locally discovered features. + for (auto F : FeatureFreqs) { + size_t LocalIncidence = F.second + 1; + Energy -= LocalIncidence * logl(LocalIncidence); + SumIncidence += LocalIncidence; + } + + // Apply add-one smoothing to locally undiscovered features. + // PreciseEnergy -= 0; // since logl(1.0) == 0) + SumIncidence += (GlobalNumberOfFeatures - FeatureFreqs.size()); + + // Add a single locally abundant feature apply add-one smoothing. + size_t AbdIncidence = NumExecutedMutations + 1; + Energy -= AbdIncidence * logl(AbdIncidence); + SumIncidence += AbdIncidence; + + // Normalize. + if (SumIncidence != 0) + Energy = (Energy / SumIncidence) + logl(SumIncidence); + + if (ScalePerExecTime) { + // Scaling to favor inputs with lower execution time. + uint32_t PerfScore = 100; + if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 10) + PerfScore = 10; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 4) + PerfScore = 25; + else if (TimeOfUnit.count() > AverageUnitExecutionTime.count() * 2) + PerfScore = 50; + else if (TimeOfUnit.count() * 3 > AverageUnitExecutionTime.count() * 4) + PerfScore = 75; + else if (TimeOfUnit.count() * 4 < AverageUnitExecutionTime.count()) + PerfScore = 300; + else if (TimeOfUnit.count() * 3 < AverageUnitExecutionTime.count()) + PerfScore = 200; + else if (TimeOfUnit.count() * 2 < AverageUnitExecutionTime.count()) + PerfScore = 150; + + Energy *= PerfScore; + } + } + + // Increment the frequency of the feature Idx. + void UpdateFeatureFrequency(uint32_t Idx) { + NeedsEnergyUpdate = true; + + // The local feature frequencies is an ordered vector of pairs. + // If there are no local feature frequencies, push_back preserves order. + // Set the feature frequency for feature Idx32 to 1. + if (FeatureFreqs.empty()) { + FeatureFreqs.push_back(std::pair(Idx, 1)); + return; + } + + // Binary search over local feature frequencies sorted by index. + auto Lower = std::lower_bound(FeatureFreqs.begin(), FeatureFreqs.end(), + std::pair(Idx, 0)); + + // If feature Idx32 already exists, increment its frequency. + // Otherwise, insert a new pair right after the next lower index. + if (Lower != FeatureFreqs.end() && Lower->first == Idx) { + Lower->second++; + } else { + FeatureFreqs.insert(Lower, std::pair(Idx, 1)); + } + } +}; + +struct EntropicOptions { + bool Enabled; + size_t NumberOfRarestFeatures; + size_t FeatureFrequencyThreshold; + bool ScalePerExecTime; +}; + +class InputCorpus { + static const uint32_t kFeatureSetSize = 1 << 21; + static const uint8_t kMaxMutationFactor = 20; + static const size_t kSparseEnergyUpdates = 100; + + size_t NumExecutedMutations = 0; + + EntropicOptions Entropic; + +public: + InputCorpus(const std::string &OutputCorpus, EntropicOptions Entropic) + : Entropic(Entropic), OutputCorpus(OutputCorpus) { + memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature)); + memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature)); + } + ~InputCorpus() { + for (auto II : Inputs) + delete II; + } + size_t size() const { return Inputs.size(); } + size_t SizeInBytes() const { + size_t Res = 0; + for (auto II : Inputs) + Res += II->U.size(); + return Res; + } + size_t NumActiveUnits() const { + size_t Res = 0; + for (auto II : Inputs) + Res += !II->U.empty(); + return Res; + } + size_t MaxInputSize() const { + size_t Res = 0; + for (auto II : Inputs) + Res = std::max(Res, II->U.size()); + return Res; + } + void IncrementNumExecutedMutations() { NumExecutedMutations++; } + + size_t NumInputsThatTouchFocusFunction() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return II->HasFocusFunction; + }); + } + + size_t NumInputsWithDataFlowTrace() { + return std::count_if(Inputs.begin(), Inputs.end(), [](const InputInfo *II) { + return !II->DataFlowTraceForFocusFunction.empty(); + }); + } + + bool empty() const { return Inputs.empty(); } + const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; } + InputInfo *AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile, + bool HasFocusFunction, bool NeverReduce, + std::chrono::microseconds TimeOfUnit, + const Vector &FeatureSet, + const DataFlowTrace &DFT, const InputInfo *BaseII) { + assert(!U.empty()); + if (FeatureDebug) + Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures); + Inputs.push_back(new InputInfo()); + InputInfo &II = *Inputs.back(); + II.U = U; + II.NumFeatures = NumFeatures; + II.NeverReduce = NeverReduce; + II.TimeOfUnit = TimeOfUnit; + II.MayDeleteFile = MayDeleteFile; + II.UniqFeatureSet = FeatureSet; + II.HasFocusFunction = HasFocusFunction; + // Assign maximal energy to the new seed. + II.Energy = RareFeatures.empty() ? 1.0 : log(RareFeatures.size()); + II.SumIncidence = RareFeatures.size(); + II.NeedsEnergyUpdate = false; + std::sort(II.UniqFeatureSet.begin(), II.UniqFeatureSet.end()); + ComputeSHA1(U.data(), U.size(), II.Sha1); + auto Sha1Str = Sha1ToString(II.Sha1); + Hashes.insert(Sha1Str); + if (HasFocusFunction) + if (auto V = DFT.Get(Sha1Str)) + II.DataFlowTraceForFocusFunction = *V; + // This is a gross heuristic. + // Ideally, when we add an element to a corpus we need to know its DFT. + // But if we don't, we'll use the DFT of its base input. + if (II.DataFlowTraceForFocusFunction.empty() && BaseII) + II.DataFlowTraceForFocusFunction = BaseII->DataFlowTraceForFocusFunction; + DistributionNeedsUpdate = true; + PrintCorpus(); + // ValidateFeatureSet(); + return &II; + } + + // Debug-only + void PrintUnit(const Unit &U) { + if (!FeatureDebug) return; + for (uint8_t C : U) { + if (C != 'F' && C != 'U' && C != 'Z') + C = '.'; + Printf("%c", C); + } + } + + // Debug-only + void PrintFeatureSet(const Vector &FeatureSet) { + if (!FeatureDebug) return; + Printf("{"); + for (uint32_t Feature: FeatureSet) + Printf("%u,", Feature); + Printf("}"); + } + + // Debug-only + void PrintCorpus() { + if (!FeatureDebug) return; + Printf("======= CORPUS:\n"); + int i = 0; + for (auto II : Inputs) { + if (std::find(II->U.begin(), II->U.end(), 'F') != II->U.end()) { + Printf("[%2d] ", i); + Printf("%s sz=%zd ", Sha1ToString(II->Sha1).c_str(), II->U.size()); + PrintUnit(II->U); + Printf(" "); + PrintFeatureSet(II->UniqFeatureSet); + Printf("\n"); + } + i++; + } + } + + void Replace(InputInfo *II, const Unit &U) { + assert(II->U.size() > U.size()); + Hashes.erase(Sha1ToString(II->Sha1)); + DeleteFile(*II); + ComputeSHA1(U.data(), U.size(), II->Sha1); + Hashes.insert(Sha1ToString(II->Sha1)); + II->U = U; + II->Reduced = true; + DistributionNeedsUpdate = true; + } + + bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); } + bool HasUnit(const std::string &H) { return Hashes.count(H); } + InputInfo &ChooseUnitToMutate(Random &Rand) { + InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)]; + assert(!II.U.empty()); + return II; + } + + InputInfo &ChooseUnitToCrossOverWith(Random &Rand, bool UniformDist) { + if (!UniformDist) { + return ChooseUnitToMutate(Rand); + } + InputInfo &II = *Inputs[Rand(Inputs.size())]; + assert(!II.U.empty()); + return II; + } + + // Returns an index of random unit from the corpus to mutate. + size_t ChooseUnitIdxToMutate(Random &Rand) { + UpdateCorpusDistribution(Rand); + size_t Idx = static_cast(CorpusDistribution(Rand)); + assert(Idx < Inputs.size()); + return Idx; + } + + void PrintStats() { + for (size_t i = 0; i < Inputs.size(); i++) { + const auto &II = *Inputs[i]; + Printf(" [% 3zd %s] sz: % 5zd runs: % 5zd succ: % 5zd focus: %d\n", i, + Sha1ToString(II.Sha1).c_str(), II.U.size(), + II.NumExecutedMutations, II.NumSuccessfullMutations, II.HasFocusFunction); + } + } + + void PrintFeatureSet() { + for (size_t i = 0; i < kFeatureSetSize; i++) { + if(size_t Sz = GetFeature(i)) + Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz); + } + Printf("\n\t"); + for (size_t i = 0; i < Inputs.size(); i++) + if (size_t N = Inputs[i]->NumFeatures) + Printf(" %zd=>%zd ", i, N); + Printf("\n"); + } + + void DeleteFile(const InputInfo &II) { + if (!OutputCorpus.empty() && II.MayDeleteFile) + RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1))); + } + + void DeleteInput(size_t Idx) { + InputInfo &II = *Inputs[Idx]; + DeleteFile(II); + Unit().swap(II.U); + II.Energy = 0.0; + II.NeedsEnergyUpdate = false; + DistributionNeedsUpdate = true; + if (FeatureDebug) + Printf("EVICTED %zd\n", Idx); + } + + void AddRareFeature(uint32_t Idx) { + // Maintain *at least* TopXRarestFeatures many rare features + // and all features with a frequency below ConsideredRare. + // Remove all other features. + while (RareFeatures.size() > Entropic.NumberOfRarestFeatures && + FreqOfMostAbundantRareFeature > Entropic.FeatureFrequencyThreshold) { + + // Find most and second most abbundant feature. + uint32_t MostAbundantRareFeatureIndices[2] = {RareFeatures[0], + RareFeatures[0]}; + size_t Delete = 0; + for (size_t i = 0; i < RareFeatures.size(); i++) { + uint32_t Idx2 = RareFeatures[i]; + if (GlobalFeatureFreqs[Idx2] >= + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[0]]) { + MostAbundantRareFeatureIndices[1] = MostAbundantRareFeatureIndices[0]; + MostAbundantRareFeatureIndices[0] = Idx2; + Delete = i; + } + } + + // Remove most abundant rare feature. + RareFeatures[Delete] = RareFeatures.back(); + RareFeatures.pop_back(); + + for (auto II : Inputs) { + if (II->DeleteFeatureFreq(MostAbundantRareFeatureIndices[0])) + II->NeedsEnergyUpdate = true; + } + + // Set 2nd most abundant as the new most abundant feature count. + FreqOfMostAbundantRareFeature = + GlobalFeatureFreqs[MostAbundantRareFeatureIndices[1]]; + } + + // Add rare feature, handle collisions, and update energy. + RareFeatures.push_back(Idx); + GlobalFeatureFreqs[Idx] = 0; + for (auto II : Inputs) { + II->DeleteFeatureFreq(Idx); + + // Apply add-one smoothing to this locally undiscovered feature. + // Zero energy seeds will never be fuzzed and remain zero energy. + if (II->Energy > 0.0) { + II->SumIncidence += 1; + II->Energy += logl(II->SumIncidence) / II->SumIncidence; + } + } + + DistributionNeedsUpdate = true; + } + + bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) { + assert(NewSize); + Idx = Idx % kFeatureSetSize; + uint32_t OldSize = GetFeature(Idx); + if (OldSize == 0 || (Shrink && OldSize > NewSize)) { + if (OldSize > 0) { + size_t OldIdx = SmallestElementPerFeature[Idx]; + InputInfo &II = *Inputs[OldIdx]; + assert(II.NumFeatures > 0); + II.NumFeatures--; + if (II.NumFeatures == 0) + DeleteInput(OldIdx); + } else { + NumAddedFeatures++; + if (Entropic.Enabled) + AddRareFeature((uint32_t)Idx); + } + NumUpdatedFeatures++; + if (FeatureDebug) + Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize); + SmallestElementPerFeature[Idx] = Inputs.size(); + InputSizesPerFeature[Idx] = NewSize; + return true; + } + return false; + } + + // Increment frequency of feature Idx globally and locally. + void UpdateFeatureFrequency(InputInfo *II, size_t Idx) { + uint32_t Idx32 = Idx % kFeatureSetSize; + + // Saturated increment. + if (GlobalFeatureFreqs[Idx32] == 0xFFFF) + return; + uint16_t Freq = GlobalFeatureFreqs[Idx32]++; + + // Skip if abundant. + if (Freq > FreqOfMostAbundantRareFeature || + std::find(RareFeatures.begin(), RareFeatures.end(), Idx32) == + RareFeatures.end()) + return; + + // Update global frequencies. + if (Freq == FreqOfMostAbundantRareFeature) + FreqOfMostAbundantRareFeature++; + + // Update local frequencies. + if (II) + II->UpdateFeatureFrequency(Idx32); + } + + size_t NumFeatures() const { return NumAddedFeatures; } + size_t NumFeatureUpdates() const { return NumUpdatedFeatures; } + +private: + + static const bool FeatureDebug = false; + + size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; } + + void ValidateFeatureSet() { + if (FeatureDebug) + PrintFeatureSet(); + for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++) + if (GetFeature(Idx)) + Inputs[SmallestElementPerFeature[Idx]]->Tmp++; + for (auto II: Inputs) { + if (II->Tmp != II->NumFeatures) + Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures); + assert(II->Tmp == II->NumFeatures); + II->Tmp = 0; + } + } + + // Updates the probability distribution for the units in the corpus. + // Must be called whenever the corpus or unit weights are changed. + // + // Hypothesis: inputs that maximize information about globally rare features + // are interesting. + void UpdateCorpusDistribution(Random &Rand) { + // Skip update if no seeds or rare features were added/deleted. + // Sparse updates for local change of feature frequencies, + // i.e., randomly do not skip. + if (!DistributionNeedsUpdate && + (!Entropic.Enabled || Rand(kSparseEnergyUpdates))) + return; + + DistributionNeedsUpdate = false; + + size_t N = Inputs.size(); + assert(N); + Intervals.resize(N + 1); + Weights.resize(N); + std::iota(Intervals.begin(), Intervals.end(), 0); + + std::chrono::microseconds AverageUnitExecutionTime(0); + for (auto II : Inputs) { + AverageUnitExecutionTime += II->TimeOfUnit; + } + AverageUnitExecutionTime /= N; + + bool VanillaSchedule = true; + if (Entropic.Enabled) { + for (auto II : Inputs) { + if (II->NeedsEnergyUpdate && II->Energy != 0.0) { + II->NeedsEnergyUpdate = false; + II->UpdateEnergy(RareFeatures.size(), Entropic.ScalePerExecTime, + AverageUnitExecutionTime); + } + } + + for (size_t i = 0; i < N; i++) { + + if (Inputs[i]->NumFeatures == 0) { + // If the seed doesn't represent any features, assign zero energy. + Weights[i] = 0.; + } else if (Inputs[i]->NumExecutedMutations / kMaxMutationFactor > + NumExecutedMutations / Inputs.size()) { + // If the seed was fuzzed a lot more than average, assign zero energy. + Weights[i] = 0.; + } else { + // Otherwise, simply assign the computed energy. + Weights[i] = Inputs[i]->Energy; + } + + // If energy for all seeds is zero, fall back to vanilla schedule. + if (Weights[i] > 0.0) + VanillaSchedule = false; + } + } + + if (VanillaSchedule) { + for (size_t i = 0; i < N; i++) + Weights[i] = Inputs[i]->NumFeatures + ? (i + 1) * (Inputs[i]->HasFocusFunction ? 1000 : 1) + : 0.; + } + + if (FeatureDebug) { + for (size_t i = 0; i < N; i++) + Printf("%zd ", Inputs[i]->NumFeatures); + Printf("SCORE\n"); + for (size_t i = 0; i < N; i++) + Printf("%f ", Weights[i]); + Printf("Weights\n"); + } + CorpusDistribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + } + std::piecewise_constant_distribution CorpusDistribution; + + Vector Intervals; + Vector Weights; + + std::unordered_set Hashes; + Vector Inputs; + + size_t NumAddedFeatures = 0; + size_t NumUpdatedFeatures = 0; + uint32_t InputSizesPerFeature[kFeatureSetSize]; + uint32_t SmallestElementPerFeature[kFeatureSetSize]; + + bool DistributionNeedsUpdate = true; + uint16_t FreqOfMostAbundantRareFeature = 0; + uint16_t GlobalFeatureFreqs[kFeatureSetSize] = {}; + Vector RareFeatures; + + std::string OutputCorpus; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_CORPUS diff --git a/custom_mutators/libfuzzer/FuzzerCrossOver.cpp b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp new file mode 100644 index 00000000..3b3fd94a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerCrossOver.cpp @@ -0,0 +1,60 @@ +//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Cross over test inputs. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerMutate.h" +#include "FuzzerRandom.h" +#include + +namespace fuzzer { + +// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out. +size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize) { + + assert(Size1 || Size2); + MaxOutSize = Rand(MaxOutSize) + 1; + size_t OutPos = 0; + size_t Pos1 = 0; + size_t Pos2 = 0; + size_t * InPos = &Pos1; + size_t InSize = Size1; + const uint8_t *Data = Data1; + bool CurrentlyUsingFirstData = true; + while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) { + + // Merge a part of Data into Out. + size_t OutSizeLeft = MaxOutSize - OutPos; + if (*InPos < InSize) { + + size_t InSizeLeft = InSize - *InPos; + size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft); + size_t ExtraSize = Rand(MaxExtraSize) + 1; + memcpy(Out + OutPos, Data + *InPos, ExtraSize); + OutPos += ExtraSize; + (*InPos) += ExtraSize; + + } + + // Use the other input data on the next iteration. + InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1; + InSize = CurrentlyUsingFirstData ? Size2 : Size1; + Data = CurrentlyUsingFirstData ? Data2 : Data1; + CurrentlyUsingFirstData = !CurrentlyUsingFirstData; + + } + + return OutPos; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp new file mode 100644 index 00000000..797a52a7 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.cpp @@ -0,0 +1,344 @@ +//===- FuzzerDataFlowTrace.cpp - DataFlowTrace ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace +//===----------------------------------------------------------------------===// + +#include "FuzzerDataFlowTrace.h" + +#include "FuzzerCommand.h" +#include "FuzzerIO.h" +#include "FuzzerRandom.h" +#include "FuzzerSHA1.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static const char *kFunctionsTxt = "functions.txt"; + +bool BlockCoverage::AppendCoverage(const std::string &S) { + + std::stringstream SS(S); + return AppendCoverage(SS); + +} + +// Coverage lines have this form: +// CN X Y Z T +// where N is the number of the function, T is the total number of instrumented +// BBs, and X,Y,Z, if present, are the indecies of covered BB. +// BB #0, which is the entry block, is not explicitly listed. +bool BlockCoverage::AppendCoverage(std::istream &IN) { + + std::string L; + while (std::getline(IN, L, '\n')) { + + if (L.empty()) continue; + std::stringstream SS(L.c_str() + 1); + size_t FunctionId = 0; + SS >> FunctionId; + if (L[0] == 'F') { + + FunctionsWithDFT.insert(FunctionId); + continue; + + } + + if (L[0] != 'C') continue; + Vector CoveredBlocks; + while (true) { + + uint32_t BB = 0; + SS >> BB; + if (!SS) break; + CoveredBlocks.push_back(BB); + + } + + if (CoveredBlocks.empty()) return false; + uint32_t NumBlocks = CoveredBlocks.back(); + CoveredBlocks.pop_back(); + for (auto BB : CoveredBlocks) + if (BB >= NumBlocks) return false; + auto It = Functions.find(FunctionId); + auto &Counters = + It == Functions.end() + ? Functions.insert({FunctionId, Vector(NumBlocks)}) + .first->second + : It->second; + + if (Counters.size() != NumBlocks) return false; // wrong number of blocks. + + Counters[0]++; + for (auto BB : CoveredBlocks) + Counters[BB]++; + + } + + return true; + +} + +// Assign weights to each function. +// General principles: +// * any uncovered function gets weight 0. +// * a function with lots of uncovered blocks gets bigger weight. +// * a function with a less frequently executed code gets bigger weight. +Vector BlockCoverage::FunctionWeights(size_t NumFunctions) const { + + Vector Res(NumFunctions); + for (auto It : Functions) { + + auto FunctionID = It.first; + auto Counters = It.second; + assert(FunctionID < NumFunctions); + auto &Weight = Res[FunctionID]; + // Give higher weight if the function has a DFT. + Weight = FunctionsWithDFT.count(FunctionID) ? 1000. : 1; + // Give higher weight to functions with less frequently seen basic blocks. + Weight /= SmallestNonZeroCounter(Counters); + // Give higher weight to functions with the most uncovered basic blocks. + Weight *= NumberOfUncoveredBlocks(Counters) + 1; + + } + + return Res; + +} + +void DataFlowTrace::ReadCoverage(const std::string &DirPath) { + + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; + std::ifstream IF(SF.File); + Coverage.AppendCoverage(IF); + + } + +} + +static void DFTStringAppendToVector(Vector * DFT, + const std::string &DFTString) { + + assert(DFT->size() == DFTString.size()); + for (size_t I = 0, Len = DFT->size(); I < Len; I++) + (*DFT)[I] = DFTString[I] == '1'; + +} + +// converts a string of '0' and '1' into a Vector +static Vector DFTStringToVector(const std::string &DFTString) { + + Vector DFT(DFTString.size()); + DFTStringAppendToVector(&DFT, DFTString); + return DFT; + +} + +static bool ParseError(const char *Err, const std::string &Line) { + + Printf("DataFlowTrace: parse error: %s: Line: %s\n", Err, Line.c_str()); + return false; + +} + +// TODO(metzman): replace std::string with std::string_view for +// better performance. Need to figure our how to use string_view on Windows. +static bool ParseDFTLine(const std::string &Line, size_t *FunctionNum, + std::string *DFTString) { + + if (!Line.empty() && Line[0] != 'F') return false; // Ignore coverage. + size_t SpacePos = Line.find(' '); + if (SpacePos == std::string::npos) + return ParseError("no space in the trace line", Line); + if (Line.empty() || Line[0] != 'F') + return ParseError("the trace line doesn't start with 'F'", Line); + *FunctionNum = std::atol(Line.c_str() + 1); + const char *Beg = Line.c_str() + SpacePos + 1; + const char *End = Line.c_str() + Line.size(); + assert(Beg < End); + size_t Len = End - Beg; + for (size_t I = 0; I < Len; I++) { + + if (Beg[I] != '0' && Beg[I] != '1') + return ParseError("the trace should contain only 0 or 1", Line); + + } + + *DFTString = Beg; + return true; + +} + +bool DataFlowTrace::Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand) { + + if (DirPath.empty()) return false; + Printf("INFO: DataFlowTrace: reading from '%s'\n", DirPath.c_str()); + Vector Files; + GetSizedFilesFromDir(DirPath, &Files); + std::string L; + size_t FocusFuncIdx = SIZE_MAX; + Vector FunctionNames; + + // Collect the hashes of the corpus files. + for (auto &SF : CorporaFiles) + CorporaHashes.insert(Hash(FileToVector(SF.File))); + + // Read functions.txt + std::ifstream IF(DirPlusFile(DirPath, kFunctionsTxt)); + size_t NumFunctions = 0; + while (std::getline(IF, L, '\n')) { + + FunctionNames.push_back(L); + NumFunctions++; + if (*FocusFunction == L) FocusFuncIdx = NumFunctions - 1; + + } + + if (!NumFunctions) return false; + + if (*FocusFunction == "auto") { + + // AUTOFOCUS works like this: + // * reads the coverage data from the DFT files. + // * assigns weights to functions based on coverage. + // * chooses a random function according to the weights. + ReadCoverage(DirPath); + auto Weights = Coverage.FunctionWeights(NumFunctions); + Vector Intervals(NumFunctions + 1); + std::iota(Intervals.begin(), Intervals.end(), 0); + auto Distribution = std::piecewise_constant_distribution( + Intervals.begin(), Intervals.end(), Weights.begin()); + FocusFuncIdx = static_cast(Distribution(Rand)); + *FocusFunction = FunctionNames[FocusFuncIdx]; + assert(FocusFuncIdx < NumFunctions); + Printf("INFO: AUTOFOCUS: %zd %s\n", FocusFuncIdx, + FunctionNames[FocusFuncIdx].c_str()); + for (size_t i = 0; i < NumFunctions; i++) { + + if (!Weights[i]) continue; + Printf(" [%zd] W %g\tBB-tot %u\tBB-cov %u\tEntryFreq %u:\t%s\n", i, + Weights[i], Coverage.GetNumberOfBlocks(i), + Coverage.GetNumberOfCoveredBlocks(i), Coverage.GetCounter(i, 0), + FunctionNames[i].c_str()); + + } + + } + + if (!NumFunctions || FocusFuncIdx == SIZE_MAX || Files.size() <= 1) + return false; + + // Read traces. + size_t NumTraceFiles = 0; + size_t NumTracesWithFocusFunction = 0; + for (auto &SF : Files) { + + auto Name = Basename(SF.File); + if (Name == kFunctionsTxt) continue; + if (!CorporaHashes.count(Name)) continue; // not in the corpus. + NumTraceFiles++; + // Printf("=== %s\n", Name.c_str()); + std::ifstream IF(SF.File); + while (std::getline(IF, L, '\n')) { + + size_t FunctionNum = 0; + std::string DFTString; + if (ParseDFTLine(L, &FunctionNum, &DFTString) && + FunctionNum == FocusFuncIdx) { + + NumTracesWithFocusFunction++; + + if (FunctionNum >= NumFunctions) + return ParseError("N is greater than the number of functions", L); + Traces[Name] = DFTStringToVector(DFTString); + // Print just a few small traces. + if (NumTracesWithFocusFunction <= 3 && DFTString.size() <= 16) + Printf("%s => |%s|\n", Name.c_str(), std::string(DFTString).c_str()); + break; // No need to parse the following lines. + + } + + } + + } + + Printf( + "INFO: DataFlowTrace: %zd trace files, %zd functions, " + "%zd traces with focus function\n", + NumTraceFiles, NumFunctions, NumTracesWithFocusFunction); + return NumTraceFiles > 0; + +} + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles) { + + Printf("INFO: collecting data flow: bin: %s dir: %s files: %zd\n", + DFTBinary.c_str(), DirPath.c_str(), CorporaFiles.size()); + if (CorporaFiles.empty()) { + + Printf("ERROR: can't collect data flow without corpus provided."); + return 1; + + } + + static char DFSanEnv[] = "DFSAN_OPTIONS=warn_unimplemented=0"; + putenv(DFSanEnv); + MkDir(DirPath); + for (auto &F : CorporaFiles) { + + // For every input F we need to collect the data flow and the coverage. + // Data flow collection may fail if we request too many DFSan tags at once. + // So, we start from requesting all tags in range [0,Size) and if that fails + // we then request tags in [0,Size/2) and [Size/2, Size), and so on. + // Function number => DFT. + auto OutPath = DirPlusFile(DirPath, Hash(FileToVector(F.File))); + std::unordered_map> DFTMap; + std::unordered_set Cov; + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.addArgument(F.File); + Cmd.addArgument(OutPath); + Printf("CMD: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + + // Write functions.txt if it's currently empty or doesn't exist. + auto FunctionsTxtPath = DirPlusFile(DirPath, kFunctionsTxt); + if (FileToString(FunctionsTxtPath).empty()) { + + Command Cmd; + Cmd.addArgument(DFTBinary); + Cmd.setOutputFile(FunctionsTxtPath); + ExecuteCommand(Cmd); + + } + + return 0; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h new file mode 100644 index 00000000..d6e3de30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDataFlowTrace.h @@ -0,0 +1,135 @@ +//===- FuzzerDataFlowTrace.h - Internal header for the Fuzzer ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::DataFlowTrace; reads and handles a data-flow trace. +// +// A data flow trace is generated by e.g. dataflow/DataFlow.cpp +// and is stored on disk in a separate directory. +// +// The trace dir contains a file 'functions.txt' which lists function names, +// oner per line, e.g. +// ==> functions.txt <== +// Func2 +// LLVMFuzzerTestOneInput +// Func1 +// +// All other files in the dir are the traces, see dataflow/DataFlow.cpp. +// The name of the file is sha1 of the input used to generate the trace. +// +// Current status: +// the data is parsed and the summary is printed, but the data is not yet +// used in any other way. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DATA_FLOW_TRACE +#define LLVM_FUZZER_DATA_FLOW_TRACE + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" + +#include +#include +#include +#include + +namespace fuzzer { + +int CollectDataFlow(const std::string &DFTBinary, const std::string &DirPath, + const Vector &CorporaFiles); + +class BlockCoverage { + public: + bool AppendCoverage(std::istream &IN); + bool AppendCoverage(const std::string &S); + + size_t NumCoveredFunctions() const { return Functions.size(); } + + uint32_t GetCounter(size_t FunctionId, size_t BasicBlockId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + if (BasicBlockId < Counters.size()) + return Counters[BasicBlockId]; + return 0; + } + + uint32_t GetNumberOfBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + return Counters.size(); + } + + uint32_t GetNumberOfCoveredBlocks(size_t FunctionId) { + auto It = Functions.find(FunctionId); + if (It == Functions.end()) return 0; + const auto &Counters = It->second; + uint32_t Result = 0; + for (auto Cnt: Counters) + if (Cnt) + Result++; + return Result; + } + + Vector FunctionWeights(size_t NumFunctions) const; + void clear() { Functions.clear(); } + + private: + + typedef Vector CoverageVector; + + uint32_t NumberOfCoveredBlocks(const CoverageVector &Counters) const { + uint32_t Res = 0; + for (auto Cnt : Counters) + if (Cnt) + Res++; + return Res; + } + + uint32_t NumberOfUncoveredBlocks(const CoverageVector &Counters) const { + return Counters.size() - NumberOfCoveredBlocks(Counters); + } + + uint32_t SmallestNonZeroCounter(const CoverageVector &Counters) const { + assert(!Counters.empty()); + uint32_t Res = Counters[0]; + for (auto Cnt : Counters) + if (Cnt) + Res = Min(Res, Cnt); + assert(Res); + return Res; + } + + // Function ID => vector of counters. + // Each counter represents how many input files trigger the given basic block. + std::unordered_map Functions; + // Functions that have DFT entry. + std::unordered_set FunctionsWithDFT; +}; + +class DataFlowTrace { + public: + void ReadCoverage(const std::string &DirPath); + bool Init(const std::string &DirPath, std::string *FocusFunction, + Vector &CorporaFiles, Random &Rand); + void Clear() { Traces.clear(); } + const Vector *Get(const std::string &InputSha1) const { + auto It = Traces.find(InputSha1); + if (It != Traces.end()) + return &It->second; + return nullptr; + } + + private: + // Input's sha1 => DFT for the FocusFunction. + std::unordered_map > Traces; + BlockCoverage Coverage; + std::unordered_set CorporaHashes; +}; +} // namespace fuzzer + +#endif // LLVM_FUZZER_DATA_FLOW_TRACE diff --git a/custom_mutators/libfuzzer/FuzzerDefs.h b/custom_mutators/libfuzzer/FuzzerDefs.h new file mode 100644 index 00000000..1a2752af --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDefs.h @@ -0,0 +1,75 @@ +//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Basic definitions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DEFS_H +#define LLVM_FUZZER_DEFS_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace fuzzer { + +template T Min(T a, T b) { return a < b ? a : b; } +template T Max(T a, T b) { return a > b ? a : b; } + +class Random; +class Dictionary; +class DictionaryEntry; +class MutationDispatcher; +struct FuzzingOptions; +class InputCorpus; +struct InputInfo; +struct ExternalFunctions; + +// Global interface to functions that may or may not be available. +extern ExternalFunctions *EF; + +// We are using a custom allocator to give a different symbol name to STL +// containers in order to avoid ODR violations. +template + class fuzzer_allocator: public std::allocator { + public: + fuzzer_allocator() = default; + + template + fuzzer_allocator(const fuzzer_allocator&) {} + + template + struct rebind { typedef fuzzer_allocator other; }; + }; + +template +using Vector = std::vector>; + +template +using Set = std::set, fuzzer_allocator>; + +typedef Vector Unit; +typedef Vector UnitVector; +typedef int (*UserCallback)(const uint8_t *Data, size_t Size); + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback); + +uint8_t *ExtraCountersBegin(); +uint8_t *ExtraCountersEnd(); +void ClearExtraCounters(); + +extern bool RunningUserCallback; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DEFS_H diff --git a/custom_mutators/libfuzzer/FuzzerDictionary.h b/custom_mutators/libfuzzer/FuzzerDictionary.h new file mode 100644 index 00000000..301c5d9a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDictionary.h @@ -0,0 +1,118 @@ +//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Dictionary +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_DICTIONARY_H +#define LLVM_FUZZER_DICTIONARY_H + +#include "FuzzerDefs.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include + +namespace fuzzer { +// A simple POD sized array of bytes. +template class FixedWord { +public: + static const size_t kMaxSize = kMaxSizeT; + FixedWord() {} + FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); } + + void Set(const uint8_t *B, uint8_t S) { + assert(S <= kMaxSize); + memcpy(Data, B, S); + Size = S; + } + + bool operator==(const FixedWord &w) const { + return Size == w.Size && 0 == memcmp(Data, w.Data, Size); + } + + static size_t GetMaxSize() { return kMaxSize; } + const uint8_t *data() const { return Data; } + uint8_t size() const { return Size; } + +private: + uint8_t Size = 0; + uint8_t Data[kMaxSize]; +}; + +typedef FixedWord<64> Word; + +class DictionaryEntry { + public: + DictionaryEntry() {} + DictionaryEntry(Word W) : W(W) {} + DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {} + const Word &GetW() const { return W; } + + bool HasPositionHint() const { return PositionHint != std::numeric_limits::max(); } + size_t GetPositionHint() const { + assert(HasPositionHint()); + return PositionHint; + } + void IncUseCount() { UseCount++; } + void IncSuccessCount() { SuccessCount++; } + size_t GetUseCount() const { return UseCount; } + size_t GetSuccessCount() const {return SuccessCount; } + + void Print(const char *PrintAfter = "\n") { + PrintASCII(W.data(), W.size()); + if (HasPositionHint()) + Printf("@%zd", GetPositionHint()); + Printf("%s", PrintAfter); + } + +private: + Word W; + size_t PositionHint = std::numeric_limits::max(); + size_t UseCount = 0; + size_t SuccessCount = 0; +}; + +class Dictionary { + public: + static const size_t kMaxDictSize = 1 << 14; + + bool ContainsWord(const Word &W) const { + return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) { + return DE.GetW() == W; + }); + } + const DictionaryEntry *begin() const { return &DE[0]; } + const DictionaryEntry *end() const { return begin() + Size; } + DictionaryEntry & operator[] (size_t Idx) { + assert(Idx < Size); + return DE[Idx]; + } + void push_back(DictionaryEntry DE) { + if (Size < kMaxDictSize) + this->DE[Size++] = DE; + } + void clear() { Size = 0; } + bool empty() const { return Size == 0; } + size_t size() const { return Size; } + +private: + DictionaryEntry DE[kMaxDictSize]; + size_t Size = 0; +}; + +// Parses one dictionary entry. +// If successful, write the enty to Unit and returns true, +// otherwise returns false. +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U); +// Parses the dictionary file, fills Units, returns true iff all lines +// were parsed successfully. +bool ParseDictionaryFile(const std::string &Text, Vector *Units); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_DICTIONARY_H diff --git a/custom_mutators/libfuzzer/FuzzerDriver.cpp b/custom_mutators/libfuzzer/FuzzerDriver.cpp new file mode 100644 index 00000000..9a0a32b0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerDriver.cpp @@ -0,0 +1,1122 @@ +//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// FuzzerDriver and flag parsing. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerCorpus.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInterface.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This function should be present in the libFuzzer so that the client +// binary can test for its existence. +#if LIBFUZZER_MSVC +extern "C" void __libfuzzer_is_present() { + +} + + #if defined(_M_IX86) || defined(__i386__) + #pragma comment(linker, "/include:___libfuzzer_is_present") + #else + #pragma comment(linker, "/include:__libfuzzer_is_present") + #endif +#else +extern "C" __attribute__((used)) void __libfuzzer_is_present() { + +} + +#endif // LIBFUZZER_MSVC + +namespace fuzzer { + +// Program arguments. +struct FlagDescription { + + const char * Name; + const char * Description; + int Default; + int * IntFlag; + const char ** StrFlag; + unsigned int *UIntFlag; + +}; + +struct { +\ +#define FUZZER_DEPRECATED_FLAG(Name) +#define FUZZER_FLAG_INT(Name, Default, Description) int Name; +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; +#define FUZZER_FLAG_STRING(Name, Description) const char *Name; +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +} Flags; + +static const FlagDescription FlagDescriptions[]{ +\ +#define FUZZER_DEPRECATED_FLAG(Name) \ + {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, +#define FUZZER_FLAG_INT(Name, Default, Description) \ + {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, +#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ + {#Name, Description, static_cast(Default), \ + nullptr, nullptr, &Flags.Name}, +#define FUZZER_FLAG_STRING(Name, Description) \ + {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, +#include "FuzzerFlags.def" +#undef FUZZER_DEPRECATED_FLAG +#undef FUZZER_FLAG_INT +#undef FUZZER_FLAG_UNSIGNED +#undef FUZZER_FLAG_STRING + +}; + +static const size_t kNumFlags = + sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); + +static Vector *Inputs; +static std::string * ProgName; + +static void PrintHelp() { + + Printf("Usage:\n"); + auto Prog = ProgName->c_str(); + Printf("\nTo run fuzzing pass 0 or more directories.\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); + + Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); + Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); + + Printf("\nFlags: (strictly in form -flag=value)\n"); + size_t MaxFlagLen = 0; + for (size_t F = 0; F < kNumFlags; F++) + MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); + + for (size_t F = 0; F < kNumFlags; F++) { + + const auto &D = FlagDescriptions[F]; + if (strstr(D.Description, "internal flag") == D.Description) continue; + Printf(" %s", D.Name); + for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) + Printf(" "); + Printf("\t"); + Printf("%d\t%s\n", D.Default, D.Description); + + } + + Printf( + "\nFlags starting with '--' will be ignored and " + "will be passed verbatim to subprocesses.\n"); + +} + +static const char *FlagValue(const char *Param, const char *Name) { + + size_t Len = strlen(Name); + if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && + Param[Len + 1] == '=') + return &Param[Len + 2]; + return nullptr; + +} + +// Avoid calling stol as it triggers a bug in clang/glibc build. +static long MyStol(const char *Str) { + + long Res = 0; + long Sign = 1; + if (*Str == '-') { + + Str++; + Sign = -1; + + } + + for (size_t i = 0; Str[i]; i++) { + + char Ch = Str[i]; + if (Ch < '0' || Ch > '9') return Res; + Res = Res * 10 + (Ch - '0'); + + } + + return Res * Sign; + +} + +static bool ParseOneFlag(const char *Param) { + + if (Param[0] != '-') return false; + if (Param[1] == '-') { + + static bool PrintedWarning = false; + if (!PrintedWarning) { + + PrintedWarning = true; + Printf("INFO: libFuzzer ignores flags that start with '--'\n"); + + } + + for (size_t F = 0; F < kNumFlags; F++) + if (FlagValue(Param + 1, FlagDescriptions[F].Name)) + Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); + return true; + + } + + for (size_t F = 0; F < kNumFlags; F++) { + + const char *Name = FlagDescriptions[F].Name; + const char *Str = FlagValue(Param, Name); + if (Str) { + + if (FlagDescriptions[F].IntFlag) { + + int Val = MyStol(Str); + *FlagDescriptions[F].IntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %d\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].UIntFlag) { + + unsigned int Val = std::stoul(Str); + *FlagDescriptions[F].UIntFlag = Val; + if (Flags.verbosity >= 2) Printf("Flag: %s %u\n", Name, Val); + return true; + + } else if (FlagDescriptions[F].StrFlag) { + + *FlagDescriptions[F].StrFlag = Str; + if (Flags.verbosity >= 2) Printf("Flag: %s %s\n", Name, Str); + return true; + + } else { // Deprecated flag. + + Printf("Flag: %s: deprecated, don't use\n", Name); + return true; + + } + + } + + } + + Printf( + "\n\nWARNING: unrecognized flag '%s'; " + "use -help=1 to list all flags\n\n", + Param); + return true; + +} + +// We don't use any library to minimize dependencies. +static void ParseFlags(const Vector &Args, + const ExternalFunctions * EF) { + + for (size_t F = 0; F < kNumFlags; F++) { + + if (FlagDescriptions[F].IntFlag) + *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; + if (FlagDescriptions[F].UIntFlag) + *FlagDescriptions[F].UIntFlag = + static_cast(FlagDescriptions[F].Default); + if (FlagDescriptions[F].StrFlag) *FlagDescriptions[F].StrFlag = nullptr; + + } + + // Disable len_control by default, if LLVMFuzzerCustomMutator is used. + if (EF->LLVMFuzzerCustomMutator) { + + Flags.len_control = 0; + Printf( + "INFO: found LLVMFuzzerCustomMutator (%p). " + "Disabling -len_control by default.\n", + EF->LLVMFuzzerCustomMutator); + + } + + Inputs = new Vector; + for (size_t A = 1; A < Args.size(); A++) { + + if (ParseOneFlag(Args[A].c_str())) { + + if (Flags.ignore_remaining_args) break; + continue; + + } + + Inputs->push_back(Args[A]); + + } + +} + +static std::mutex Mu; + +static void PulseThread() { + + while (true) { + + SleepSeconds(600); + std::lock_guard Lock(Mu); + Printf("pulse...\n"); + + } + +} + +static void WorkerThread(const Command &BaseCmd, std::atomic *Counter, + unsigned NumJobs, std::atomic *HasErrors) { + + while (true) { + + unsigned C = (*Counter)++; + if (C >= NumJobs) break; + std::string Log = "fuzz-" + std::to_string(C) + ".log"; + Command Cmd(BaseCmd); + Cmd.setOutputFile(Log); + Cmd.combineOutAndErr(); + if (Flags.verbosity) { + + std::string CommandLine = Cmd.toString(); + Printf("%s\n", CommandLine.c_str()); + + } + + int ExitCode = ExecuteCommand(Cmd); + if (ExitCode != 0) *HasErrors = true; + std::lock_guard Lock(Mu); + Printf("================== Job %u exited with exit code %d ============\n", + C, ExitCode); + fuzzer::CopyFileToErr(Log); + + } + +} + +static void ValidateDirectoryExists(const std::string &Path, + bool CreateDirectory) { + + if (Path.empty()) { + + Printf("ERROR: Provided directory path is an empty string\n"); + exit(1); + + } + + if (IsDirectory(Path)) return; + + if (CreateDirectory) { + + if (!MkDirRecursive(Path)) { + + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + + } + + return; + + } + + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); + +} + +std::string CloneArgsWithoutX(const Vector &Args, const char *X1, + const char *X2) { + + std::string Cmd; + for (auto &S : Args) { + + if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) continue; + Cmd += S + " "; + + } + + return Cmd; + +} + +static int RunInMultipleProcesses(const Vector &Args, + unsigned NumWorkers, unsigned NumJobs) { + + std::atomic Counter(0); + std::atomic HasErrors(false); + Command Cmd(Args); + Cmd.removeFlag("jobs"); + Cmd.removeFlag("workers"); + Vector V; + std::thread Pulse(PulseThread); + Pulse.detach(); + for (unsigned i = 0; i < NumWorkers; i++) + V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, + &HasErrors)); + for (auto &T : V) + T.join(); + return HasErrors ? 1 : 0; + +} + +static void RssThread(Fuzzer *F, size_t RssLimitMb) { + + while (true) { + + SleepSeconds(1); + size_t Peak = GetPeakRSSMb(); + if (Peak > RssLimitMb) F->RssLimitCallback(); + + } + +} + +static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { + + if (!RssLimitMb) return; + std::thread T(RssThread, F, RssLimitMb); + T.detach(); + +} + +int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { + + Unit U = FileToVector(InputFilePath); + if (MaxLen && MaxLen < U.size()) U.resize(MaxLen); + F->ExecuteCallback(U.data(), U.size()); + F->TryDetectingAMemoryLeak(U.data(), U.size(), true); + return 0; + +} + +static bool AllInputsAreFiles() { + + if (Inputs->empty()) return false; + for (auto &Path : *Inputs) + if (!IsFile(Path)) return false; + return true; + +} + +static std::string GetDedupTokenFromCmdOutput(const std::string &S) { + + auto Beg = S.find("DEDUP_TOKEN:"); + if (Beg == std::string::npos) return ""; + auto End = S.find('\n', Beg); + if (End == std::string::npos) return ""; + return S.substr(Beg, End - Beg); + +} + +int CleanseCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1 || !Flags.exact_artifact_path) { + + Printf( + "ERROR: -cleanse_crash should be given one input file and" + " -exact_artifact_path\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + std::string OutputFilePath = Flags.exact_artifact_path; + Command Cmd(Args); + Cmd.removeFlag("cleanse_crash"); + + assert(Cmd.hasArgument(InputFilePath)); + Cmd.removeArgument(InputFilePath); + + auto TmpFilePath = TempPath("CleanseCrashInput", ".repro"); + Cmd.addArgument(TmpFilePath); + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + auto U = FileToVector(CurrentFilePath); + size_t Size = U.size(); + + const Vector ReplacementBytes = {' ', 0xff}; + for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { + + bool Changed = false; + for (size_t Idx = 0; Idx < Size; Idx++) { + + Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts, + Idx, Size); + uint8_t OriginalByte = U[Idx]; + if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(), + ReplacementBytes.end(), + OriginalByte)) + continue; + for (auto NewByte : ReplacementBytes) { + + U[Idx] = NewByte; + WriteToFile(U, TmpFilePath); + auto ExitCode = ExecuteCommand(Cmd); + RemoveFile(TmpFilePath); + if (!ExitCode) { + + U[Idx] = OriginalByte; + + } else { + + Changed = true; + Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte); + WriteToFile(U, OutputFilePath); + break; + + } + + } + + } + + if (!Changed) break; + + } + + return 0; + +} + +int MinimizeCrashInput(const Vector &Args, + const FuzzingOptions & Options) { + + if (Inputs->size() != 1) { + + Printf("ERROR: -minimize_crash should be given one input file\n"); + exit(1); + + } + + std::string InputFilePath = Inputs->at(0); + Command BaseCmd(Args); + BaseCmd.removeFlag("minimize_crash"); + BaseCmd.removeFlag("exact_artifact_path"); + assert(BaseCmd.hasArgument(InputFilePath)); + BaseCmd.removeArgument(InputFilePath); + if (Flags.runs <= 0 && Flags.max_total_time == 0) { + + Printf( + "INFO: you need to specify -runs=N or " + "-max_total_time=N with -minimize_crash=1\n" + "INFO: defaulting to -max_total_time=600\n"); + BaseCmd.addFlag("max_total_time", "600"); + + } + + BaseCmd.combineOutAndErr(); + + std::string CurrentFilePath = InputFilePath; + while (true) { + + Unit U = FileToVector(CurrentFilePath); + Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", + CurrentFilePath.c_str(), U.size()); + + Command Cmd(BaseCmd); + Cmd.addArgument(CurrentFilePath); + + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + std::string CmdOutput; + bool Success = ExecuteCommand(Cmd, &CmdOutput); + if (Success) { + + Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); + exit(1); + + } + + Printf( + "CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " + "it further\n", + CurrentFilePath.c_str(), U.size()); + auto DedupToken1 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken1.empty()) + Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); + + std::string ArtifactPath = + Flags.exact_artifact_path + ? Flags.exact_artifact_path + : Options.ArtifactPrefix + "minimized-from-" + Hash(U); + Cmd.addFlag("minimize_crash_internal_step", "1"); + Cmd.addFlag("exact_artifact_path", ArtifactPath); + Printf("CRASH_MIN: executing: %s\n", Cmd.toString().c_str()); + CmdOutput.clear(); + Success = ExecuteCommand(Cmd, &CmdOutput); + Printf("%s", CmdOutput.c_str()); + if (Success) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", + CurrentFilePath.c_str(), U.size()); + break; + + } + + auto DedupToken2 = GetDedupTokenFromCmdOutput(CmdOutput); + if (!DedupToken2.empty()) + Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); + + if (DedupToken1 != DedupToken2) { + + if (Flags.exact_artifact_path) { + + CurrentFilePath = Flags.exact_artifact_path; + WriteToFile(U, CurrentFilePath); + + } + + Printf( + "CRASH_MIN: mismatch in dedup tokens" + " (looks like a different bug). Won't minimize further\n"); + break; + + } + + CurrentFilePath = ArtifactPath; + Printf("*********************************\n"); + + } + + return 0; + +} + +int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { + + assert(Inputs->size() == 1); + std::string InputFilePath = Inputs->at(0); + Unit U = FileToVector(InputFilePath); + Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); + if (U.size() < 2) { + + Printf("INFO: The input is small enough, exiting\n"); + exit(0); + + } + + F->SetMaxInputLen(U.size()); + F->SetMaxMutationLen(U.size() - 1); + F->MinimizeCrashLoop(U); + Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); + exit(0); + return 0; + +} + +void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector &Args, + const Vector &Corpora, const char *CFPathOrNull) { + + if (Corpora.size() < 2) { + + Printf("INFO: Merge requires two or more corpus dirs\n"); + exit(0); + + } + + Vector OldCorpus, NewCorpus; + GetSizedFilesFromDir(Corpora[0], &OldCorpus); + for (size_t i = 1; i < Corpora.size(); i++) + GetSizedFilesFromDir(Corpora[i], &NewCorpus); + std::sort(OldCorpus.begin(), OldCorpus.end()); + std::sort(NewCorpus.begin(), NewCorpus.end()); + + std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath("Merge", ".txt"); + Vector NewFiles; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, + {}, &NewCov, CFPath, true); + for (auto &Path : NewFiles) + F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); + // We are done, delete the control file if it was a temporary one. + if (!Flags.merge_control_file) RemoveFile(CFPath); + + exit(0); + +} + +int AnalyzeDictionary(Fuzzer *F, const Vector &Dict, UnitVector &Corpus) { + + Printf("Started dictionary minimization (up to %d tests)\n", + Dict.size() * Corpus.size() * 2); + + // Scores and usage count for each dictionary unit. + Vector Scores(Dict.size()); + Vector Usages(Dict.size()); + + Vector InitialFeatures; + Vector ModifiedFeatures; + for (auto &C : Corpus) { + + // Get coverage for the testcase without modifications. + F->ExecuteCallback(C.data(), C.size()); + InitialFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { InitialFeatures.push_back(Feature); }); + + for (size_t i = 0; i < Dict.size(); ++i) { + + Vector Data = C; + auto StartPos = + std::search(Data.begin(), Data.end(), Dict[i].begin(), Dict[i].end()); + // Skip dictionary unit, if the testcase does not contain it. + if (StartPos == Data.end()) continue; + + ++Usages[i]; + while (StartPos != Data.end()) { + + // Replace all occurrences of dictionary unit in the testcase. + auto EndPos = StartPos + Dict[i].size(); + for (auto It = StartPos; It != EndPos; ++It) + *It ^= 0xFF; + + StartPos = + std::search(EndPos, Data.end(), Dict[i].begin(), Dict[i].end()); + + } + + // Get coverage for testcase with masked occurrences of dictionary unit. + F->ExecuteCallback(Data.data(), Data.size()); + ModifiedFeatures.clear(); + TPC.CollectFeatures( + [&](size_t Feature) { ModifiedFeatures.push_back(Feature); }); + + if (InitialFeatures == ModifiedFeatures) + --Scores[i]; + else + Scores[i] += 2; + + } + + } + + Printf("###### Useless dictionary elements. ######\n"); + for (size_t i = 0; i < Dict.size(); ++i) { + + // Dictionary units with positive score are treated as useful ones. + if (Scores[i] > 0) continue; + + Printf("\""); + PrintASCII(Dict[i].data(), Dict[i].size(), "\""); + Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); + + } + + Printf("###### End of useless dictionary elements. ######\n"); + return 0; + +} + +Vector ParseSeedInuts(const char *seed_inputs) { + + // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file + Vector Files; + if (!seed_inputs) return Files; + std::string SeedInputs; + if (Flags.seed_inputs[0] == '@') + SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. + else + SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. + if (SeedInputs.empty()) { + + Printf("seed_inputs is empty or @file does not exist.\n"); + exit(1); + + } + + // Parse SeedInputs. + size_t comma_pos = 0; + while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) { + + Files.push_back(SeedInputs.substr(comma_pos + 1)); + SeedInputs = SeedInputs.substr(0, comma_pos); + + } + + Files.push_back(SeedInputs); + return Files; + +} + +static Vector ReadCorpora( + const Vector &CorpusDirs, + const Vector &ExtraSeedFiles) { + + Vector SizedFiles; + size_t LastNumFiles = 0; + for (auto &Dir : CorpusDirs) { + + GetSizedFilesFromDir(Dir, &SizedFiles); + Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles, + Dir.c_str()); + LastNumFiles = SizedFiles.size(); + + } + + for (auto &File : ExtraSeedFiles) + if (auto Size = FileSize(File)) SizedFiles.push_back({File, Size}); + return SizedFiles; + +} + +int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { + + using namespace fuzzer; + assert(argc && argv && "Argument pointers cannot be nullptr"); + std::string Argv0((*argv)[0]); + EF = new ExternalFunctions(); + if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + const Vector Args(*argv, *argv + *argc); + assert(!Args.empty()); + ProgName = new std::string(Args[0]); + if (Argv0 != *ProgName) { + + Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); + exit(1); + + } + + ParseFlags(Args, EF); + if (Flags.help) { + + PrintHelp(); + return 0; + + } + + if (Flags.close_fd_mask & 2) DupAndCloseStderr(); + if (Flags.close_fd_mask & 1) CloseStdout(); + + if (Flags.jobs > 0 && Flags.workers == 0) { + + Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); + if (Flags.workers > 1) Printf("Running %u workers\n", Flags.workers); + + } + + if (Flags.workers > 0 && Flags.jobs > 0) + return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); + + FuzzingOptions Options; + Options.Verbosity = Flags.verbosity; + Options.MaxLen = Flags.max_len; + Options.LenControl = Flags.len_control; + Options.KeepSeed = Flags.keep_seed; + Options.UnitTimeoutSec = Flags.timeout; + Options.ErrorExitCode = Flags.error_exitcode; + Options.TimeoutExitCode = Flags.timeout_exitcode; + Options.IgnoreTimeouts = Flags.ignore_timeouts; + Options.IgnoreOOMs = Flags.ignore_ooms; + Options.IgnoreCrashes = Flags.ignore_crashes; + Options.MaxTotalTimeSec = Flags.max_total_time; + Options.DoCrossOver = Flags.cross_over; + Options.CrossOverUniformDist = Flags.cross_over_uniform_dist; + Options.MutateDepth = Flags.mutate_depth; + Options.ReduceDepth = Flags.reduce_depth; + Options.UseCounters = Flags.use_counters; + Options.UseMemmem = Flags.use_memmem; + Options.UseCmp = Flags.use_cmp; + Options.UseValueProfile = Flags.use_value_profile; + Options.Shrink = Flags.shrink; + Options.ReduceInputs = Flags.reduce_inputs; + Options.ShuffleAtStartUp = Flags.shuffle; + Options.PreferSmall = Flags.prefer_small; + Options.ReloadIntervalSec = Flags.reload; + Options.OnlyASCII = Flags.only_ascii; + Options.DetectLeaks = Flags.detect_leaks; + Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval; + Options.TraceMalloc = Flags.trace_malloc; + Options.RssLimitMb = Flags.rss_limit_mb; + Options.MallocLimitMb = Flags.malloc_limit_mb; + if (!Options.MallocLimitMb) Options.MallocLimitMb = Options.RssLimitMb; + if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) { + + // Ensure output corpus assumed to be the first arbitrary argument input + // is not a path to an existing file. + std::string OutputCorpusDir = (*Inputs)[0]; + if (!IsFile(OutputCorpusDir)) { + + Options.OutputCorpus = OutputCorpusDir; + ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs); + + } + + } + + Options.ReportSlowUnits = Flags.report_slow_units; + if (Flags.artifact_prefix) { + + Options.ArtifactPrefix = Flags.artifact_prefix; + + // Since the prefix could be a full path to a file name prefix, assume + // that if the path ends with the platform's separator that a directory + // is desired + std::string ArtifactPathDir = Options.ArtifactPrefix; + if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { + + ArtifactPathDir = DirName(ArtifactPathDir); + + } + + ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs); + + } + + if (Flags.exact_artifact_path) { + + Options.ExactArtifactPath = Flags.exact_artifact_path; + ValidateDirectoryExists(DirName(Options.ExactArtifactPath), + Flags.create_missing_dirs); + + } + + Vector Dictionary; + if (Flags.dict) + if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) return 1; + if (Flags.verbosity > 0 && !Dictionary.empty()) + Printf("Dictionary: %zd entries\n", Dictionary.size()); + bool RunIndividualFiles = AllInputsAreFiles(); + Options.SaveArtifacts = + !RunIndividualFiles || Flags.minimize_crash_internal_step; + Options.PrintNewCovPcs = Flags.print_pcs; + Options.PrintNewCovFuncs = Flags.print_funcs; + Options.PrintFinalStats = Flags.print_final_stats; + Options.PrintCorpusStats = Flags.print_corpus_stats; + Options.PrintCoverage = Flags.print_coverage; + if (Flags.exit_on_src_pos) Options.ExitOnSrcPos = Flags.exit_on_src_pos; + if (Flags.exit_on_item) Options.ExitOnItem = Flags.exit_on_item; + if (Flags.focus_function) Options.FocusFunction = Flags.focus_function; + if (Flags.data_flow_trace) Options.DataFlowTrace = Flags.data_flow_trace; + if (Flags.features_dir) { + + Options.FeaturesDir = Flags.features_dir; + ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs); + + } + + if (Flags.mutation_graph_file) + Options.MutationGraphFile = Flags.mutation_graph_file; + if (Flags.collect_data_flow) + Options.CollectDataFlow = Flags.collect_data_flow; + if (Flags.stop_file) Options.StopFile = Flags.stop_file; + Options.Entropic = Flags.entropic; + Options.EntropicFeatureFrequencyThreshold = + (size_t)Flags.entropic_feature_frequency_threshold; + Options.EntropicNumberOfRarestFeatures = + (size_t)Flags.entropic_number_of_rarest_features; + Options.EntropicScalePerExecTime = Flags.entropic_scale_per_exec_time; + if (Options.Entropic) { + + if (!Options.FocusFunction.empty()) { + + Printf( + "ERROR: The parameters `--entropic` and `--focus_function` cannot " + "be used together.\n"); + exit(1); + + } + + Printf("INFO: Running with entropic power schedule (0x%X, %d).\n", + Options.EntropicFeatureFrequencyThreshold, + Options.EntropicNumberOfRarestFeatures); + + } + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + Entropic.FeatureFrequencyThreshold = + Options.EntropicFeatureFrequencyThreshold; + Entropic.NumberOfRarestFeatures = Options.EntropicNumberOfRarestFeatures; + Entropic.ScalePerExecTime = Options.EntropicScalePerExecTime; + + unsigned Seed = Flags.seed; + // Initialize Seed. + if (Seed == 0) + Seed = + std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); + if (Flags.verbosity) Printf("INFO: Seed: %u\n", Seed); + + if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { + + if (RunIndividualFiles) + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora({}, *Inputs)); + else + return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, + ReadCorpora(*Inputs, {})); + + } + + Random Rand(Seed); + auto * MD = new MutationDispatcher(Rand, Options); + auto * Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto * F = new Fuzzer(Callback, *Corpus, *MD, Options); + + for (auto &U : Dictionary) + if (U.size() <= Word::GetMaxSize()) + MD->AddWordToManualDictionary(Word(U.data(), U.size())); + + // Threads are only supported by Chrome. Don't use them with emscripten + // for now. +#if !LIBFUZZER_EMSCRIPTEN + StartRssThread(F, Flags.rss_limit_mb); +#endif // LIBFUZZER_EMSCRIPTEN + + Options.HandleAbrt = Flags.handle_abrt; + Options.HandleAlrm = !Flags.minimize_crash; + Options.HandleBus = Flags.handle_bus; + Options.HandleFpe = Flags.handle_fpe; + Options.HandleIll = Flags.handle_ill; + Options.HandleInt = Flags.handle_int; + Options.HandleSegv = Flags.handle_segv; + Options.HandleTerm = Flags.handle_term; + Options.HandleXfsz = Flags.handle_xfsz; + Options.HandleUsr1 = Flags.handle_usr1; + Options.HandleUsr2 = Flags.handle_usr2; + SetSignalHandler(Options); + + std::atexit(Fuzzer::StaticExitCallback); + + if (Flags.minimize_crash) return MinimizeCrashInput(Args, Options); + + if (Flags.minimize_crash_internal_step) + return MinimizeCrashInputInternalStep(F, Corpus); + + if (Flags.cleanse_crash) return CleanseCrashInput(Args, Options); + + if (RunIndividualFiles) { + + Options.SaveArtifacts = false; + int Runs = std::max(1, Flags.runs); + Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), + Inputs->size(), Runs); + for (auto &Path : *Inputs) { + + auto StartTime = system_clock::now(); + Printf("Running: %s\n", Path.c_str()); + for (int Iter = 0; Iter < Runs; Iter++) + RunOneTest(F, Path.c_str(), Options.MaxLen); + auto StopTime = system_clock::now(); + auto MS = duration_cast(StopTime - StartTime).count(); + Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); + + } + + Printf( + "***\n" + "*** NOTE: fuzzing was not performed, you have only\n" + "*** executed the target code on a fixed set of inputs.\n" + "***\n"); + F->PrintFinalStats(); + exit(0); + + } + + if (Flags.fork) + FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); + + if (Flags.merge) Merge(F, Options, Args, *Inputs, Flags.merge_control_file); + + if (Flags.merge_inner) { + + const size_t kDefaultMaxMergeLen = 1 << 20; + if (Options.MaxLen == 0) F->SetMaxInputLen(kDefaultMaxMergeLen); + assert(Flags.merge_control_file); + F->CrashResistantMergeInternalStep(Flags.merge_control_file); + exit(0); + + } + + if (Flags.analyze_dict) { + + size_t MaxLen = INT_MAX; // Large max length. + UnitVector InitialCorpus; + for (auto &Inp : *Inputs) { + + Printf("Loading corpus dir: %s\n", Inp.c_str()); + ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, MaxLen, + /*ExitOnError=*/false); + + } + + if (Dictionary.empty() || Inputs->empty()) { + + Printf("ERROR: can't analyze dict without dict and corpus provided\n"); + return 1; + + } + + if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { + + Printf("Dictionary analysis failed\n"); + exit(1); + + } + + Printf("Dictionary analysis succeeded\n"); + exit(0); + + } + + auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); + F->Loop(CorporaFiles); + + if (Flags.verbosity) + Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), + F->secondsSinceProcessStartUp()); + F->PrintFinalStats(); + + exit(0); // Don't let F destroy itself. + +} + +extern "C" ATTRIBUTE_INTERFACE int LLVMFuzzerRunDriver( + int *argc, char ***argv, int (*UserCb)(const uint8_t *Data, size_t Size)) { + + return FuzzerDriver(argc, argv, UserCb); + +} + +#include "libfuzzer.inc" + +// Storage for global ExternalFunctions object. +ExternalFunctions *EF = nullptr; + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.def b/custom_mutators/libfuzzer/FuzzerExtFunctions.def new file mode 100644 index 00000000..51edf844 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.def @@ -0,0 +1,50 @@ +//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This defines the external function pointers that +// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The +// EXT_FUNC macro must be defined at the point of inclusion. The signature of +// the macro is: +// +// EXT_FUNC(, , , ) +//===----------------------------------------------------------------------===// + +// Optional user functions +EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false); +EXT_FUNC(LLVMFuzzerCustomMutator, size_t, + (uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed), + false); +EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t, + (const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, + uint8_t *Out, size_t MaxOutSize, unsigned int Seed), + false); + +// Sanitizer functions +EXT_FUNC(__lsan_enable, void, (), false); +EXT_FUNC(__lsan_disable, void, (), false); +EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false); +EXT_FUNC(__sanitizer_acquire_crash_state, int, (), true); +EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int, + (void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)), + false); +EXT_FUNC(__sanitizer_log_write, void, (const char *buf, size_t len), false); +EXT_FUNC(__sanitizer_purge_allocator, void, (), false); +EXT_FUNC(__sanitizer_print_memory_profile, void, (size_t, size_t), false); +EXT_FUNC(__sanitizer_print_stack_trace, void, (), true); +EXT_FUNC(__sanitizer_symbolize_pc, void, + (void *, const char *fmt, char *out_buf, size_t out_buf_size), false); +EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int, + (void *pc, char *module_path, + size_t module_path_len,void **pc_offset), false); +EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true); +EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); +EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false); +EXT_FUNC(__msan_unpoison_param, void, (size_t n), false); diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctions.h b/custom_mutators/libfuzzer/FuzzerExtFunctions.h new file mode 100644 index 00000000..c88aac4e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctions.h @@ -0,0 +1,34 @@ +//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Defines an interface to (possibly optional) functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H +#define LLVM_FUZZER_EXT_FUNCTIONS_H + +#include +#include + +namespace fuzzer { + +struct ExternalFunctions { + // Initialize function pointers. Functions that are not available will be set + // to nullptr. Do not call this constructor before ``main()`` has been + // entered. + ExternalFunctions(); + +#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE(*NAME) FUNC_SIG = nullptr + +#include "FuzzerExtFunctions.def" + +#undef EXT_FUNC +}; +} // namespace fuzzer + +#endif diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp new file mode 100644 index 00000000..8009b237 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsDlsym.cpp @@ -0,0 +1,60 @@ +//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for operating systems that support dlsym(). We only use it on +// Apple platforms for now. We don't use this approach on Linux because it +// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker. +// That is a complication we don't wish to expose to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + +using namespace fuzzer; + +template +static T GetFnPtr(const char *FnName, bool WarnIfMissing) { + + dlerror(); // Clear any previous errors. + void *Fn = dlsym(RTLD_DEFAULT, FnName); + if (Fn == nullptr) { + + if (WarnIfMissing) { + + const char *ErrorMsg = dlerror(); + Printf("WARNING: Failed to find function \"%s\".", FnName); + if (ErrorMsg) Printf(" Reason %s.", ErrorMsg); + Printf("\n"); + + } + + } + + return reinterpret_cast(Fn); + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(#NAME, WARN) + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp new file mode 100644 index 00000000..c7a1d05e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWeak.cpp @@ -0,0 +1,63 @@ +//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation for Linux. This relies on the linker's support for weak +// symbols. We don't use this approach on Apple platforms because it requires +// clients of LibFuzzer to pass ``-U _`` to the linker to allow +// weak symbols to be undefined. That is a complication we don't want to expose +// to clients right now. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FUCHSIA || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +extern "C" { + + // Declare these symbols as weak to allow them to be optionally defined. + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +using namespace fuzzer; + +static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) { + + if (FnPtr == nullptr && WarnIfMissing) { + + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + + } + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = ::NAME; \ + CheckFnPtr(reinterpret_cast(reinterpret_cast(::NAME)), \ + #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp new file mode 100644 index 00000000..a727220a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtFunctionsWindows.cpp @@ -0,0 +1,95 @@ +//=== FuzzerExtWindows.cpp - Interface to external functions --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when +// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately +// the method each compiler supports is not supported by the other. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + +using namespace fuzzer; + + // Intermediate macro to ensure the parameter is expanded before stringified. + #define STRINGIFY_(A) #A + #define STRINGIFY(A) STRINGIFY_(A) + + #if LIBFUZZER_MSVC + // Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h + #if defined(_M_IX86) || defined(__i386__) + #define WIN_SYM_PREFIX "_" + #else + #define WIN_SYM_PREFIX + #endif + + // Declare external functions as having alternativenames, so that we can + // determine if they are not defined. + #define EXTERNAL_FUNC(Name, Default) \ + __pragma( \ + comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \ + Name) "=" WIN_SYM_PREFIX STRINGIFY(Default))) + #else + // Declare external functions as weak to allow them to default to a + // specified function if not defined explicitly. We must use weak symbols + // because clang's support for alternatename is not 100%, see + // https://bugs.llvm.org/show_bug.cgi?id=40218 for more details. + #define EXTERNAL_FUNC(Name, Default) \ + __attribute__((weak, alias(STRINGIFY(Default)))) + #endif // LIBFUZZER_MSVC + +extern "C" { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + RETURN_TYPE NAME##Def FUNC_SIG { \ + \ + Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \ + exit(1); \ + \ + } \ + EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +template +static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) { + + if (Fun == FunDef) { + + if (WarnIfMissing) + Printf("WARNING: Failed to find function \"%s\".\n", FnName); + return nullptr; + + } + + return Fun; + +} + +namespace fuzzer { + +ExternalFunctions::ExternalFunctions() { +\ + #define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \ + this->NAME = GetFnPtr(::NAME, ::NAME##Def, #NAME, WARN); + + #include "FuzzerExtFunctions.def" + + #undef EXT_FUNC + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp new file mode 100644 index 00000000..3ff9b0d5 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerExtraCounters.cpp @@ -0,0 +1,71 @@ +//===- FuzzerExtraCounters.cpp - Extra coverage counters ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Extra coverage counters defined by user code. +//===----------------------------------------------------------------------===// + +#include "FuzzerPlatform.h" +#include + +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_FUCHSIA || LIBFUZZER_EMSCRIPTEN +__attribute__((weak)) extern uint8_t __start___libfuzzer_extra_counters; +__attribute__((weak)) extern uint8_t __stop___libfuzzer_extra_counters; + +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return &__start___libfuzzer_extra_counters; + +} + +uint8_t *ExtraCountersEnd() { + + return &__stop___libfuzzer_extra_counters; + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void ClearExtraCounters() { // hand-written memset, don't asan-ify. + uintptr_t *Beg = reinterpret_cast(ExtraCountersBegin()); + uintptr_t *End = reinterpret_cast(ExtraCountersEnd()); + for (; Beg < End; Beg++) { + + *Beg = 0; + __asm__ __volatile__("" : : : "memory"); + + } + +} + +} // namespace fuzzer + +#else +// TODO: implement for other platforms. +namespace fuzzer { + +uint8_t *ExtraCountersBegin() { + + return nullptr; + +} + +uint8_t *ExtraCountersEnd() { + + return nullptr; + +} + +void ClearExtraCounters() { + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerFlags.def b/custom_mutators/libfuzzer/FuzzerFlags.def new file mode 100644 index 00000000..c9a787e0 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFlags.def @@ -0,0 +1,197 @@ +//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the +// point of inclusion. We are not using any flag parsing library for better +// portability and independence. +//===----------------------------------------------------------------------===// +FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.") +FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.") +FUZZER_FLAG_INT(runs, -1, + "Number of individual test runs (-1 for infinite runs).") +FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. " + "If 0, libFuzzer tries to guess a good value based on the corpus " + "and reports it. ") +FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, " + "then try larger inputs over time. Specifies the rate at which the length " + "limit is increased (smaller == faster). If 0, immediately try inputs with " + "size up to max_len. Default value is 0, if LLVMFuzzerCustomMutator is used.") +FUZZER_FLAG_STRING(seed_inputs, "A comma-separated list of input files " + "to use as an additional seed corpus. Alternatively, an \"@\" followed by " + "the name of a file containing the comma-separated list.") +FUZZER_FLAG_INT(keep_seed, 0, "If 1, keep seed inputs in the corpus even if " + "they do not produce new coverage. When used with |reduce_inputs==1|, the " + "seed inputs will never be reduced. This option can be useful when seeds are" + "not properly formed for the fuzz target but still have useful snippets.") +FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.") +FUZZER_FLAG_INT(cross_over_uniform_dist, 0, "Experimental. If 1, use a " + "uniform probability distribution when choosing inputs to cross over with. " + "Some of the inputs in the corpus may never get chosen for mutation " + "depending on the input mutation scheduling policy. With this flag, all " + "inputs, regardless of the input mutation scheduling policy, can be chosen " + "as an input to cross over with. This can be particularly useful with " + "|keep_seed==1|; all the initial seed inputs, even though they do not " + "increase coverage because they are not properly formed, will still be " + "chosen as an input to cross over with.") + +FUZZER_FLAG_INT(mutate_depth, 5, + "Apply this number of consecutive mutations to each input.") +FUZZER_FLAG_INT(reduce_depth, 0, "Experimental/internal. " + "Reduce depth if mutations lose unique features") +FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup") +FUZZER_FLAG_INT(prefer_small, 1, + "If 1, always prefer smaller inputs during the corpus shuffle.") +FUZZER_FLAG_INT( + timeout, 1200, + "Timeout in seconds (if positive). " + "If one unit runs more than this number of seconds the process will abort.") +FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug " + "this exit code will be used.") +FUZZER_FLAG_INT(timeout_exitcode, 70, "When libFuzzer reports a timeout " + "this exit code will be used.") +FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total " + "time in seconds to run the fuzzer.") +FUZZER_FLAG_INT(help, 0, "Print help.") +FUZZER_FLAG_INT(fork, 0, "Experimental mode where fuzzing happens " + "in a subprocess") +FUZZER_FLAG_INT(ignore_timeouts, 1, "Ignore timeouts in fork mode") +FUZZER_FLAG_INT(ignore_ooms, 1, "Ignore OOMs in fork mode") +FUZZER_FLAG_INT(ignore_crashes, 0, "Ignore crashes in fork mode") +FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be " + "merged into the 1-st corpus. Only interesting units will be taken. " + "This flag can be used to minimize a corpus.") +FUZZER_FLAG_STRING(stop_file, "Stop fuzzing ASAP if this file exists") +FUZZER_FLAG_STRING(merge_inner, "internal flag") +FUZZER_FLAG_STRING(merge_control_file, + "Specify a control file used for the merge process. " + "If a merge process gets killed it tries to leave this file " + "in a state suitable for resuming the merge. " + "By default a temporary file will be used." + "The same file can be used for multistep merge process.") +FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided" + " crash input. Use with -runs=N or -max_total_time=N to limit " + "the number attempts." + " Use with -exact_artifact_path to specify the output." + " Combine with ASAN_OPTIONS=dedup_token_length=3 (or similar) to ensure that" + " the minimized input triggers the same crash." + ) +FUZZER_FLAG_INT(cleanse_crash, 0, "If 1, tries to cleanse the provided" + " crash input to make it contain fewer original bytes." + " Use with -exact_artifact_path to specify the output." + ) +FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag") +FUZZER_FLAG_STRING(features_dir, "internal flag. Used to dump feature sets on disk." + "Every time a new input is added to the corpus, a corresponding file in the features_dir" + " is created containing the unique features of that input." + " Features are stored in binary format.") +FUZZER_FLAG_STRING(mutation_graph_file, "Saves a graph (in DOT format) to" + " mutation_graph_file. The graph contains a vertex for each input that has" + " unique coverage; directed edges are provided between parents and children" + " where the child has unique coverage, and are recorded with the type of" + " mutation that caused the child.") +FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters") +FUZZER_FLAG_INT(use_memmem, 1, + "Use hints from intercepting memmem, strstr, etc") +FUZZER_FLAG_INT(use_value_profile, 0, + "Experimental. Use value profile to guide fuzzing.") +FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations") +FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus inputs.") +FUZZER_FLAG_INT(reduce_inputs, 1, + "Try to reduce the size of inputs while preserving their full feature sets") +FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn" + " this number of jobs in separate worker processes" + " with stdout/stderr redirected to fuzz-JOB.log.") +FUZZER_FLAG_UNSIGNED(workers, 0, + "Number of simultaneous worker processes to run the jobs." + " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.") +FUZZER_FLAG_INT(reload, 1, + "Reload the main corpus every seconds to get new units" + " discovered by other processes. If 0, disabled") +FUZZER_FLAG_INT(report_slow_units, 10, + "Report slowest units if they run for more than this number of seconds.") +FUZZER_FLAG_INT(only_ascii, 0, + "If 1, generate only ASCII (isprint+isspace) inputs.") +FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.") +FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, " + "timeout, or slow inputs) as " + "$(artifact_prefix)file") +FUZZER_FLAG_STRING(exact_artifact_path, + "Write the single artifact on failure (crash, timeout) " + "as $(exact_artifact_path). This overrides -artifact_prefix " + "and will not use checksum in the file name. Do not " + "use the same path for several parallel processes.") +FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.") +FUZZER_FLAG_INT(print_funcs, 2, "If >=1, print out at most this number of " + "newly covered functions.") +FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.") +FUZZER_FLAG_INT(print_corpus_stats, 0, + "If 1, print statistics on corpus elements at exit.") +FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text" + " at exit.") +FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated.") +FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.") +FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.") +FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.") +FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.") +FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.") +FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.") +FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.") +FUZZER_FLAG_INT(handle_xfsz, 1, "If 1, try to intercept SIGXFSZ.") +FUZZER_FLAG_INT(handle_usr1, 1, "If 1, try to intercept SIGUSR1.") +FUZZER_FLAG_INT(handle_usr2, 1, "If 1, try to intercept SIGUSR2.") +FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; " + "if 2, close stderr; if 3, close both. " + "Be careful, this will also close e.g. stderr of asan.") +FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled " + "try to detect memory leaks during fuzzing (i.e. not only at shut down).") +FUZZER_FLAG_INT(purge_allocator_interval, 1, "Purge allocator caches and " + "quarantines every seconds. When rss_limit_mb is specified (>0), " + "purging starts when RSS exceeds 50% of rss_limit_mb. Pass " + "purge_allocator_interval=-1 to disable this functionality.") +FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. " + "If >= 2 will also print stack traces.") +FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon" + "reaching this limit of RSS memory usage.") +FUZZER_FLAG_INT(malloc_limit_mb, 0, "If non-zero, the fuzzer will exit " + "if the target tries to allocate this number of Mb with one malloc call. " + "If zero (default) same limit as rss_limit_mb is applied.") +FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates" + " from the given source location. Example: -exit_on_src_pos=foo.cc:123. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum" + " was added to the corpus. " + "Used primarily for testing libFuzzer itself.") +FUZZER_FLAG_INT(ignore_remaining_args, 0, "If 1, ignore all arguments passed " + "after this one. Useful for fuzzers that need to do their own " + "argument parsing.") +FUZZER_FLAG_STRING(focus_function, "Experimental. " + "Fuzzing will focus on inputs that trigger calls to this function. " + "If -focus_function=auto and -data_flow_trace is used, libFuzzer " + "will choose the focus functions automatically.") +FUZZER_FLAG_INT(entropic, 0, "Experimental. Enables entropic power schedule.") +FUZZER_FLAG_INT(entropic_feature_frequency_threshold, 0xFF, "Experimental. If " + "entropic is enabled, all features which are observed less often than " + "the specified value are considered as rare.") +FUZZER_FLAG_INT(entropic_number_of_rarest_features, 100, "Experimental. If " + "entropic is enabled, we keep track of the frequencies only for the " + "Top-X least abundant features (union features that are considered as " + "rare).") +FUZZER_FLAG_INT(entropic_scale_per_exec_time, 0, "Experimental. If 1, " + "the Entropic power schedule gets scaled based on the input execution " + "time. Inputs with lower execution time get scheduled more (up to 30x). " + "Note that, if 1, fuzzer stops from being deterministic even if a " + "non-zero random seed is given.") + +FUZZER_FLAG_INT(analyze_dict, 0, "Experimental") +FUZZER_DEPRECATED_FLAG(use_clang_coverage) +FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") +FUZZER_FLAG_STRING(collect_data_flow, + "Experimental: collect the data flow trace") + +FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create " + "directories for arguments that would normally expect them to already " + "exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)") diff --git a/custom_mutators/libfuzzer/FuzzerFork.cpp b/custom_mutators/libfuzzer/FuzzerFork.cpp new file mode 100644 index 00000000..d6ffed74 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.cpp @@ -0,0 +1,501 @@ +//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Spawn and orchestrate separate fuzzing processes. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerFork.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMerge.h" +#include "FuzzerSHA1.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +struct Stats { + + size_t number_of_executed_units = 0; + size_t peak_rss_mb = 0; + size_t average_exec_per_sec = 0; + +}; + +static Stats ParseFinalStatsFromLog(const std::string &LogPath) { + + std::ifstream In(LogPath); + std::string Line; + Stats Res; + struct { + + const char *Name; + size_t * Var; + + } NameVarPairs[] = { + + {"stat::number_of_executed_units:", &Res.number_of_executed_units}, + {"stat::peak_rss_mb:", &Res.peak_rss_mb}, + {"stat::average_exec_per_sec:", &Res.average_exec_per_sec}, + {nullptr, nullptr}, + + }; + + while (std::getline(In, Line, '\n')) { + + if (Line.find("stat::") != 0) continue; + std::istringstream ISS(Line); + std::string Name; + size_t Val; + ISS >> Name >> Val; + for (size_t i = 0; NameVarPairs[i].Name; i++) + if (Name == NameVarPairs[i].Name) *NameVarPairs[i].Var = Val; + + } + + return Res; + +} + +struct FuzzJob { + + // Inputs. + Command Cmd; + std::string CorpusDir; + std::string FeaturesDir; + std::string LogPath; + std::string SeedListPath; + std::string CFPath; + size_t JobId; + + int DftTimeInSeconds = 0; + + // Fuzzing Outputs. + int ExitCode; + + ~FuzzJob() { + + RemoveFile(CFPath); + RemoveFile(LogPath); + RemoveFile(SeedListPath); + RmDirRecursive(CorpusDir); + RmDirRecursive(FeaturesDir); + + } + +}; + +struct GlobalEnv { + + Vector Args; + Vector CorpusDirs; + std::string MainCorpusDir; + std::string TempDir; + std::string DFTDir; + std::string DataFlowBinary; + Set Features, Cov; + Set FilesWithDFT; + Vector Files; + Random * Rand; + std::chrono::system_clock::time_point ProcessStartTime; + int Verbosity = 0; + + size_t NumTimeouts = 0; + size_t NumOOMs = 0; + size_t NumCrashes = 0; + + size_t NumRuns = 0; + + std::string StopFile() { + + return DirPlusFile(TempDir, "STOP"); + + } + + size_t secondsSinceProcessStartUp() const { + + return std::chrono::duration_cast( + std::chrono::system_clock::now() - ProcessStartTime) + .count(); + + } + + FuzzJob *CreateNewJob(size_t JobId) { + + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.removeFlag("collect_data_flow"); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.addFlag("reload", "0"); // working in an isolated dir, no reload. + Cmd.addFlag("print_final_stats", "1"); + Cmd.addFlag("print_funcs", "0"); // no need to spend time symbolizing. + Cmd.addFlag("max_total_time", std::to_string(std::min((size_t)300, JobId))); + Cmd.addFlag("stop_file", StopFile()); + if (!DataFlowBinary.empty()) { + + Cmd.addFlag("data_flow_trace", DFTDir); + if (!Cmd.hasFlag("focus_function")) Cmd.addFlag("focus_function", "auto"); + + } + + auto Job = new FuzzJob; + std::string Seeds; + if (size_t CorpusSubsetSize = + std::min(Files.size(), (size_t)sqrt(Files.size() + 2))) { + + auto Time1 = std::chrono::system_clock::now(); + for (size_t i = 0; i < CorpusSubsetSize; i++) { + + auto &SF = Files[Rand->SkewTowardsLast(Files.size())]; + Seeds += (Seeds.empty() ? "" : ",") + SF; + CollectDFT(SF); + + } + + auto Time2 = std::chrono::system_clock::now(); + Job->DftTimeInSeconds = duration_cast(Time2 - Time1).count(); + + } + + if (!Seeds.empty()) { + + Job->SeedListPath = + DirPlusFile(TempDir, std::to_string(JobId) + ".seeds"); + WriteToFile(Seeds, Job->SeedListPath); + Cmd.addFlag("seed_inputs", "@" + Job->SeedListPath); + + } + + Job->LogPath = DirPlusFile(TempDir, std::to_string(JobId) + ".log"); + Job->CorpusDir = DirPlusFile(TempDir, "C" + std::to_string(JobId)); + Job->FeaturesDir = DirPlusFile(TempDir, "F" + std::to_string(JobId)); + Job->CFPath = DirPlusFile(TempDir, std::to_string(JobId) + ".merge"); + Job->JobId = JobId; + + Cmd.addArgument(Job->CorpusDir); + Cmd.addFlag("features_dir", Job->FeaturesDir); + + for (auto &D : {Job->CorpusDir, Job->FeaturesDir}) { + + RmDirRecursive(D); + MkDir(D); + + } + + Cmd.setOutputFile(Job->LogPath); + Cmd.combineOutAndErr(); + + Job->Cmd = Cmd; + + if (Verbosity >= 2) + Printf("Job %zd/%p Created: %s\n", JobId, Job, + Job->Cmd.toString().c_str()); + // Start from very short runs and gradually increase them. + return Job; + + } + + void RunOneMergeJob(FuzzJob *Job) { + + auto Stats = ParseFinalStatsFromLog(Job->LogPath); + NumRuns += Stats.number_of_executed_units; + + Vector TempFiles, MergeCandidates; + // Read all newly created inputs and their feature sets. + // Choose only those inputs that have new features. + GetSizedFilesFromDir(Job->CorpusDir, &TempFiles); + std::sort(TempFiles.begin(), TempFiles.end()); + for (auto &F : TempFiles) { + + auto FeatureFile = F.File; + FeatureFile.replace(0, Job->CorpusDir.size(), Job->FeaturesDir); + auto FeatureBytes = FileToVector(FeatureFile, 0, false); + assert((FeatureBytes.size() % sizeof(uint32_t)) == 0); + Vector NewFeatures(FeatureBytes.size() / sizeof(uint32_t)); + memcpy(NewFeatures.data(), FeatureBytes.data(), FeatureBytes.size()); + for (auto Ft : NewFeatures) { + + if (!Features.count(Ft)) { + + MergeCandidates.push_back(F); + break; + + } + + } + + } + + // if (!FilesToAdd.empty() || Job->ExitCode != 0) + Printf( + "#%zd: cov: %zd ft: %zd corp: %zd exec/s %zd " + "oom/timeout/crash: %zd/%zd/%zd time: %zds job: %zd dft_time: %d\n", + NumRuns, Cov.size(), Features.size(), Files.size(), + Stats.average_exec_per_sec, NumOOMs, NumTimeouts, NumCrashes, + secondsSinceProcessStartUp(), Job->JobId, Job->DftTimeInSeconds); + + if (MergeCandidates.empty()) return; + + Vector FilesToAdd; + Set NewFeatures, NewCov; + CrashResistantMerge(Args, {}, MergeCandidates, &FilesToAdd, Features, + &NewFeatures, Cov, &NewCov, Job->CFPath, false); + for (auto &Path : FilesToAdd) { + + auto U = FileToVector(Path); + auto NewPath = DirPlusFile(MainCorpusDir, Hash(U)); + WriteToFile(U, NewPath); + Files.push_back(NewPath); + + } + + Features.insert(NewFeatures.begin(), NewFeatures.end()); + Cov.insert(NewCov.begin(), NewCov.end()); + for (auto Idx : NewCov) + if (auto *TE = TPC.PCTableEntryByIdx(Idx)) + if (TPC.PcIsFuncEntry(TE)) + PrintPC(" NEW_FUNC: %p %F %L\n", "", + TPC.GetNextInstructionPc(TE->PC)); + + } + + void CollectDFT(const std::string &InputPath) { + + if (DataFlowBinary.empty()) return; + if (!FilesWithDFT.insert(InputPath).second) return; + Command Cmd(Args); + Cmd.removeFlag("fork"); + Cmd.removeFlag("runs"); + Cmd.addFlag("data_flow_trace", DFTDir); + Cmd.addArgument(InputPath); + for (auto &C : CorpusDirs) // Remove all corpora from the args. + Cmd.removeArgument(C); + Cmd.setOutputFile(DirPlusFile(TempDir, "dft.log")); + Cmd.combineOutAndErr(); + // Printf("CollectDFT: %s\n", Cmd.toString().c_str()); + ExecuteCommand(Cmd); + + } + +}; + +struct JobQueue { + + std::queue Qu; + std::mutex Mu; + std::condition_variable Cv; + + void Push(FuzzJob *Job) { + + { + + std::lock_guard Lock(Mu); + Qu.push(Job); + + } + + Cv.notify_one(); + + } + + FuzzJob *Pop() { + + std::unique_lock Lk(Mu); + // std::lock_guard Lock(Mu); + Cv.wait(Lk, [&] { return !Qu.empty(); }); + assert(!Qu.empty()); + auto Job = Qu.front(); + Qu.pop(); + return Job; + + } + +}; + +void WorkerThread(JobQueue *FuzzQ, JobQueue *MergeQ) { + + while (auto Job = FuzzQ->Pop()) { + + // Printf("WorkerThread: job %p\n", Job); + Job->ExitCode = ExecuteCommand(Job->Cmd); + MergeQ->Push(Job); + + } + +} + +// This is just a skeleton of an experimental -fork=1 feature. +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs) { + + Printf("INFO: -fork=%d: fuzzing in separate process(s)\n", NumJobs); + + GlobalEnv Env; + Env.Args = Args; + Env.CorpusDirs = CorpusDirs; + Env.Rand = &Rand; + Env.Verbosity = Options.Verbosity; + Env.ProcessStartTime = std::chrono::system_clock::now(); + Env.DataFlowBinary = Options.CollectDataFlow; + + Vector SeedFiles; + for (auto &Dir : CorpusDirs) + GetSizedFilesFromDir(Dir, &SeedFiles); + std::sort(SeedFiles.begin(), SeedFiles.end()); + Env.TempDir = TempPath("FuzzWithFork", ".dir"); + Env.DFTDir = DirPlusFile(Env.TempDir, "DFT"); + RmDirRecursive(Env.TempDir); // in case there is a leftover from old runs. + MkDir(Env.TempDir); + MkDir(Env.DFTDir); + + if (CorpusDirs.empty()) + MkDir(Env.MainCorpusDir = DirPlusFile(Env.TempDir, "C")); + else + Env.MainCorpusDir = CorpusDirs[0]; + + if (Options.KeepSeed) { + + for (auto &File : SeedFiles) + Env.Files.push_back(File.File); + + } else { + + auto CFPath = DirPlusFile(Env.TempDir, "merge.txt"); + CrashResistantMerge(Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features, + {}, &Env.Cov, CFPath, false); + RemoveFile(CFPath); + + } + + Printf("INFO: -fork=%d: %zd seed inputs, starting to fuzz in %s\n", NumJobs, + Env.Files.size(), Env.TempDir.c_str()); + + int ExitCode = 0; + + JobQueue FuzzQ, MergeQ; + + auto StopJobs = [&]() { + + for (int i = 0; i < NumJobs; i++) + FuzzQ.Push(nullptr); + MergeQ.Push(nullptr); + WriteToFile(Unit({1}), Env.StopFile()); + + }; + + size_t JobId = 1; + Vector Threads; + for (int t = 0; t < NumJobs; t++) { + + Threads.push_back(std::thread(WorkerThread, &FuzzQ, &MergeQ)); + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + while (true) { + + std::unique_ptr Job(MergeQ.Pop()); + if (!Job) break; + ExitCode = Job->ExitCode; + if (ExitCode == Options.InterruptExitCode) { + + Printf("==%lu== libFuzzer: a child was interrupted; exiting\n", GetPid()); + StopJobs(); + break; + + } + + Fuzzer::MaybeExitGracefully(); + + Env.RunOneMergeJob(Job.get()); + + // Continue if our crash is one of the ignorred ones. + if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode) + Env.NumTimeouts++; + else if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode) + Env.NumOOMs++; + else if (ExitCode != 0) { + + Env.NumCrashes++; + if (Options.IgnoreCrashes) { + + std::ifstream In(Job->LogPath); + std::string Line; + while (std::getline(In, Line, '\n')) + if (Line.find("ERROR:") != Line.npos || + Line.find("runtime error:") != Line.npos) + Printf("%s\n", Line.c_str()); + + } else { + + // And exit if we don't ignore this crash. + Printf("INFO: log from the inner process:\n%s", + FileToString(Job->LogPath).c_str()); + StopJobs(); + break; + + } + + } + + // Stop if we are over the time budget. + // This is not precise, since other threads are still running + // and we will wait while joining them. + // We also don't stop instantly: other jobs need to finish. + if (Options.MaxTotalTimeSec > 0 && + Env.secondsSinceProcessStartUp() >= (size_t)Options.MaxTotalTimeSec) { + + Printf("INFO: fuzzed for %zd seconds, wrapping up soon\n", + Env.secondsSinceProcessStartUp()); + StopJobs(); + break; + + } + + if (Env.NumRuns >= Options.MaxNumberOfRuns) { + + Printf("INFO: fuzzed for %zd iterations, wrapping up soon\n", + Env.NumRuns); + StopJobs(); + break; + + } + + FuzzQ.Push(Env.CreateNewJob(JobId++)); + + } + + for (auto &T : Threads) + T.join(); + + // The workers have terminated. Don't try to remove the directory before they + // terminate to avoid a race condition preventing cleanup on Windows. + RmDirRecursive(Env.TempDir); + + // Use the exit code from the last child process. + Printf("INFO: exiting: %d time: %zds\n", ExitCode, + Env.secondsSinceProcessStartUp()); + exit(ExitCode); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerFork.h b/custom_mutators/libfuzzer/FuzzerFork.h new file mode 100644 index 00000000..b29a43e1 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerFork.h @@ -0,0 +1,24 @@ +//===- FuzzerFork.h - run fuzzing in sub-processes --------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_FORK_H +#define LLVM_FUZZER_FORK_H + +#include "FuzzerDefs.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +#include + +namespace fuzzer { +void FuzzWithFork(Random &Rand, const FuzzingOptions &Options, + const Vector &Args, + const Vector &CorpusDirs, int NumJobs); +} // namespace fuzzer + +#endif // LLVM_FUZZER_FORK_H diff --git a/custom_mutators/libfuzzer/FuzzerIO.cpp b/custom_mutators/libfuzzer/FuzzerIO.cpp new file mode 100644 index 00000000..e0c15db4 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.cpp @@ -0,0 +1,248 @@ +//===- FuzzerIO.cpp - IO utils. -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerUtil.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +static FILE *OutputFile = stderr; + +long GetEpoch(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; // Can't stat, be conservative. + return St.st_mtime; + +} + +Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) { + + std::ifstream T(Path, std::ios::binary); + if (ExitOnError && !T) { + + Printf("No such directory: %s; exiting\n", Path.c_str()); + exit(1); + + } + + T.seekg(0, T.end); + auto EndPos = T.tellg(); + if (EndPos < 0) return {}; + size_t FileLen = EndPos; + if (MaxSize) FileLen = std::min(FileLen, MaxSize); + + T.seekg(0, T.beg); + Unit Res(FileLen); + T.read(reinterpret_cast(Res.data()), FileLen); + return Res; + +} + +std::string FileToString(const std::string &Path) { + + std::ifstream T(Path, std::ios::binary); + return std::string((std::istreambuf_iterator(T)), + std::istreambuf_iterator()); + +} + +void CopyFileToErr(const std::string &Path) { + + Printf("%s", FileToString(Path).c_str()); + +} + +void WriteToFile(const Unit &U, const std::string &Path) { + + WriteToFile(U.data(), U.size(), Path); + +} + +void WriteToFile(const std::string &Data, const std::string &Path) { + + WriteToFile(reinterpret_cast(Data.c_str()), Data.size(), + Path); + +} + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + // Use raw C interface because this function may be called from a sig handler. + FILE *Out = fopen(Path.c_str(), "wb"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void AppendToFile(const std::string &Data, const std::string &Path) { + + AppendToFile(reinterpret_cast(Data.data()), Data.size(), + Path); + +} + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path) { + + FILE *Out = fopen(Path.c_str(), "a"); + if (!Out) return; + fwrite(Data, sizeof(Data[0]), Size, Out); + fclose(Out); + +} + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, long *Epoch, + size_t MaxSize, bool ExitOnError) { + + long E = Epoch ? *Epoch : 0; + Vector Files; + ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/ true); + size_t NumLoaded = 0; + for (size_t i = 0; i < Files.size(); i++) { + + auto &X = Files[i]; + if (Epoch && GetEpoch(X) < E) continue; + NumLoaded++; + if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024) + Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path); + auto S = FileToVector(X, MaxSize, ExitOnError); + if (!S.empty()) V->push_back(S); + + } + +} + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V) { + + Vector Files; + ListFilesInDirRecursive(Dir, 0, &Files, /*TopDir*/ true); + for (auto &File : Files) + if (size_t Size = FileSize(File)) V->push_back({File, Size}); + +} + +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName) { + + return DirPath + GetSeparator() + FileName; + +} + +void DupAndCloseStderr() { + + int OutputFd = DuplicateFile(2); + if (OutputFd >= 0) { + + FILE *NewOutputFile = OpenFile(OutputFd, "w"); + if (NewOutputFile) { + + OutputFile = NewOutputFile; + if (EF->__sanitizer_set_report_fd) + EF->__sanitizer_set_report_fd( + reinterpret_cast(GetHandleFromFd(OutputFd))); + DiscardOutput(2); + + } + + } + +} + +void CloseStdout() { + + DiscardOutput(1); + +} + +void Printf(const char *Fmt, ...) { + + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +void VPrintf(bool Verbose, const char *Fmt, ...) { + + if (!Verbose) return; + va_list ap; + va_start(ap, Fmt); + vfprintf(OutputFile, Fmt, ap); + va_end(ap); + fflush(OutputFile); + +} + +static bool MkDirRecursiveInner(const std::string &Leaf) { + + // Prevent chance of potential infinite recursion + if (Leaf == ".") return true; + + const std::string &Dir = DirName(Leaf); + + if (IsDirectory(Dir)) { + + MkDir(Leaf); + return IsDirectory(Leaf); + + } + + bool ret = MkDirRecursiveInner(Dir); + if (!ret) { + + // Give up early if a previous MkDir failed + return ret; + + } + + MkDir(Leaf); + return IsDirectory(Leaf); + +} + +bool MkDirRecursive(const std::string &Dir) { + + if (Dir.empty()) return false; + + if (IsDirectory(Dir)) return true; + + return MkDirRecursiveInner(Dir); + +} + +void RmDirRecursive(const std::string &Dir) { + + IterateDirRecursive( + Dir, [](const std::string &Path) {}, + [](const std::string &Path) { RmDir(Path); }, + [](const std::string &Path) { RemoveFile(Path); }); + +} + +std::string TempPath(const char *Prefix, const char *Extension) { + + return DirPlusFile(TmpDir(), std::string("libFuzzerTemp.") + Prefix + + std::to_string(GetPid()) + Extension); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerIO.h b/custom_mutators/libfuzzer/FuzzerIO.h new file mode 100644 index 00000000..abd25110 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIO.h @@ -0,0 +1,112 @@ +//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO interface. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_IO_H +#define LLVM_FUZZER_IO_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +long GetEpoch(const std::string &Path); + +Unit FileToVector(const std::string &Path, size_t MaxSize = 0, + bool ExitOnError = true); + +std::string FileToString(const std::string &Path); + +void CopyFileToErr(const std::string &Path); + +void WriteToFile(const uint8_t *Data, size_t Size, const std::string &Path); +// Write Data.c_str() to the file without terminating null character. +void WriteToFile(const std::string &Data, const std::string &Path); +void WriteToFile(const Unit &U, const std::string &Path); + +void AppendToFile(const uint8_t *Data, size_t Size, const std::string &Path); +void AppendToFile(const std::string &Data, const std::string &Path); + +void ReadDirToVectorOfUnits(const char *Path, Vector *V, + long *Epoch, size_t MaxSize, bool ExitOnError); + +// Returns "Dir/FileName" or equivalent for the current OS. +std::string DirPlusFile(const std::string &DirPath, + const std::string &FileName); + +// Returns the name of the dir, similar to the 'dirname' utility. +std::string DirName(const std::string &FileName); + +// Returns path to a TmpDir. +std::string TmpDir(); + +std::string TempPath(const char *Prefix, const char *Extension); + +bool IsInterestingCoverageFile(const std::string &FileName); + +void DupAndCloseStderr(); + +void CloseStdout(); + +void Printf(const char *Fmt, ...); +void VPrintf(bool Verbose, const char *Fmt, ...); + +// Print using raw syscalls, useful when printing at early init stages. +void RawPrint(const char *Str); + +// Platform specific functions: +bool IsFile(const std::string &Path); +bool IsDirectory(const std::string &Path); +size_t FileSize(const std::string &Path); + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir); + +bool MkDirRecursive(const std::string &Dir); +void RmDirRecursive(const std::string &Dir); + +// Iterate files and dirs inside Dir, recursively. +// Call DirPreCallback/DirPostCallback on dirs before/after +// calling FileCallback on files. +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)); + +struct SizedFile { + std::string File; + size_t Size; + bool operator<(const SizedFile &B) const { return Size < B.Size; } +}; + +void GetSizedFilesFromDir(const std::string &Dir, Vector *V); + +char GetSeparator(); +bool IsSeparator(char C); +// Similar to the basename utility: returns the file name w/o the dir prefix. +std::string Basename(const std::string &Path); + +FILE* OpenFile(int Fd, const char *Mode); + +int CloseFile(int Fd); + +int DuplicateFile(int Fd); + +void RemoveFile(const std::string &Path); +void RenameFile(const std::string &OldPath, const std::string &NewPath); + +intptr_t GetHandleFromFd(int fd); + +void MkDir(const std::string &Path); +void RmDir(const std::string &Path); + +const std::string &getDevNull(); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_IO_H diff --git a/custom_mutators/libfuzzer/FuzzerIOPosix.cpp b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp new file mode 100644 index 00000000..36ec5a9c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOPosix.cpp @@ -0,0 +1,223 @@ +//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX || LIBFUZZER_FUCHSIA + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +bool IsFile(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISREG(St.st_mode); + +} + +bool IsDirectory(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return false; + return S_ISDIR(St.st_mode); + +} + +size_t FileSize(const std::string &Path) { + + struct stat St; + if (stat(Path.c_str(), &St)) return 0; + return St.st_size; + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.rfind(GetSeparator()); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + DIR *D = opendir(Dir.c_str()); + if (!D) { + + Printf("%s: %s; exiting\n", strerror(errno), Dir.c_str()); + exit(1); + + } + + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + V->push_back(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + ListFilesInDirRecursive(Path, Epoch, V, false); + + } + + closedir(D); + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + DirPreCallback(Dir); + DIR *D = opendir(Dir.c_str()); + if (!D) return; + while (auto E = readdir(D)) { + + std::string Path = DirPlusFile(Dir, E->d_name); + if (E->d_type == DT_REG || E->d_type == DT_LNK || + (E->d_type == DT_UNKNOWN && IsFile(Path))) + FileCallback(Path); + else if ((E->d_type == DT_DIR || + (E->d_type == DT_UNKNOWN && IsDirectory(Path))) && + *E->d_name != '.') + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } + + closedir(D); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '/'; + +} + +bool IsSeparator(char C) { + + return C == '/'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return fdopen(Fd, Mode); + +} + +int CloseFile(int fd) { + + return close(fd); + +} + +int DuplicateFile(int Fd) { + + return dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return static_cast(fd); + +} + +std::string DirName(const std::string &FileName) { + + char *Tmp = new char[FileName.size() + 1]; + memcpy(Tmp, FileName.c_str(), FileName.size() + 1); + std::string Res = dirname(Tmp); + delete[] Tmp; + return Res; + +} + +std::string TmpDir() { + + if (auto Env = getenv("TMPDIR")) return Env; + return "/tmp"; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("compiler-rt/lib/") != std::string::npos) + return false; // sanitizer internal. + if (FileName.find("/usr/lib/") != std::string::npos) return false; + if (FileName.find("/usr/include/") != std::string::npos) return false; + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + mkdir(Path.c_str(), 0700); + +} + +void RmDir(const std::string &Path) { + + rmdir(Path.c_str()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "/dev/null"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerIOWindows.cpp b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp new file mode 100644 index 00000000..9352984a --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerIOWindows.cpp @@ -0,0 +1,513 @@ +//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// IO functions implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + + #include "FuzzerExtFunctions.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static bool IsFile(const std::string &Path, const DWORD &FileAttributes) { + + if (FileAttributes & FILE_ATTRIBUTE_NORMAL) return true; + + if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) return false; + + HANDLE FileHandle(CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); + + if (FileHandle == INVALID_HANDLE_VALUE) { + + Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + return false; + + } + + DWORD FileType = GetFileType(FileHandle); + + if (FileType == FILE_TYPE_UNKNOWN) { + + Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(), + GetLastError()); + CloseHandle(FileHandle); + return false; + + } + + if (FileType != FILE_TYPE_DISK) { + + CloseHandle(FileHandle); + return false; + + } + + CloseHandle(FileHandle); + return true; + +} + +bool IsFile(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsFile(Path, Att); + +} + +static bool IsDir(DWORD FileAttrs) { + + if (FileAttrs == INVALID_FILE_ATTRIBUTES) return false; + return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; + +} + +bool IsDirectory(const std::string &Path) { + + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + + } + + return IsDir(Att); + +} + +std::string Basename(const std::string &Path) { + + size_t Pos = Path.find_last_of("/\\"); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); + +} + +size_t FileSize(const std::string &Path) { + + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) + Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), LastError); + return 0; + + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + return size.QuadPart; + +} + +void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, + Vector *V, bool TopDir) { + + auto E = GetEpoch(Dir); + if (Epoch) + if (E && *Epoch >= E) return; + + std::string Path(Dir); + assert(!Path.empty()); + if (Path.back() != '\\') Path.push_back('\\'); + Path.push_back('*'); + + // Get the first directory entry. + WIN32_FIND_DATAA FindInfo; + HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo)); + if (FindHandle == INVALID_HANDLE_VALUE) { + + if (GetLastError() == ERROR_FILE_NOT_FOUND) return; + Printf("No such file or directory: %s; exiting\n", Dir.c_str()); + exit(1); + + } + + do { + + std::string FileName = DirPlusFile(Dir, FindInfo.cFileName); + + if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + + size_t FilenameLen = strlen(FindInfo.cFileName); + if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') || + (FilenameLen == 2 && FindInfo.cFileName[0] == '.' && + FindInfo.cFileName[1] == '.')) + continue; + + ListFilesInDirRecursive(FileName, Epoch, V, false); + + } else if (IsFile(FileName, FindInfo.dwFileAttributes)) + + V->push_back(FileName); + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed (Error code: %lu).\n", LastError); + + FindClose(FindHandle); + + if (Epoch && TopDir) *Epoch = E; + +} + +void IterateDirRecursive(const std::string &Dir, + void (*DirPreCallback)(const std::string &Dir), + void (*DirPostCallback)(const std::string &Dir), + void (*FileCallback)(const std::string &Dir)) { + + // TODO(metzman): Implement ListFilesInDirRecursive via this function. + DirPreCallback(Dir); + + DWORD DirAttrs = GetFileAttributesA(Dir.c_str()); + if (!IsDir(DirAttrs)) return; + + std::string TargetDir(Dir); + assert(!TargetDir.empty()); + if (TargetDir.back() != '\\') TargetDir.push_back('\\'); + TargetDir.push_back('*'); + + WIN32_FIND_DATAA FindInfo; + // Find the directory's first file. + HANDLE FindHandle = FindFirstFileA(TargetDir.c_str(), &FindInfo); + if (FindHandle == INVALID_HANDLE_VALUE) { + + DWORD LastError = GetLastError(); + if (LastError != ERROR_FILE_NOT_FOUND) { + + // If the directory isn't empty, then something abnormal is going on. + Printf("FindFirstFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + } + + return; + + } + + do { + + std::string Path = DirPlusFile(Dir, FindInfo.cFileName); + DWORD PathAttrs = FindInfo.dwFileAttributes; + if (IsDir(PathAttrs)) { + + // Is Path the current directory (".") or the parent ("..")? + if (strcmp(FindInfo.cFileName, ".") == 0 || + strcmp(FindInfo.cFileName, "..") == 0) + continue; + IterateDirRecursive(Path, DirPreCallback, DirPostCallback, FileCallback); + + } else if (PathAttrs != INVALID_FILE_ATTRIBUTES) { + + FileCallback(Path); + + } + + } while (FindNextFileA(FindHandle, &FindInfo)); + + DWORD LastError = GetLastError(); + if (LastError != ERROR_NO_MORE_FILES) + Printf("FindNextFileA failed for %s (Error code: %lu).\n", Dir.c_str(), + LastError); + + FindClose(FindHandle); + DirPostCallback(Dir); + +} + +char GetSeparator() { + + return '\\'; + +} + +FILE *OpenFile(int Fd, const char *Mode) { + + return _fdopen(Fd, Mode); + +} + +int CloseFile(int Fd) { + + return _close(Fd); + +} + +int DuplicateFile(int Fd) { + + return _dup(Fd); + +} + +void RemoveFile(const std::string &Path) { + + _unlink(Path.c_str()); + +} + +void RenameFile(const std::string &OldPath, const std::string &NewPath) { + + rename(OldPath.c_str(), NewPath.c_str()); + +} + +intptr_t GetHandleFromFd(int fd) { + + return _get_osfhandle(fd); + +} + +bool IsSeparator(char C) { + + return C == '\\' || C == '/'; + +} + +// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:". +// Returns number of characters considered if successful. +static size_t ParseDrive(const std::string &FileName, const size_t Offset, + bool Relative = true) { + + if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':') return 0; + if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) { + + if (!Relative) // Accept relative path? + return 0; + else + return 2; + + } + + return 3; + +} + +// Parse a file name, like: SomeFile.txt +// Returns number of characters considered if successful. +static size_t ParseFileName(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + return Pos - Offset; + +} + +// Parse a directory ending in separator, like: `SomeDir\` +// Returns number of characters considered if successful. +static size_t ParseDir(const std::string &FileName, const size_t Offset) { + + size_t Pos = Offset; + const size_t End = FileName.size(); + if (Pos >= End || IsSeparator(FileName[Pos])) return 0; + for (; Pos < End && !IsSeparator(FileName[Pos]); ++Pos) + ; + if (Pos >= End) return 0; + ++Pos; // Include separator. + return Pos - Offset; + +} + +// Parse a servername and share, like: `SomeServer\SomeShare\` +// Returns number of characters considered if successful. +static size_t ParseServerAndShare(const std::string &FileName, + const size_t Offset) { + + size_t Pos = Offset, Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + if (!(Res = ParseDir(FileName, Pos))) return 0; + Pos += Res; + return Pos - Offset; + +} + +// Parse the given Ref string from the position Offset, to exactly match the +// given string Patt. Returns number of characters considered if successful. +static size_t ParseCustomString(const std::string &Ref, size_t Offset, + const char *Patt) { + + size_t Len = strlen(Patt); + if (Offset + Len > Ref.size()) return 0; + return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0; + +} + +// Parse a location, like: +// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C: +// Returns number of characters considered if successful. +static size_t ParseLocation(const std::string &FileName) { + + size_t Pos = 0, Res; + + if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) { + + Pos += Res; + if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) { + + Pos += Res; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + if ((Res = ParseDrive(FileName, Pos, false))) return Pos + Res; + return 0; + + } + + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if (Pos < FileName.size() && IsSeparator(FileName[Pos])) { + + ++Pos; + if ((Res = ParseServerAndShare(FileName, Pos))) return Pos + Res; + return 0; + + } + + return Pos; + + } + + if ((Res = ParseDrive(FileName, Pos))) return Pos + Res; + + return Pos; + +} + +std::string DirName(const std::string &FileName) { + + size_t LocationLen = ParseLocation(FileName); + size_t DirLen = 0, Res; + while ((Res = ParseDir(FileName, LocationLen + DirLen))) + DirLen += Res; + size_t FileLen = ParseFileName(FileName, LocationLen + DirLen); + + if (LocationLen + DirLen + FileLen != FileName.size()) { + + Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str()); + exit(1); + + } + + if (DirLen) { + + --DirLen; // Remove trailing separator. + if (!FileLen) { // Path ended in separator. + assert(DirLen); + // Remove file name from Dir. + while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1])) + --DirLen; + if (DirLen) // Remove trailing separator. + --DirLen; + + } + + } + + if (!LocationLen) { // Relative path. + if (!DirLen) return "."; + return std::string(".\\").append(FileName, 0, DirLen); + + } + + return FileName.substr(0, LocationLen + DirLen); + +} + +std::string TmpDir() { + + std::string Tmp; + Tmp.resize(MAX_PATH + 1); + DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]); + if (Size == 0) { + + Printf("Couldn't get Tmp path.\n"); + exit(1); + + } + + Tmp.resize(Size); + return Tmp; + +} + +bool IsInterestingCoverageFile(const std::string &FileName) { + + if (FileName.find("Program Files") != std::string::npos) return false; + if (FileName.find("compiler-rt\\lib\\") != std::string::npos) + return false; // sanitizer internal. + if (FileName == "") return false; + return true; + +} + +void RawPrint(const char *Str) { + + _write(2, Str, strlen(Str)); + +} + +void MkDir(const std::string &Path) { + + if (CreateDirectoryA(Path.c_str(), nullptr)) return; + Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +void RmDir(const std::string &Path) { + + if (RemoveDirectoryA(Path.c_str())) return; + Printf("RemoveDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), + GetLastError()); + +} + +const std::string &getDevNull() { + + static const std::string devNull = "NUL"; + return devNull; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerInterface.h b/custom_mutators/libfuzzer/FuzzerInterface.h new file mode 100644 index 00000000..4f62822e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInterface.h @@ -0,0 +1,79 @@ +//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the interface between libFuzzer and the library being tested. +//===----------------------------------------------------------------------===// + +// NOTE: the libFuzzer interface is thin and in the majority of cases +// you should not include this file into your target. In 95% of cases +// all you need is to define the following function in your file: +// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// WARNING: keep the interface in C. + +#ifndef LLVM_FUZZER_INTERFACE_H +#define LLVM_FUZZER_INTERFACE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that +// doesn't break MSVC. +#if defined(_WIN32) +#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport) +#else +#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default"))) +#endif + +// Mandatory user-provided target function. +// Executes the code under test with [Data, Data+Size) as the input. +// libFuzzer will invoke this function *many* times with different inputs. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +// Optional user-provided initialization function. +// If provided, this function will be called by libFuzzer once at startup. +// It may read and modify argc/argv. +// Must return 0. +FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Optional user-provided custom mutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +// Given the same Seed produces the same mutation. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, + unsigned int Seed); + +// Optional user-provided custom cross-over function. +// Combines pieces of Data1 & Data2 together into Out. +// Returns the new size, which is not greater than MaxOutSize. +// Should produce the same mutation given the same Seed. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1, + const uint8_t *Data2, size_t Size2, uint8_t *Out, + size_t MaxOutSize, unsigned int Seed); + +// Experimental, may go away in future. +// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator. +// Mutates raw data in [Data, Data+Size) inplace. +// Returns the new size, which is not greater than MaxSize. +FUZZER_INTERFACE_VISIBILITY size_t +LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); + +#undef FUZZER_INTERFACE_VISIBILITY + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // LLVM_FUZZER_INTERFACE_H diff --git a/custom_mutators/libfuzzer/FuzzerInternal.h b/custom_mutators/libfuzzer/FuzzerInternal.h new file mode 100644 index 00000000..2b172d91 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerInternal.h @@ -0,0 +1,173 @@ +//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Define the main class fuzzer::Fuzzer and most functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_INTERNAL_H +#define LLVM_FUZZER_INTERNAL_H + +#include "FuzzerDataFlowTrace.h" +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerInterface.h" +#include "FuzzerOptions.h" +#include "FuzzerSHA1.h" +#include "FuzzerValueBitMap.h" +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +using namespace std::chrono; + +class Fuzzer { +public: + + Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options); + ~Fuzzer(); + void Loop(Vector &CorporaFiles); + void ReadAndExecuteSeedCorpora(Vector &CorporaFiles); + void MinimizeCrashLoop(const Unit &U); + void RereadOutputCorpus(size_t MaxSize); + + size_t secondsSinceProcessStartUp() { + return duration_cast(system_clock::now() - ProcessStartTime) + .count(); + } + + bool TimedOut() { + return Options.MaxTotalTimeSec > 0 && + secondsSinceProcessStartUp() > + static_cast(Options.MaxTotalTimeSec); + } + + size_t execPerSec() { + size_t Seconds = secondsSinceProcessStartUp(); + return Seconds ? TotalNumberOfRuns / Seconds : 0; + } + + size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; } + + static void StaticAlarmCallback(); + static void StaticCrashSignalCallback(); + static void StaticExitCallback(); + static void StaticInterruptCallback(); + static void StaticFileSizeExceedCallback(); + static void StaticGracefulExitCallback(); + + void ExecuteCallback(const uint8_t *Data, size_t Size); + bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false, + InputInfo *II = nullptr, bool ForceAddToCorpus = false, + bool *FoundUniqFeatures = nullptr); + + // Merge Corpora[1:] into Corpora[0]. + void Merge(const Vector &Corpora); + void CrashResistantMergeInternalStep(const std::string &ControlFilePath); + MutationDispatcher &GetMD() { return MD; } + void PrintFinalStats(); + void SetMaxInputLen(size_t MaxInputLen); + void SetMaxMutationLen(size_t MaxMutationLen); + void RssLimitCallback(); + + bool InFuzzingThread() const { return IsMyThread; } + size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const; + void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution); + + void HandleMalloc(size_t Size); + static void MaybeExitGracefully(); + std::string WriteToOutputCorpus(const Unit &U); + +private: + void AlarmCallback(); + void CrashCallback(); + void ExitCallback(); + void CrashOnOverwrittenData(); + void InterruptCallback(); + void MutateAndTestOne(); + void PurgeAllocator(); + void ReportNewCoverage(InputInfo *II, const Unit &U); + void PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size); + void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix); + void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0, + size_t Features = 0); + void PrintStatusForNewUnit(const Unit &U, const char *Text); + void CheckExitOnSrcPosOrItem(); + + static void StaticDeathCallback(); + void DumpCurrentUnit(const char *Prefix); + void DeathCallback(); + + void AllocateCurrentUnitData(); + uint8_t *CurrentUnitData = nullptr; + std::atomic CurrentUnitSize; + uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit. + + bool GracefulExitRequested = false; + + size_t TotalNumberOfRuns = 0; + size_t NumberOfNewUnitsAdded = 0; + + size_t LastCorpusUpdateRun = 0; + + bool HasMoreMallocsThanFrees = false; + size_t NumberOfLeakDetectionAttempts = 0; + + system_clock::time_point LastAllocatorPurgeAttemptTime = system_clock::now(); + + UserCallback CB; + InputCorpus &Corpus; + MutationDispatcher &MD; + FuzzingOptions Options; + DataFlowTrace DFT; + + system_clock::time_point ProcessStartTime = system_clock::now(); + system_clock::time_point UnitStartTime, UnitStopTime; + long TimeOfLongestUnitInSeconds = 0; + long EpochOfLastReadOfOutputCorpus = 0; + + size_t MaxInputLen = 0; + size_t MaxMutationLen = 0; + size_t TmpMaxMutationLen = 0; + + Vector UniqFeatureSetTmp; + + // Need to know our own thread. + static thread_local bool IsMyThread; +}; + +struct ScopedEnableMsanInterceptorChecks { + ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } + ~ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } +}; + +struct ScopedDisableMsanInterceptorChecks { + ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } + ~ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_INTERNAL_H diff --git a/custom_mutators/libfuzzer/FuzzerLoop.cpp b/custom_mutators/libfuzzer/FuzzerLoop.cpp new file mode 100644 index 00000000..49187b30 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerLoop.cpp @@ -0,0 +1,1087 @@ +//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Fuzzer's main loop. +//===----------------------------------------------------------------------===// + +#include "FuzzerCorpus.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerMutate.h" +#include "FuzzerPlatform.h" +#include "FuzzerRandom.h" +#include "FuzzerTracePC.h" +#include +#include +#include +#include +#include + +#if defined(__has_include) + #if __has_include() + #include + #endif +#endif + +#define NO_SANITIZE_MEMORY +#if defined(__has_feature) + #if __has_feature(memory_sanitizer) + #undef NO_SANITIZE_MEMORY + #define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) + #endif +#endif + +namespace fuzzer { + +static const size_t kMaxUnitSizeToPrint = 256; + +thread_local bool Fuzzer::IsMyThread; + +bool RunningUserCallback = false; + +// Only one Fuzzer per process. +static Fuzzer *F; + +// Leak detection is expensive, so we first check if there were more mallocs +// than frees (using the sanitizer malloc hooks) and only then try to call lsan. +struct MallocFreeTracer { + + void Start(int TraceLevel) { + + this->TraceLevel = TraceLevel; + if (TraceLevel) Printf("MallocFreeTracer: START\n"); + Mallocs = 0; + Frees = 0; + + } + + // Returns true if there were more mallocs than frees. + bool Stop() { + + if (TraceLevel) + Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(), + Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT"); + bool Result = Mallocs > Frees; + Mallocs = 0; + Frees = 0; + TraceLevel = 0; + return Result; + + } + + std::atomic Mallocs; + std::atomic Frees; + int TraceLevel = 0; + + std::recursive_mutex TraceMutex; + bool TraceDisabled = false; + +}; + +static MallocFreeTracer AllocTracer; + +// Locks printing and avoids nested hooks triggered from mallocs/frees in +// sanitizer. +class TraceLock { + + public: + TraceLock() : Lock(AllocTracer.TraceMutex) { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + ~TraceLock() { + + AllocTracer.TraceDisabled = !AllocTracer.TraceDisabled; + + } + + bool IsDisabled() const { + + // This is already inverted value. + return !AllocTracer.TraceDisabled; + + } + + private: + std::lock_guard Lock; + +}; + +ATTRIBUTE_NO_SANITIZE_MEMORY +void MallocHook(const volatile void *ptr, size_t size) { + + size_t N = AllocTracer.Mallocs++; + F->HandleMalloc(size); + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("MALLOC[%zd] %p %zd\n", N, ptr, size); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +ATTRIBUTE_NO_SANITIZE_MEMORY +void FreeHook(const volatile void *ptr) { + + size_t N = AllocTracer.Frees++; + if (int TraceLevel = AllocTracer.TraceLevel) { + + TraceLock Lock; + if (Lock.IsDisabled()) return; + Printf("FREE[%zd] %p\n", N, ptr); + if (TraceLevel >= 2 && EF) PrintStackTrace(); + + } + +} + +// Crash on a single malloc that exceeds the rss limit. +void Fuzzer::HandleMalloc(size_t Size) { + + if (!Options.MallocLimitMb || (Size >> 20) < (size_t)Options.MallocLimitMb) + return; + Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(), + Size); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintStackTrace(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD, + FuzzingOptions Options) + : CB(CB), Corpus(Corpus), MD(MD), Options(Options) { + + if (EF->__sanitizer_set_death_callback) + EF->__sanitizer_set_death_callback(StaticDeathCallback); + assert(!F); + F = this; + TPC.ResetMaps(); + IsMyThread = true; + if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks) + EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook); + TPC.SetUseCounters(Options.UseCounters); + TPC.SetUseValueProfileMask(Options.UseValueProfile); + + if (Options.Verbosity) TPC.PrintModuleInfo(); + if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec) + EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus); + MaxInputLen = MaxMutationLen = Options.MaxLen; + TmpMaxMutationLen = 0; // Will be set once we load the corpus. + AllocateCurrentUnitData(); + CurrentUnitSize = 0; + memset(BaseSha1, 0, sizeof(BaseSha1)); + +} + +Fuzzer::~Fuzzer() { + +} + +void Fuzzer::AllocateCurrentUnitData() { + + if (CurrentUnitData || MaxInputLen == 0) return; + CurrentUnitData = new uint8_t[MaxInputLen]; + +} + +void Fuzzer::StaticDeathCallback() { + + assert(F); + F->DeathCallback(); + +} + +void Fuzzer::DumpCurrentUnit(const char *Prefix) { + + if (!CurrentUnitData) return; // Happens when running individual inputs. + ScopedDisableMsanInterceptorChecks S; + MD.PrintMutationSequence(); + Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); + size_t UnitSize = CurrentUnitSize; + if (UnitSize <= kMaxUnitSizeToPrint) { + + PrintHexArray(CurrentUnitData, UnitSize, "\n"); + PrintASCII(CurrentUnitData, UnitSize, "\n"); + + } + + WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize}, + Prefix); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::DeathCallback() { + + DumpCurrentUnit("crash-"); + PrintFinalStats(); + +} + +void Fuzzer::StaticAlarmCallback() { + + assert(F); + F->AlarmCallback(); + +} + +void Fuzzer::StaticCrashSignalCallback() { + + assert(F); + F->CrashCallback(); + +} + +void Fuzzer::StaticExitCallback() { + + assert(F); + F->ExitCallback(); + +} + +void Fuzzer::StaticInterruptCallback() { + + assert(F); + F->InterruptCallback(); + +} + +void Fuzzer::StaticGracefulExitCallback() { + + assert(F); + F->GracefulExitRequested = true; + Printf("INFO: signal received, trying to exit gracefully\n"); + +} + +void Fuzzer::StaticFileSizeExceedCallback() { + + Printf("==%lu== ERROR: libFuzzer: file size exceeded\n", GetPid()); + exit(1); + +} + +void Fuzzer::CrashCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid()); + PrintStackTrace(); + Printf( + "NOTE: libFuzzer has rudimentary signal handlers.\n" + " Combine libFuzzer with AddressSanitizer or similar for better " + "crash reports.\n"); + Printf("SUMMARY: libFuzzer: deadly signal\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +void Fuzzer::ExitCallback() { + + if (!RunningUserCallback) + return; // This exit did not come from the user callback + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("==%lu== ERROR: libFuzzer: fuzz target exited\n", GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: fuzz target exited\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); + +} + +void Fuzzer::MaybeExitGracefully() { + + if (!F->GracefulExitRequested) return; + Printf("==%lu== INFO: libFuzzer: exiting as requested\n", GetPid()); + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + F->PrintFinalStats(); + _Exit(0); + +} + +void Fuzzer::InterruptCallback() { + + Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid()); + PrintFinalStats(); + ScopedDisableMsanInterceptorChecks S; // RmDirRecursive may call opendir(). + RmDirRecursive(TempPath("FuzzWithFork", ".dir")); + // Stop right now, don't perform any at-exit actions. + _Exit(Options.InterruptExitCode); + +} + +NO_SANITIZE_MEMORY +void Fuzzer::AlarmCallback() { + + assert(Options.UnitTimeoutSec > 0); + // In Windows and Fuchsia, Alarm callback is executed by a different thread. + // NetBSD's current behavior needs this change too. +#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD && !LIBFUZZER_FUCHSIA + if (!InFuzzingThread()) return; +#endif + if (!RunningUserCallback) return; // We have not started running units yet. + size_t Seconds = + duration_cast(system_clock::now() - UnitStartTime).count(); + if (Seconds == 0) return; + if (Options.Verbosity >= 2) Printf("AlarmCallback %zd\n", Seconds); + if (Seconds >= (size_t)Options.UnitTimeoutSec) { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds); + Printf(" and the timeout value is %d (use -timeout=N to change)\n", + Options.UnitTimeoutSec); + DumpCurrentUnit("timeout-"); + Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(), + Seconds); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: timeout\n"); + PrintFinalStats(); + _Exit(Options.TimeoutExitCode); // Stop right now. + + } + +} + +void Fuzzer::RssLimitCallback() { + + if (EF->__sanitizer_acquire_crash_state && + !EF->__sanitizer_acquire_crash_state()) + return; + Printf( + "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n", + GetPid(), GetPeakRSSMb(), Options.RssLimitMb); + Printf(" To change the out-of-memory limit use -rss_limit_mb=\n\n"); + PrintMemoryProfile(); + DumpCurrentUnit("oom-"); + Printf("SUMMARY: libFuzzer: out-of-memory\n"); + PrintFinalStats(); + _Exit(Options.OOMExitCode); // Stop right now. + +} + +void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units, + size_t Features) { + + size_t ExecPerSec = execPerSec(); + if (!Options.Verbosity) return; + Printf("#%zd\t%s", TotalNumberOfRuns, Where); + if (size_t N = TPC.GetTotalPCCoverage()) Printf(" cov: %zd", N); + if (size_t N = Features ? Features : Corpus.NumFeatures()) + Printf(" ft: %zd", N); + if (!Corpus.empty()) { + + Printf(" corp: %zd", Corpus.NumActiveUnits()); + if (size_t N = Corpus.SizeInBytes()) { + + if (N < (1 << 14)) + Printf("/%zdb", N); + else if (N < (1 << 24)) + Printf("/%zdKb", N >> 10); + else + Printf("/%zdMb", N >> 20); + + } + + if (size_t FF = Corpus.NumInputsThatTouchFocusFunction()) + Printf(" focus: %zd", FF); + + } + + if (TmpMaxMutationLen) Printf(" lim: %zd", TmpMaxMutationLen); + if (Units) Printf(" units: %zd", Units); + + Printf(" exec/s: %zd", ExecPerSec); + Printf(" rss: %zdMb", GetPeakRSSMb()); + Printf("%s", End); + +} + +void Fuzzer::PrintFinalStats() { + + if (Options.PrintCoverage) TPC.PrintCoverage(); + if (Options.PrintCorpusStats) Corpus.PrintStats(); + if (!Options.PrintFinalStats) return; + size_t ExecPerSec = execPerSec(); + Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns); + Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec); + Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded); + Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds); + Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb()); + +} + +void Fuzzer::SetMaxInputLen(size_t MaxInputLen) { + + assert(this->MaxInputLen == + 0); // Can only reset MaxInputLen from 0 to non-0. + assert(MaxInputLen); + this->MaxInputLen = MaxInputLen; + this->MaxMutationLen = MaxInputLen; + AllocateCurrentUnitData(); + Printf( + "INFO: -max_len is not provided; " + "libFuzzer will not generate inputs larger than %zd bytes\n", + MaxInputLen); + +} + +void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) { + + assert(MaxMutationLen && MaxMutationLen <= MaxInputLen); + this->MaxMutationLen = MaxMutationLen; + +} + +void Fuzzer::CheckExitOnSrcPosOrItem() { + + if (!Options.ExitOnSrcPos.empty()) { + + static auto *PCsSet = new Set; + auto HandlePC = [&](const TracePC::PCTableEntry *TE) { + + if (!PCsSet->insert(TE->PC).second) return; + std::string Descr = DescribePC("%F %L", TE->PC + 1); + if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) { + + Printf("INFO: found line matching '%s', exiting.\n", + Options.ExitOnSrcPos.c_str()); + _Exit(0); + + } + + }; + + TPC.ForEachObservedPC(HandlePC); + + } + + if (!Options.ExitOnItem.empty()) { + + if (Corpus.HasUnit(Options.ExitOnItem)) { + + Printf("INFO: found item with checksum '%s', exiting.\n", + Options.ExitOnItem.c_str()); + _Exit(0); + + } + + } + +} + +void Fuzzer::RereadOutputCorpus(size_t MaxSize) { + + if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return; + Vector AdditionalCorpus; + ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus, + &EpochOfLastReadOfOutputCorpus, MaxSize, + /*ExitOnError*/ false); + if (Options.Verbosity >= 2) + Printf("Reload: read %zd new units.\n", AdditionalCorpus.size()); + bool Reloaded = false; + for (auto &U : AdditionalCorpus) { + + if (U.size() > MaxSize) U.resize(MaxSize); + if (!Corpus.HasUnit(U)) { + + if (RunOne(U.data(), U.size())) { + + CheckExitOnSrcPosOrItem(); + Reloaded = true; + + } + + } + + } + + if (Reloaded) PrintStats("RELOAD"); + +} + +void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) { + + auto TimeOfUnit = + duration_cast(UnitStopTime - UnitStartTime).count(); + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) && + secondsSinceProcessStartUp() >= 2) + PrintStats("pulse "); + if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 && + TimeOfUnit >= Options.ReportSlowUnits) { + + TimeOfLongestUnitInSeconds = TimeOfUnit; + Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds); + WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-"); + + } + +} + +static void WriteFeatureSetToFile(const std::string & FeaturesDir, + const std::string & FileName, + const Vector &FeatureSet) { + + if (FeaturesDir.empty() || FeatureSet.empty()) return; + WriteToFile(reinterpret_cast(FeatureSet.data()), + FeatureSet.size() * sizeof(FeatureSet[0]), + DirPlusFile(FeaturesDir, FileName)); + +} + +static void RenameFeatureSetFile(const std::string &FeaturesDir, + const std::string &OldFile, + const std::string &NewFile) { + + if (FeaturesDir.empty()) return; + RenameFile(DirPlusFile(FeaturesDir, OldFile), + DirPlusFile(FeaturesDir, NewFile)); + +} + +static void WriteEdgeToMutationGraphFile(const std::string &MutationGraphFile, + const InputInfo * II, + const InputInfo * BaseII, + const std::string &MS) { + + if (MutationGraphFile.empty()) return; + + std::string Sha1 = Sha1ToString(II->Sha1); + + std::string OutputString; + + // Add a new vertex. + OutputString.append("\""); + OutputString.append(Sha1); + OutputString.append("\"\n"); + + // Add a new edge if there is base input. + if (BaseII) { + + std::string BaseSha1 = Sha1ToString(BaseII->Sha1); + OutputString.append("\""); + OutputString.append(BaseSha1); + OutputString.append("\" -> \""); + OutputString.append(Sha1); + OutputString.append("\" [label=\""); + OutputString.append(MS); + OutputString.append("\"];\n"); + + } + + AppendToFile(OutputString, MutationGraphFile); + +} + +bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile, + InputInfo *II, bool ForceAddToCorpus, + bool *FoundUniqFeatures) { + + if (!Size) return false; + + ExecuteCallback(Data, Size); + auto TimeOfUnit = duration_cast(UnitStopTime - UnitStartTime); + + UniqFeatureSetTmp.clear(); + size_t FoundUniqFeaturesOfII = 0; + size_t NumUpdatesBefore = Corpus.NumFeatureUpdates(); + TPC.CollectFeatures([&](size_t Feature) { + + if (Corpus.AddFeature(Feature, Size, Options.Shrink)) + UniqFeatureSetTmp.push_back(Feature); + if (Options.Entropic) Corpus.UpdateFeatureFrequency(II, Feature); + if (Options.ReduceInputs && II && !II->NeverReduce) + if (std::binary_search(II->UniqFeatureSet.begin(), + II->UniqFeatureSet.end(), Feature)) + FoundUniqFeaturesOfII++; + + }); + + if (FoundUniqFeatures) *FoundUniqFeatures = FoundUniqFeaturesOfII; + PrintPulseAndReportSlowInput(Data, Size); + size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore; + if (NumNewFeatures || ForceAddToCorpus) { + + TPC.UpdateObservedPCs(); + auto NewII = + Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile, + TPC.ObservedFocusFunction(), ForceAddToCorpus, + TimeOfUnit, UniqFeatureSetTmp, DFT, II); + WriteFeatureSetToFile(Options.FeaturesDir, Sha1ToString(NewII->Sha1), + NewII->UniqFeatureSet); + WriteEdgeToMutationGraphFile(Options.MutationGraphFile, NewII, II, + MD.MutationSequence()); + return true; + + } + + if (II && FoundUniqFeaturesOfII && + II->DataFlowTraceForFocusFunction.empty() && + FoundUniqFeaturesOfII == II->UniqFeatureSet.size() && + II->U.size() > Size) { + + auto OldFeaturesFile = Sha1ToString(II->Sha1); + Corpus.Replace(II, {Data, Data + Size}); + RenameFeatureSetFile(Options.FeaturesDir, OldFeaturesFile, + Sha1ToString(II->Sha1)); + return true; + + } + + return false; + +} + +size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const { + + assert(InFuzzingThread()); + *Data = CurrentUnitData; + return CurrentUnitSize; + +} + +void Fuzzer::CrashOnOverwrittenData() { + + Printf("==%d== ERROR: libFuzzer: fuzz target overwrites its const input\n", + GetPid()); + PrintStackTrace(); + Printf("SUMMARY: libFuzzer: overwrites-const-input\n"); + DumpCurrentUnit("crash-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // Stop right now. + +} + +// Compare two arrays, but not all bytes if the arrays are large. +static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) { + + const size_t Limit = 64; + if (Size <= 64) return !memcmp(A, B, Size); + // Compare first and last Limit/2 bytes. + return !memcmp(A, B, Limit / 2) && + !memcmp(A + Size - Limit / 2, B + Size - Limit / 2, Limit / 2); + +} + +void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) { + + TPC.RecordInitialStack(); + TotalNumberOfRuns++; + assert(InFuzzingThread()); + // We copy the contents of Unit into a separate heap buffer + // so that we reliably find buffer overflows in it. + uint8_t *DataCopy = new uint8_t[Size]; + memcpy(DataCopy, Data, Size); + if (EF->__msan_unpoison) EF->__msan_unpoison(DataCopy, Size); + if (EF->__msan_unpoison_param) EF->__msan_unpoison_param(2); + if (CurrentUnitData && CurrentUnitData != Data) + memcpy(CurrentUnitData, Data, Size); + CurrentUnitSize = Size; + { + + ScopedEnableMsanInterceptorChecks S; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + TPC.ResetMaps(); + RunningUserCallback = true; + int Res = CB(DataCopy, Size); + RunningUserCallback = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + + } + + if (!LooseMemeq(DataCopy, Data, Size)) CrashOnOverwrittenData(); + CurrentUnitSize = 0; + delete[] DataCopy; + +} + +std::string Fuzzer::WriteToOutputCorpus(const Unit &U) { + + if (Options.OnlyASCII) assert(IsASCII(U)); + if (Options.OutputCorpus.empty()) return ""; + std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U)); + WriteToFile(U, Path); + if (Options.Verbosity >= 2) + Printf("Written %zd bytes to %s\n", U.size(), Path.c_str()); + return Path; + +} + +void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) { + + if (!Options.SaveArtifacts) return; + std::string Path = Options.ArtifactPrefix + Prefix + Hash(U); + if (!Options.ExactArtifactPath.empty()) + Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix. + WriteToFile(U, Path); + Printf("artifact_prefix='%s'; Test unit written to %s\n", + Options.ArtifactPrefix.c_str(), Path.c_str()); + if (U.size() <= kMaxUnitSizeToPrint) + Printf("Base64: %s\n", Base64(U).c_str()); + +} + +void Fuzzer::PrintStatusForNewUnit(const Unit &U, const char *Text) { + + if (!Options.PrintNEW) return; + PrintStats(Text, ""); + if (Options.Verbosity) { + + Printf(" L: %zd/%zd ", U.size(), Corpus.MaxInputSize()); + MD.PrintMutationSequence(); + Printf("\n"); + + } + +} + +void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) { + + II->NumSuccessfullMutations++; + MD.RecordSuccessfulMutationSequence(); + PrintStatusForNewUnit(U, II->Reduced ? "REDUCE" : "NEW "); + WriteToOutputCorpus(U); + NumberOfNewUnitsAdded++; + CheckExitOnSrcPosOrItem(); // Check only after the unit is saved to corpus. + LastCorpusUpdateRun = TotalNumberOfRuns; + +} + +// Tries detecting a memory leak on the particular input that we have just +// executed before calling this function. +void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size, + bool DuringInitialCorpusExecution) { + + if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely. + if (!Options.DetectLeaks) return; + if (!DuringInitialCorpusExecution && + TotalNumberOfRuns >= Options.MaxNumberOfRuns) + return; + if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) || + !(EF->__lsan_do_recoverable_leak_check)) + return; // No lsan. + // Run the target once again, but with lsan disabled so that if there is + // a real leak we do not report it twice. + EF->__lsan_disable(); + ExecuteCallback(Data, Size); + EF->__lsan_enable(); + if (!HasMoreMallocsThanFrees) return; // a leak is unlikely. + if (NumberOfLeakDetectionAttempts++ > 1000) { + + Options.DetectLeaks = false; + Printf( + "INFO: libFuzzer disabled leak detection after every mutation.\n" + " Most likely the target function accumulates allocated\n" + " memory in a global state w/o actually leaking it.\n" + " You may try running this binary with -trace_malloc=[12]" + " to get a trace of mallocs and frees.\n" + " If LeakSanitizer is enabled in this process it will still\n" + " run on the process shutdown.\n"); + return; + + } + + // Now perform the actual lsan pass. This is expensive and we must ensure + // we don't call it too often. + if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it. + if (DuringInitialCorpusExecution) + Printf("\nINFO: a leak has been found in the initial corpus.\n\n"); + Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n"); + CurrentUnitSize = Size; + DumpCurrentUnit("leak-"); + PrintFinalStats(); + _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on. + + } + +} + +void Fuzzer::MutateAndTestOne() { + + MD.StartMutationSequence(); + + auto &II = Corpus.ChooseUnitToMutate(MD.GetRand()); + if (Options.DoCrossOver) { + + auto &CrossOverII = Corpus.ChooseUnitToCrossOverWith( + MD.GetRand(), Options.CrossOverUniformDist); + MD.SetCrossOverWith(&CrossOverII.U); + + } + + const auto &U = II.U; + memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1)); + assert(CurrentUnitData); + size_t Size = U.size(); + assert(Size <= MaxInputLen && "Oversized Unit"); + memcpy(CurrentUnitData, U.data(), Size); + + assert(MaxMutationLen > 0); + + size_t CurrentMaxMutationLen = + Min(MaxMutationLen, Max(U.size(), TmpMaxMutationLen)); + assert(CurrentMaxMutationLen > 0); + + for (int i = 0; i < Options.MutateDepth; i++) { + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + MaybeExitGracefully(); + size_t NewSize = 0; + if (II.HasFocusFunction && !II.DataFlowTraceForFocusFunction.empty() && + Size <= CurrentMaxMutationLen) + NewSize = MD.MutateWithMask(CurrentUnitData, Size, Size, + II.DataFlowTraceForFocusFunction); + + // If MutateWithMask either failed or wasn't called, call default Mutate. + if (!NewSize) + NewSize = MD.Mutate(CurrentUnitData, Size, CurrentMaxMutationLen); + assert(NewSize > 0 && "Mutator returned empty unit"); + assert(NewSize <= CurrentMaxMutationLen && "Mutator return oversized unit"); + Size = NewSize; + II.NumExecutedMutations++; + Corpus.IncrementNumExecutedMutations(); + + bool FoundUniqFeatures = false; + bool NewCov = RunOne(CurrentUnitData, Size, /*MayDeleteFile=*/true, &II, + /*ForceAddToCorpus*/ false, &FoundUniqFeatures); + TryDetectingAMemoryLeak(CurrentUnitData, Size, + /*DuringInitialCorpusExecution*/ false); + if (NewCov) { + + ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size}); + break; // We will mutate this input more in the next rounds. + + } + + if (Options.ReduceDepth && !FoundUniqFeatures) break; + + } + + II.NeedsEnergyUpdate = true; + +} + +void Fuzzer::PurgeAllocator() { + + if (Options.PurgeAllocatorIntervalSec < 0 || !EF->__sanitizer_purge_allocator) + return; + if (duration_cast(system_clock::now() - + LastAllocatorPurgeAttemptTime) + .count() < Options.PurgeAllocatorIntervalSec) + return; + + if (Options.RssLimitMb <= 0 || + GetPeakRSSMb() > static_cast(Options.RssLimitMb) / 2) + EF->__sanitizer_purge_allocator(); + + LastAllocatorPurgeAttemptTime = system_clock::now(); + +} + +void Fuzzer::ReadAndExecuteSeedCorpora(Vector &CorporaFiles) { + + const size_t kMaxSaneLen = 1 << 20; + const size_t kMinDefaultLen = 4096; + size_t MaxSize = 0; + size_t MinSize = -1; + size_t TotalSize = 0; + for (auto &File : CorporaFiles) { + + MaxSize = Max(File.Size, MaxSize); + MinSize = Min(File.Size, MinSize); + TotalSize += File.Size; + + } + + if (Options.MaxLen == 0) + SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxSize), kMaxSaneLen)); + assert(MaxInputLen > 0); + + // Test the callback with empty input and never try it again. + uint8_t dummy = 0; + ExecuteCallback(&dummy, 0); + + if (CorporaFiles.empty()) { + + Printf("INFO: A corpus is not provided, starting from an empty corpus\n"); + Unit U({'\n'}); // Valid ASCII input. + RunOne(U.data(), U.size()); + + } else { + + Printf( + "INFO: seed corpus: files: %zd min: %zdb max: %zdb total: %zdb" + " rss: %zdMb\n", + CorporaFiles.size(), MinSize, MaxSize, TotalSize, GetPeakRSSMb()); + if (Options.ShuffleAtStartUp) + std::shuffle(CorporaFiles.begin(), CorporaFiles.end(), MD.GetRand()); + + if (Options.PreferSmall) { + + std::stable_sort(CorporaFiles.begin(), CorporaFiles.end()); + assert(CorporaFiles.front().Size <= CorporaFiles.back().Size); + + } + + // Load and execute inputs one by one. + for (auto &SF : CorporaFiles) { + + auto U = FileToVector(SF.File, MaxInputLen, /*ExitOnError=*/false); + assert(U.size() <= MaxInputLen); + RunOne(U.data(), U.size(), /*MayDeleteFile*/ false, /*II*/ nullptr, + /*ForceAddToCorpus*/ Options.KeepSeed, + /*FoundUniqFeatures*/ nullptr); + CheckExitOnSrcPosOrItem(); + TryDetectingAMemoryLeak(U.data(), U.size(), + /*DuringInitialCorpusExecution*/ true); + + } + + } + + PrintStats("INITED"); + if (!Options.FocusFunction.empty()) { + + Printf("INFO: %zd/%zd inputs touch the focus function\n", + Corpus.NumInputsThatTouchFocusFunction(), Corpus.size()); + if (!Options.DataFlowTrace.empty()) + Printf("INFO: %zd/%zd inputs have the Data Flow Trace\n", + Corpus.NumInputsWithDataFlowTrace(), + Corpus.NumInputsThatTouchFocusFunction()); + + } + + if (Corpus.empty() && Options.MaxNumberOfRuns) { + + Printf( + "ERROR: no interesting inputs were found. " + "Is the code instrumented for coverage? Exiting.\n"); + exit(1); + + } + +} + +void Fuzzer::Loop(Vector &CorporaFiles) { + + auto FocusFunctionOrAuto = Options.FocusFunction; + DFT.Init(Options.DataFlowTrace, &FocusFunctionOrAuto, CorporaFiles, + MD.GetRand()); + TPC.SetFocusFunction(FocusFunctionOrAuto); + ReadAndExecuteSeedCorpora(CorporaFiles); + DFT.Clear(); // No need for DFT any more. + TPC.SetPrintNewPCs(Options.PrintNewCovPcs); + TPC.SetPrintNewFuncs(Options.PrintNewCovFuncs); + system_clock::time_point LastCorpusReload = system_clock::now(); + + TmpMaxMutationLen = + Min(MaxMutationLen, Max(size_t(4), Corpus.MaxInputSize())); + + while (true) { + + auto Now = system_clock::now(); + if (!Options.StopFile.empty() && + !FileToVector(Options.StopFile, 1, false).empty()) + break; + if (duration_cast(Now - LastCorpusReload).count() >= + Options.ReloadIntervalSec) { + + RereadOutputCorpus(MaxInputLen); + LastCorpusReload = system_clock::now(); + + } + + if (TotalNumberOfRuns >= Options.MaxNumberOfRuns) break; + if (TimedOut()) break; + + // Update TmpMaxMutationLen + if (Options.LenControl) { + + if (TmpMaxMutationLen < MaxMutationLen && + TotalNumberOfRuns - LastCorpusUpdateRun > + Options.LenControl * Log(TmpMaxMutationLen)) { + + TmpMaxMutationLen = + Min(MaxMutationLen, TmpMaxMutationLen + Log(TmpMaxMutationLen)); + LastCorpusUpdateRun = TotalNumberOfRuns; + + } + + } else { + + TmpMaxMutationLen = MaxMutationLen; + + } + + // Perform several mutations and runs. + MutateAndTestOne(); + + PurgeAllocator(); + + } + + PrintStats("DONE ", "\n"); + MD.PrintRecommendedDictionary(); + +} + +void Fuzzer::MinimizeCrashLoop(const Unit &U) { + + if (U.size() <= 1) return; + while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) { + + MD.StartMutationSequence(); + memcpy(CurrentUnitData, U.data(), U.size()); + for (int i = 0; i < Options.MutateDepth; i++) { + + size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen); + assert(NewSize > 0 && NewSize <= MaxMutationLen); + ExecuteCallback(CurrentUnitData, NewSize); + PrintPulseAndReportSlowInput(CurrentUnitData, NewSize); + TryDetectingAMemoryLeak(CurrentUnitData, NewSize, + /*DuringInitialCorpusExecution*/ false); + + } + + } + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + assert(fuzzer::F); + return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.cpp b/custom_mutators/libfuzzer/FuzzerMerge.cpp new file mode 100644 index 00000000..b341f5b3 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.cpp @@ -0,0 +1,485 @@ +//===- FuzzerMerge.cpp - merging corpora ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging corpora. +//===----------------------------------------------------------------------===// + +#include "FuzzerCommand.h" +#include "FuzzerMerge.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include "FuzzerTracePC.h" +#include "FuzzerUtil.h" + +#include +#include +#include +#include +#include + +namespace fuzzer { + +bool Merger::Parse(const std::string &Str, bool ParseCoverage) { + + std::istringstream SS(Str); + return Parse(SS, ParseCoverage); + +} + +void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) { + + if (!Parse(IS, ParseCoverage)) { + + Printf("MERGE: failed to parse the control file (unexpected error)\n"); + exit(1); + + } + +} + +// The control file example: +// +// 3 # The number of inputs +// 1 # The number of inputs in the first corpus, <= the previous number +// file0 +// file1 +// file2 # One file name per line. +// STARTED 0 123 # FileID, file size +// FT 0 1 4 6 8 # FileID COV1 COV2 ... +// COV 0 7 8 9 # FileID COV1 COV1 +// STARTED 1 456 # If FT is missing, the input crashed while processing. +// STARTED 2 567 +// FT 2 8 9 +// COV 2 11 12 +bool Merger::Parse(std::istream &IS, bool ParseCoverage) { + + LastFailure.clear(); + std::string Line; + + // Parse NumFiles. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L1(Line); + size_t NumFiles = 0; + L1 >> NumFiles; + if (NumFiles == 0 || NumFiles > 10000000) return false; + + // Parse NumFilesInFirstCorpus. + if (!std::getline(IS, Line, '\n')) return false; + std::istringstream L2(Line); + NumFilesInFirstCorpus = NumFiles + 1; + L2 >> NumFilesInFirstCorpus; + if (NumFilesInFirstCorpus > NumFiles) return false; + + // Parse file names. + Files.resize(NumFiles); + for (size_t i = 0; i < NumFiles; i++) + if (!std::getline(IS, Files[i].Name, '\n')) return false; + + // Parse STARTED, FT, and COV lines. + size_t ExpectedStartMarker = 0; + const size_t kInvalidStartMarker = -1; + size_t LastSeenStartMarker = kInvalidStartMarker; + Vector TmpFeatures; + Set PCs; + while (std::getline(IS, Line, '\n')) { + + std::istringstream ISS1(Line); + std::string Marker; + size_t N; + ISS1 >> Marker; + ISS1 >> N; + if (Marker == "STARTED") { + + // STARTED FILE_ID FILE_SIZE + if (ExpectedStartMarker != N) return false; + ISS1 >> Files[ExpectedStartMarker].Size; + LastSeenStartMarker = ExpectedStartMarker; + assert(ExpectedStartMarker < Files.size()); + ExpectedStartMarker++; + + } else if (Marker == "FT") { + + // FT FILE_ID COV1 COV2 COV3 ... + size_t CurrentFileIdx = N; + if (CurrentFileIdx != LastSeenStartMarker) return false; + LastSeenStartMarker = kInvalidStartMarker; + if (ParseCoverage) { + + TmpFeatures.clear(); // use a vector from outer scope to avoid resizes. + while (ISS1 >> N) + TmpFeatures.push_back(N); + std::sort(TmpFeatures.begin(), TmpFeatures.end()); + Files[CurrentFileIdx].Features = TmpFeatures; + + } + + } else if (Marker == "COV") { + + size_t CurrentFileIdx = N; + if (ParseCoverage) + while (ISS1 >> N) + if (PCs.insert(N).second) Files[CurrentFileIdx].Cov.push_back(N); + + } else { + + return false; + + } + + } + + if (LastSeenStartMarker != kInvalidStartMarker) + LastFailure = Files[LastSeenStartMarker].Name; + + FirstNotProcessedFile = ExpectedStartMarker; + return true; + +} + +size_t Merger::ApproximateMemoryConsumption() const { + + size_t Res = 0; + for (const auto &F : Files) + Res += sizeof(F) + F.Features.size() * sizeof(F.Features[0]); + return Res; + +} + +// Decides which files need to be merged (add those to NewFiles). +// Returns the number of new features added. +size_t Merger::Merge(const Set &InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles) { + + NewFiles->clear(); + assert(NumFilesInFirstCorpus <= Files.size()); + Set AllFeatures = InitialFeatures; + + // What features are in the initial corpus? + for (size_t i = 0; i < NumFilesInFirstCorpus; i++) { + + auto &Cur = Files[i].Features; + AllFeatures.insert(Cur.begin(), Cur.end()); + + } + + // Remove all features that we already know from all other inputs. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto & Cur = Files[i].Features; + Vector Tmp; + std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(), + AllFeatures.end(), std::inserter(Tmp, Tmp.begin())); + Cur.swap(Tmp); + + } + + // Sort. Give preference to + // * smaller files + // * files with more features. + std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(), + [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool { + + if (a.Size != b.Size) return a.Size < b.Size; + return a.Features.size() > b.Features.size(); + + }); + + // One greedy pass: add the file's features to AllFeatures. + // If new features were added, add this file to NewFiles. + for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) { + + auto &Cur = Files[i].Features; + // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(), + // Files[i].Size, Cur.size()); + bool FoundNewFeatures = false; + for (auto Fe : Cur) { + + if (AllFeatures.insert(Fe).second) { + + FoundNewFeatures = true; + NewFeatures->insert(Fe); + + } + + } + + if (FoundNewFeatures) NewFiles->push_back(Files[i].Name); + for (auto Cov : Files[i].Cov) + if (InitialCov.find(Cov) == InitialCov.end()) NewCov->insert(Cov); + + } + + return NewFeatures->size(); + +} + +Set Merger::AllFeatures() const { + + Set S; + for (auto &File : Files) + S.insert(File.Features.begin(), File.Features.end()); + return S; + +} + +// Inner process. May crash if the target crashes. +void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) { + + Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + M.ParseOrExit(IF, false); + IF.close(); + if (!M.LastFailure.empty()) + Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n", + M.LastFailure.c_str()); + + Printf( + "MERGE-INNER: %zd total files;" + " %zd processed earlier; will process %zd files now\n", + M.Files.size(), M.FirstNotProcessedFile, + M.Files.size() - M.FirstNotProcessedFile); + + std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app); + Set AllFeatures; + auto PrintStatsWrapper = [this, &AllFeatures](const char *Where) { + + this->PrintStats(Where, "\n", 0, AllFeatures.size()); + + }; + + Set AllPCs; + for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) { + + Fuzzer::MaybeExitGracefully(); + auto U = FileToVector(M.Files[i].Name); + if (U.size() > MaxInputLen) { + + U.resize(MaxInputLen); + U.shrink_to_fit(); + + } + + // Write the pre-run marker. + OF << "STARTED " << i << " " << U.size() << "\n"; + OF.flush(); // Flush is important since Command::Execute may crash. + // Run. + TPC.ResetMaps(); + ExecuteCallback(U.data(), U.size()); + // Collect coverage. We are iterating over the files in this order: + // * First, files in the initial corpus ordered by size, smallest first. + // * Then, all other files, smallest first. + // So it makes no sense to record all features for all files, instead we + // only record features that were not seen before. + Set UniqFeatures; + TPC.CollectFeatures([&](size_t Feature) { + + if (AllFeatures.insert(Feature).second) UniqFeatures.insert(Feature); + + }); + + TPC.UpdateObservedPCs(); + // Show stats. + if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1))) + PrintStatsWrapper("pulse "); + if (TotalNumberOfRuns == M.NumFilesInFirstCorpus) + PrintStatsWrapper("LOADED"); + // Write the post-run marker and the coverage. + OF << "FT " << i; + for (size_t F : UniqFeatures) + OF << " " << F; + OF << "\n"; + OF << "COV " << i; + TPC.ForEachObservedPC([&](const TracePC::PCTableEntry *TE) { + + if (AllPCs.insert(TE).second) OF << " " << TPC.PCTableEntryIdx(TE); + + }); + + OF << "\n"; + OF.flush(); + + } + + PrintStatsWrapper("DONE "); + +} + +static size_t WriteNewControlFile(const std::string & CFPath, + const Vector & OldCorpus, + const Vector & NewCorpus, + const Vector &KnownFiles) { + + std::unordered_set FilesToSkip; + for (auto &SF : KnownFiles) + FilesToSkip.insert(SF.Name); + + Vector FilesToUse; + auto MaybeUseFile = [=, &FilesToUse](std::string Name) { + + if (FilesToSkip.find(Name) == FilesToSkip.end()) FilesToUse.push_back(Name); + + }; + + for (auto &SF : OldCorpus) + MaybeUseFile(SF.File); + auto FilesToUseFromOldCorpus = FilesToUse.size(); + for (auto &SF : NewCorpus) + MaybeUseFile(SF.File); + + RemoveFile(CFPath); + std::ofstream ControlFile(CFPath); + ControlFile << FilesToUse.size() << "\n"; + ControlFile << FilesToUseFromOldCorpus << "\n"; + for (auto &FN : FilesToUse) + ControlFile << FN << "\n"; + + if (!ControlFile) { + + Printf("MERGE-OUTER: failed to write to the control file: %s\n", + CFPath.c_str()); + exit(1); + + } + + return FilesToUse.size(); + +} + +// Outer process. Does not call the target code and thus should not fail. +void CrashResistantMerge(const Vector &Args, + const Vector & OldCorpus, + const Vector & NewCorpus, + Vector * NewFiles, + const Set & InitialFeatures, + Set * NewFeatures, + const Set &InitialCov, Set *NewCov, + const std::string &CFPath, bool V /*Verbose*/) { + + if (NewCorpus.empty() && OldCorpus.empty()) return; // Nothing to merge. + size_t NumAttempts = 0; + Vector KnownFiles; + if (FileSize(CFPath)) { + + VPrintf(V, "MERGE-OUTER: non-empty control file provided: '%s'\n", + CFPath.c_str()); + Merger M; + std::ifstream IF(CFPath); + if (M.Parse(IF, /*ParseCoverage=*/true)) { + + VPrintf(V, + "MERGE-OUTER: control file ok, %zd files total," + " first not processed file %zd\n", + M.Files.size(), M.FirstNotProcessedFile); + if (!M.LastFailure.empty()) + VPrintf(V, + "MERGE-OUTER: '%s' will be skipped as unlucky " + "(merge has stumbled on it the last time)\n", + M.LastFailure.c_str()); + if (M.FirstNotProcessedFile >= M.Files.size()) { + + // Merge has already been completed with the given merge control file. + if (M.Files.size() == OldCorpus.size() + NewCorpus.size()) { + + VPrintf( + V, + "MERGE-OUTER: nothing to do, merge has been completed before\n"); + exit(0); + + } + + // Number of input files likely changed, start merge from scratch, but + // reuse coverage information from the given merge control file. + VPrintf( + V, + "MERGE-OUTER: starting merge from scratch, but reusing coverage " + "information from the given control file\n"); + KnownFiles = M.Files; + + } else { + + // There is a merge in progress, continue. + NumAttempts = M.Files.size() - M.FirstNotProcessedFile; + + } + + } else { + + VPrintf(V, "MERGE-OUTER: bad control file, will overwrite it\n"); + + } + + } + + if (!NumAttempts) { + + // The supplied control file is empty or bad, create a fresh one. + VPrintf(V, + "MERGE-OUTER: " + "%zd files, %zd in the initial corpus, %zd processed earlier\n", + OldCorpus.size() + NewCorpus.size(), OldCorpus.size(), + KnownFiles.size()); + NumAttempts = WriteNewControlFile(CFPath, OldCorpus, NewCorpus, KnownFiles); + + } + + // Execute the inner process until it passes. + // Every inner process should execute at least one input. + Command BaseCmd(Args); + BaseCmd.removeFlag("merge"); + BaseCmd.removeFlag("fork"); + BaseCmd.removeFlag("collect_data_flow"); + for (size_t Attempt = 1; Attempt <= NumAttempts; Attempt++) { + + Fuzzer::MaybeExitGracefully(); + VPrintf(V, "MERGE-OUTER: attempt %zd\n", Attempt); + Command Cmd(BaseCmd); + Cmd.addFlag("merge_control_file", CFPath); + Cmd.addFlag("merge_inner", "1"); + if (!V) { + + Cmd.setOutputFile(getDevNull()); + Cmd.combineOutAndErr(); + + } + + auto ExitCode = ExecuteCommand(Cmd); + if (!ExitCode) { + + VPrintf(V, "MERGE-OUTER: succesfull in %zd attempt(s)\n", Attempt); + break; + + } + + } + + // Read the control file and do the merge. + Merger M; + std::ifstream IF(CFPath); + IF.seekg(0, IF.end); + VPrintf(V, "MERGE-OUTER: the control file has %zd bytes\n", + (size_t)IF.tellg()); + IF.seekg(0, IF.beg); + M.ParseOrExit(IF, true); + IF.close(); + VPrintf(V, + "MERGE-OUTER: consumed %zdMb (%zdMb rss) to parse the control file\n", + M.ApproximateMemoryConsumption() >> 20, GetPeakRSSMb()); + + M.Files.insert(M.Files.end(), KnownFiles.begin(), KnownFiles.end()); + M.Merge(InitialFeatures, NewFeatures, InitialCov, NewCov, NewFiles); + VPrintf(V, + "MERGE-OUTER: %zd new files with %zd new features added; " + "%zd new coverage edges\n", + NewFiles->size(), NewFeatures->size(), NewCov->size()); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMerge.h b/custom_mutators/libfuzzer/FuzzerMerge.h new file mode 100644 index 00000000..e0c6bc53 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMerge.h @@ -0,0 +1,87 @@ +//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Merging Corpora. +// +// The task: +// Take the existing corpus (possibly empty) and merge new inputs into +// it so that only inputs with new coverage ('features') are added. +// The process should tolerate the crashes, OOMs, leaks, etc. +// +// Algorithm: +// The outer process collects the set of files and writes their names +// into a temporary "control" file, then repeatedly launches the inner +// process until all inputs are processed. +// The outer process does not actually execute the target code. +// +// The inner process reads the control file and sees a) list of all the inputs +// and b) the last processed input. Then it starts processing the inputs one +// by one. Before processing every input it writes one line to control file: +// STARTED INPUT_ID INPUT_SIZE +// After processing an input it writes the following lines: +// FT INPUT_ID Feature1 Feature2 Feature3 ... +// COV INPUT_ID Coverage1 Coverage2 Coverage3 ... +// If a crash happens while processing an input the last line in the control +// file will be "STARTED INPUT_ID" and so the next process will know +// where to resume. +// +// Once all inputs are processed by the inner process(es) the outer process +// reads the control files and does the merge based entirely on the contents +// of control file. +// It uses a single pass greedy algorithm choosing first the smallest inputs +// within the same size the inputs that have more new features. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MERGE_H +#define LLVM_FUZZER_MERGE_H + +#include "FuzzerDefs.h" + +#include +#include +#include +#include + +namespace fuzzer { + +struct MergeFileInfo { + std::string Name; + size_t Size = 0; + Vector Features, Cov; +}; + +struct Merger { + Vector Files; + size_t NumFilesInFirstCorpus = 0; + size_t FirstNotProcessedFile = 0; + std::string LastFailure; + + bool Parse(std::istream &IS, bool ParseCoverage); + bool Parse(const std::string &Str, bool ParseCoverage); + void ParseOrExit(std::istream &IS, bool ParseCoverage); + size_t Merge(const Set &InitialFeatures, Set *NewFeatures, + const Set &InitialCov, Set *NewCov, + Vector *NewFiles); + size_t ApproximateMemoryConsumption() const; + Set AllFeatures() const; +}; + +void CrashResistantMerge(const Vector &Args, + const Vector &OldCorpus, + const Vector &NewCorpus, + Vector *NewFiles, + const Set &InitialFeatures, + Set *NewFeatures, + const Set &InitialCov, + Set *NewCov, + const std::string &CFPath, + bool Verbose); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MERGE_H diff --git a/custom_mutators/libfuzzer/FuzzerMutate.cpp b/custom_mutators/libfuzzer/FuzzerMutate.cpp new file mode 100644 index 00000000..8faf6918 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.cpp @@ -0,0 +1,720 @@ +//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Mutate a test input. +//===----------------------------------------------------------------------===// + +#include "FuzzerDefs.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerMutate.h" +#include "FuzzerOptions.h" +#include "FuzzerTracePC.h" + +namespace fuzzer { + +const size_t Dictionary::kMaxDictSize; + +static void PrintASCII(const Word &W, const char *PrintAfter) { + + PrintASCII(W.data(), W.size(), PrintAfter); + +} + +MutationDispatcher::MutationDispatcher(Random & Rand, + const FuzzingOptions &Options) + : Rand(Rand), Options(Options) { + + DefaultMutators.insert( + DefaultMutators.begin(), + { + + {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"}, + {&MutationDispatcher::Mutate_InsertByte, "InsertByte"}, + {&MutationDispatcher::Mutate_InsertRepeatedBytes, + "InsertRepeatedBytes"}, + {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"}, + {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"}, + {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"}, + {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"}, + {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"}, + {&MutationDispatcher::Mutate_CopyPart, "CopyPart"}, + {&MutationDispatcher::Mutate_CrossOver, "CrossOver"}, + {&MutationDispatcher::Mutate_AddWordFromManualDictionary, + "ManualDict"}, + {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary, + "PersAutoDict"}, + + }); + + if (Options.UseCmp) + DefaultMutators.push_back( + {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"}); + + if (EF->LLVMFuzzerCustomMutator) + Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"}); + else + Mutators = DefaultMutators; + + if (EF->LLVMFuzzerCustomCrossOver) + Mutators.push_back( + {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"}); + +} + +static char RandCh(Random &Rand) { + + if (Rand.RandBool()) return Rand(256); + const char Special[] = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00"; + return Special[Rand(sizeof(Special) - 1)]; + +} + +size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand()); + +} + +size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &Other = *CrossOverWith; + if (Other.empty()) return 0; + CustomCrossOverInPlaceHere.resize(MaxSize); + auto & U = CustomCrossOverInPlaceHere; + size_t NewSize = EF->LLVMFuzzerCustomCrossOver( + Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand()); + if (!NewSize) return 0; + assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit"); + memcpy(Data, U.data(), NewSize); + return NewSize; + +} + +size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + size_t ShuffleAmount = + Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size. + size_t ShuffleStart = Rand(Size - ShuffleAmount); + assert(ShuffleStart + ShuffleAmount <= Size); + std::shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount, Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size <= 1) return 0; + size_t N = Rand(Size / 2) + 1; + assert(N < Size); + size_t Idx = Rand(Size - N + 1); + // Erase Data[Idx:Idx+N]. + memmove(Data + Idx, Data + Idx + N, Size - Idx - N); + // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx); + return Size - N; + +} + +size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size >= MaxSize) return 0; + size_t Idx = Rand(Size + 1); + // Insert new value at Data[Idx]. + memmove(Data + Idx + 1, Data + Idx, Size - Idx); + Data[Idx] = RandCh(Rand); + return Size + 1; + +} + +size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + const size_t kMinBytesToInsert = 3; + if (Size + kMinBytesToInsert >= MaxSize) return 0; + size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128); + size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert; + assert(Size + N <= MaxSize && N); + size_t Idx = Rand(Size + 1); + // Insert new values at Data[Idx]. + memmove(Data + Idx + N, Data + Idx, Size - Idx); + // Give preference to 0x00 and 0xff. + uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255); + for (size_t i = 0; i < N; i++) + Data[Idx + i] = Byte; + return Size + N; + +} + +size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] = RandCh(Rand); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t Idx = Rand(Size); + Data[Idx] ^= 1 << Rand(8); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size, + size_t MaxSize, + DictionaryEntry &DE) { + + const Word &W = DE.GetW(); + bool UsePositionHint = DE.HasPositionHint() && + DE.GetPositionHint() + W.size() < Size && + Rand.RandBool(); + if (Rand.RandBool()) { // Insert W. + if (Size + W.size() > MaxSize) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1); + memmove(Data + Idx + W.size(), Data + Idx, Size - Idx); + memcpy(Data + Idx, W.data(), W.size()); + Size += W.size(); + + } else { // Overwrite some bytes with W. + + if (W.size() > Size) return 0; + size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size()); + memcpy(Data + Idx, W.data(), W.size()); + + } + + return Size; + +} + +// Somewhere in the past we have observed a comparison instructions +// with arguments Arg1 Arg2. This function tries to guess a dictionary +// entry that will satisfy that comparison. +// It first tries to find one of the arguments (possibly swapped) in the +// input and if it succeeds it creates a DE with a position hint. +// Otherwise it creates a DE with one of the arguments w/o a position hint. +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const void *Arg1, const void *Arg2, const void *Arg1Mutation, + const void *Arg2Mutation, size_t ArgSize, const uint8_t *Data, + size_t Size) { + + bool HandleFirst = Rand.RandBool(); + const void * ExistingBytes, *DesiredBytes; + Word W; + const uint8_t *End = Data + Size; + for (int Arg = 0; Arg < 2; Arg++) { + + ExistingBytes = HandleFirst ? Arg1 : Arg2; + DesiredBytes = HandleFirst ? Arg2Mutation : Arg1Mutation; + HandleFirst = !HandleFirst; + W.Set(reinterpret_cast(DesiredBytes), ArgSize); + const size_t kMaxNumPositions = 8; + size_t Positions[kMaxNumPositions]; + size_t NumPositions = 0; + for (const uint8_t *Cur = Data; + Cur < End && NumPositions < kMaxNumPositions; Cur++) { + + Cur = + (const uint8_t *)SearchMemory(Cur, End - Cur, ExistingBytes, ArgSize); + if (!Cur) break; + Positions[NumPositions++] = Cur - Data; + + } + + if (!NumPositions) continue; + return DictionaryEntry(W, Positions[Rand(NumPositions)]); + + } + + DictionaryEntry DE(W); + return DE; + +} + +template +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + T Arg1, T Arg2, const uint8_t *Data, size_t Size) { + + if (Rand.RandBool()) Arg1 = Bswap(Arg1); + if (Rand.RandBool()) Arg2 = Bswap(Arg2); + T Arg1Mutation = Arg1 + Rand(-1, 1); + T Arg2Mutation = Arg2 + Rand(-1, 1); + return MakeDictionaryEntryFromCMP(&Arg1, &Arg2, &Arg1Mutation, &Arg2Mutation, + sizeof(Arg1), Data, Size); + +} + +DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP( + const Word &Arg1, const Word &Arg2, const uint8_t *Data, size_t Size) { + + return MakeDictionaryEntryFromCMP(Arg1.data(), Arg2.data(), Arg1.data(), + Arg2.data(), Arg1.size(), Data, Size); + +} + +size_t MutationDispatcher::Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, + size_t MaxSize) { + + Word W; + DictionaryEntry DE; + switch (Rand(4)) { + + case 0: { + + auto X = TPC.TORC8.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 1: { + + auto X = TPC.TORC4.Get(Rand.Rand()); + if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool()) + DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data, + Size); + else + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 2: { + + auto X = TPC.TORCW.Get(Rand.Rand()); + DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size); + + } break; + + case 3: + if (Options.UseMemmem) { + + auto X = TPC.MMT.Get(Rand.Rand()); + DE = DictionaryEntry(X); + + } + + break; + default: + assert(0); + + } + + if (!DE.GetW().size()) return 0; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DictionaryEntry &DERef = + CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ % + kCmpDictionaryEntriesDequeSize]; + DERef = DE; + CurrentDictionaryEntrySequence.push_back(&DERef); + return Size; + +} + +size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary( + uint8_t *Data, size_t Size, size_t MaxSize) { + + return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data, + size_t Size, size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (D.empty()) return 0; + DictionaryEntry &DE = D[Rand(D.size())]; + Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE); + if (!Size) return 0; + DE.IncUseCount(); + CurrentDictionaryEntrySequence.push_back(&DE); + return Size; + +} + +// Overwrites part of To[0,ToSize) with a part of From[0,FromSize). +// Returns ToSize. +size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize) { + + // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize). + size_t ToBeg = Rand(ToSize); + size_t CopySize = Rand(ToSize - ToBeg) + 1; + assert(ToBeg + CopySize <= ToSize); + CopySize = std::min(CopySize, FromSize); + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + memmove(To + ToBeg, From + FromBeg, CopySize); + return ToSize; + +} + +// Inserts part of From[0,ToSize) into To. +// Returns new size of To on success or 0 on failure. +size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize, + uint8_t *To, size_t ToSize, + size_t MaxToSize) { + + if (ToSize >= MaxToSize) return 0; + size_t AvailableSpace = MaxToSize - ToSize; + size_t MaxCopySize = std::min(AvailableSpace, FromSize); + size_t CopySize = Rand(MaxCopySize) + 1; + size_t FromBeg = Rand(FromSize - CopySize + 1); + assert(FromBeg + CopySize <= FromSize); + size_t ToInsertPos = Rand(ToSize + 1); + assert(ToInsertPos + CopySize <= MaxToSize); + size_t TailSize = ToSize - ToInsertPos; + if (To == From) { + + MutateInPlaceHere.resize(MaxToSize); + memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize); + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize); + + } else { + + memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize); + memmove(To + ToInsertPos, From + FromBeg, CopySize); + + } + + return ToSize + CopySize; + +} + +size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize || Size == 0) return 0; + // If Size == MaxSize, `InsertPartOf(...)` will + // fail so there's no point using it in this case. + if (Size == MaxSize || Rand.RandBool()) + return CopyPartOf(Data, Size, Data, Size); + else + return InsertPartOf(Data, Size, Data, Size, MaxSize); + +} + +size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + size_t B = Rand(Size); + while (B < Size && !isdigit(Data[B])) + B++; + if (B == Size) return 0; + size_t E = B; + while (E < Size && isdigit(Data[E])) + E++; + assert(B < E); + // now we have digits in [B, E). + // strtol and friends don't accept non-zero-teminated data, parse it manually. + uint64_t Val = Data[B] - '0'; + for (size_t i = B + 1; i < E; i++) + Val = Val * 10 + Data[i] - '0'; + + // Mutate the integer value. + switch (Rand(5)) { + + case 0: + Val++; + break; + case 1: + Val--; + break; + case 2: + Val /= 2; + break; + case 3: + Val *= 2; + break; + case 4: + Val = Rand(Val * Val); + break; + default: + assert(0); + + } + + // Just replace the bytes with the new ones, don't bother moving bytes. + for (size_t i = B; i < E; i++) { + + size_t Idx = E + B - i - 1; + assert(Idx >= B && Idx < E); + Data[Idx] = (Val % 10) + '0'; + Val /= 10; + + } + + return Size; + +} + +template +size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) { + + if (Size < sizeof(T)) return 0; + size_t Off = Rand(Size - sizeof(T) + 1); + assert(Off + sizeof(T) <= Size); + T Val; + if (Off < 64 && !Rand(4)) { + + Val = Size; + if (Rand.RandBool()) Val = Bswap(Val); + + } else { + + memcpy(&Val, Data + Off, sizeof(Val)); + T Add = Rand(21); + Add -= 10; + if (Rand.RandBool()) + Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes. + else + Val = Val + Add; // Add assuming current endiannes. + if (Add == 0 || Rand.RandBool()) // Maybe negate. + Val = -Val; + + } + + memcpy(Data + Off, &Val, sizeof(Val)); + return Size; + +} + +size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data, + size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + switch (Rand(4)) { + + case 3: + return ChangeBinaryInteger(Data, Size, Rand); + case 2: + return ChangeBinaryInteger(Data, Size, Rand); + case 1: + return ChangeBinaryInteger(Data, Size, Rand); + case 0: + return ChangeBinaryInteger(Data, Size, Rand); + default: + assert(0); + + } + + return 0; + +} + +size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size, + size_t MaxSize) { + + if (Size > MaxSize) return 0; + if (Size == 0) return 0; + if (!CrossOverWith) return 0; + const Unit &O = *CrossOverWith; + if (O.empty()) return 0; + size_t NewSize = 0; + switch (Rand(3)) { + + case 0: + MutateInPlaceHere.resize(MaxSize); + NewSize = CrossOver(Data, Size, O.data(), O.size(), + MutateInPlaceHere.data(), MaxSize); + memcpy(Data, MutateInPlaceHere.data(), NewSize); + break; + case 1: + NewSize = InsertPartOf(O.data(), O.size(), Data, Size, MaxSize); + if (!NewSize) NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + case 2: + NewSize = CopyPartOf(O.data(), O.size(), Data, Size); + break; + default: + assert(0); + + } + + assert(NewSize > 0 && "CrossOver returned empty unit"); + assert(NewSize <= MaxSize && "CrossOver returned overisized unit"); + return NewSize; + +} + +void MutationDispatcher::StartMutationSequence() { + + CurrentMutatorSequence.clear(); + CurrentDictionaryEntrySequence.clear(); + +} + +// Copy successful dictionary entries to PersistentAutoDictionary. +void MutationDispatcher::RecordSuccessfulMutationSequence() { + + for (auto DE : CurrentDictionaryEntrySequence) { + + // PersistentAutoDictionary.AddWithSuccessCountOne(DE); + DE->IncSuccessCount(); + assert(DE->GetW().size()); + // Linear search is fine here as this happens seldom. + if (!PersistentAutoDictionary.ContainsWord(DE->GetW())) + PersistentAutoDictionary.push_back({DE->GetW(), 1}); + + } + +} + +void MutationDispatcher::PrintRecommendedDictionary() { + + Vector V; + for (auto &DE : PersistentAutoDictionary) + if (!ManualDictionary.ContainsWord(DE.GetW())) V.push_back(DE); + if (V.empty()) return; + Printf("###### Recommended dictionary. ######\n"); + for (auto &DE : V) { + + assert(DE.GetW().size()); + Printf("\""); + PrintASCII(DE.GetW(), "\""); + Printf(" # Uses: %zd\n", DE.GetUseCount()); + + } + + Printf("###### End of recommended dictionary. ######\n"); + +} + +void MutationDispatcher::PrintMutationSequence() { + + Printf("MS: %zd ", CurrentMutatorSequence.size()); + for (auto M : CurrentMutatorSequence) + Printf("%s-", M.Name); + if (!CurrentDictionaryEntrySequence.empty()) { + + Printf(" DE: "); + for (auto DE : CurrentDictionaryEntrySequence) { + + Printf("\""); + PrintASCII(DE->GetW(), "\"-"); + + } + + } + +} + +std::string MutationDispatcher::MutationSequence() { + + std::string MS; + for (auto M : CurrentMutatorSequence) { + + MS += M.Name; + MS += "-"; + + } + + return MS; + +} + +size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, Mutators); + +} + +size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size, + size_t MaxSize) { + + return MutateImpl(Data, Size, MaxSize, DefaultMutators); + +} + +// Mutates Data in place, returns new size. +size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size, + size_t MaxSize, + Vector &Mutators) { + + assert(MaxSize > 0); + // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize), + // in which case they will return 0. + // Try several times before returning un-mutated data. + for (int Iter = 0; Iter < 100; Iter++) { + + auto M = Mutators[Rand(Mutators.size())]; + size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize); + if (NewSize && NewSize <= MaxSize) { + + if (Options.OnlyASCII) ToASCII(Data, NewSize); + CurrentMutatorSequence.push_back(M); + return NewSize; + + } + + } + + *Data = ' '; + return 1; // Fallback, should not happen frequently. + +} + +// Mask represents the set of Data bytes that are worth mutating. +size_t MutationDispatcher::MutateWithMask(uint8_t *Data, size_t Size, + size_t MaxSize, + const Vector &Mask) { + + size_t MaskedSize = std::min(Size, Mask.size()); + // * Copy the worthy bytes into a temporary array T + // * Mutate T + // * Copy T back. + // This is totally unoptimized. + auto &T = MutateWithMaskTemp; + if (T.size() < Size) T.resize(Size); + size_t OneBits = 0; + for (size_t I = 0; I < MaskedSize; I++) + if (Mask[I]) T[OneBits++] = Data[I]; + + if (!OneBits) return 0; + assert(!T.empty()); + size_t NewSize = Mutate(T.data(), OneBits, OneBits); + assert(NewSize <= OneBits); + (void)NewSize; + // Even if NewSize < OneBits we still use all OneBits bytes. + for (size_t I = 0, J = 0; I < MaskedSize; I++) + if (Mask[I]) Data[I] = T[J++]; + return Size; + +} + +void MutationDispatcher::AddWordToManualDictionary(const Word &W) { + + ManualDictionary.push_back({W, std::numeric_limits::max()}); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerMutate.h b/custom_mutators/libfuzzer/FuzzerMutate.h new file mode 100644 index 00000000..3ce3159f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerMutate.h @@ -0,0 +1,158 @@ +//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::MutationDispatcher +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_MUTATE_H +#define LLVM_FUZZER_MUTATE_H + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerOptions.h" +#include "FuzzerRandom.h" + +namespace fuzzer { + +class MutationDispatcher { +public: + MutationDispatcher(Random &Rand, const FuzzingOptions &Options); + ~MutationDispatcher() {} + /// Indicate that we are about to start a new sequence of mutations. + void StartMutationSequence(); + /// Print the current sequence of mutations. + void PrintMutationSequence(); + /// Return the current sequence of mutations. + std::string MutationSequence(); + /// Indicate that the current sequence of mutations was successful. + void RecordSuccessfulMutationSequence(); + /// Mutates data by invoking user-provided mutator. + size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by invoking user-provided crossover. + size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by shuffling bytes. + size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by erasing bytes. + size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting a byte. + size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by inserting several repeated bytes. + size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one byte. + size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by chanding one bit. + size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize); + /// Mutates data by copying/inserting a part of data into a different place. + size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the manual dictionary. + size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Mutates data by adding a word from the TORC. + size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Mutates data by adding a word from the persistent automatic dictionary. + size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size, + size_t MaxSize); + + /// Tries to find an ASCII integer in Data, changes it to another ASCII int. + size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize); + /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways. + size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize); + + /// CrossOver Data with CrossOverWith. + size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations. + /// Returns the new size of data which could be up to MaxSize. + size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Applies one of the configured mutations to the bytes of Data + /// that have '1' in Mask. + /// Mask.size() should be >= Size. + size_t MutateWithMask(uint8_t *Data, size_t Size, size_t MaxSize, + const Vector &Mask); + + /// Applies one of the default mutations. Provided as a service + /// to mutation authors. + size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize); + + /// Creates a cross-over of two pieces of Data, returns its size. + size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2, + size_t Size2, uint8_t *Out, size_t MaxOutSize); + + void AddWordToManualDictionary(const Word &W); + + void PrintRecommendedDictionary(); + + void SetCrossOverWith(const Unit *U) { CrossOverWith = U; } + + Random &GetRand() { return Rand; } + + private: + struct Mutator { + size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max); + const char *Name; + }; + + size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size, + size_t MaxSize); + size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize, + Vector &Mutators); + + size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize, size_t MaxToSize); + size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To, + size_t ToSize); + size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize, + DictionaryEntry &DE); + + template + DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const Word &Arg1, const Word &Arg2, + const uint8_t *Data, size_t Size); + DictionaryEntry MakeDictionaryEntryFromCMP(const void *Arg1, const void *Arg2, + const void *Arg1Mutation, + const void *Arg2Mutation, + size_t ArgSize, + const uint8_t *Data, size_t Size); + + Random &Rand; + const FuzzingOptions Options; + + // Dictionary provided by the user via -dict=DICT_FILE. + Dictionary ManualDictionary; + // Temporary dictionary modified by the fuzzer itself, + // recreated periodically. + Dictionary TempAutoDictionary; + // Persistent dictionary modified by the fuzzer, consists of + // entries that led to successful discoveries in the past mutations. + Dictionary PersistentAutoDictionary; + + Vector CurrentDictionaryEntrySequence; + + static const size_t kCmpDictionaryEntriesDequeSize = 16; + DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize]; + size_t CmpDictionaryEntriesDequeIdx = 0; + + const Unit *CrossOverWith = nullptr; + Vector MutateInPlaceHere; + Vector MutateWithMaskTemp; + // CustomCrossOver needs its own buffer as a custom implementation may call + // LLVMFuzzerMutate, which in turn may resize MutateInPlaceHere. + Vector CustomCrossOverInPlaceHere; + + Vector Mutators; + Vector DefaultMutators; + Vector CurrentMutatorSequence; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_MUTATE_H diff --git a/custom_mutators/libfuzzer/FuzzerOptions.h b/custom_mutators/libfuzzer/FuzzerOptions.h new file mode 100644 index 00000000..706e1c64 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerOptions.h @@ -0,0 +1,90 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::FuzzingOptions +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_OPTIONS_H +#define LLVM_FUZZER_OPTIONS_H + +#include "FuzzerDefs.h" + +namespace fuzzer { + +struct FuzzingOptions { + int Verbosity = 1; + size_t MaxLen = 0; + size_t LenControl = 1000; + bool KeepSeed = false; + int UnitTimeoutSec = 300; + int TimeoutExitCode = 70; + int OOMExitCode = 71; + int InterruptExitCode = 72; + int ErrorExitCode = 77; + bool IgnoreTimeouts = true; + bool IgnoreOOMs = true; + bool IgnoreCrashes = false; + int MaxTotalTimeSec = 0; + int RssLimitMb = 0; + int MallocLimitMb = 0; + bool DoCrossOver = true; + bool CrossOverUniformDist = false; + int MutateDepth = 5; + bool ReduceDepth = false; + bool UseCounters = false; + bool UseMemmem = true; + bool UseCmp = false; + int UseValueProfile = false; + bool Shrink = false; + bool ReduceInputs = false; + int ReloadIntervalSec = 1; + bool ShuffleAtStartUp = true; + bool PreferSmall = true; + size_t MaxNumberOfRuns = -1L; + int ReportSlowUnits = 10; + bool OnlyASCII = false; + bool Entropic = false; + size_t EntropicFeatureFrequencyThreshold = 0xFF; + size_t EntropicNumberOfRarestFeatures = 100; + bool EntropicScalePerExecTime = false; + std::string OutputCorpus; + std::string ArtifactPrefix = "./"; + std::string ExactArtifactPath; + std::string ExitOnSrcPos; + std::string ExitOnItem; + std::string FocusFunction; + std::string DataFlowTrace; + std::string CollectDataFlow; + std::string FeaturesDir; + std::string MutationGraphFile; + std::string StopFile; + bool SaveArtifacts = true; + bool PrintNEW = true; // Print a status line when new units are found; + bool PrintNewCovPcs = false; + int PrintNewCovFuncs = 0; + bool PrintFinalStats = false; + bool PrintCorpusStats = false; + bool PrintCoverage = false; + bool DumpCoverage = false; + bool DetectLeaks = true; + int PurgeAllocatorIntervalSec = 1; + int TraceMalloc = 0; + bool HandleAbrt = false; + bool HandleAlrm = false; + bool HandleBus = false; + bool HandleFpe = false; + bool HandleIll = false; + bool HandleInt = false; + bool HandleSegv = false; + bool HandleTerm = false; + bool HandleXfsz = false; + bool HandleUsr1 = false; + bool HandleUsr2 = false; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_OPTIONS_H diff --git a/custom_mutators/libfuzzer/FuzzerPlatform.h b/custom_mutators/libfuzzer/FuzzerPlatform.h new file mode 100644 index 00000000..8befdb88 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerPlatform.h @@ -0,0 +1,163 @@ +//===-- FuzzerPlatform.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Common platform macros. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_PLATFORM_H +#define LLVM_FUZZER_PLATFORM_H + +// Platform detection. +#ifdef __linux__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 1 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __APPLE__ +#define LIBFUZZER_APPLE 1 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __NetBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 1 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __FreeBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 1 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __OpenBSD__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 1 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif _WIN32 +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 1 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __Fuchsia__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 1 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 0 +#elif __EMSCRIPTEN__ +#define LIBFUZZER_APPLE 0 +#define LIBFUZZER_FUCHSIA 0 +#define LIBFUZZER_LINUX 0 +#define LIBFUZZER_NETBSD 0 +#define LIBFUZZER_FREEBSD 0 +#define LIBFUZZER_OPENBSD 0 +#define LIBFUZZER_WINDOWS 0 +#define LIBFUZZER_EMSCRIPTEN 1 +#else +#error "Support for your platform has not been implemented" +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +// MSVC compiler is being used. +#define LIBFUZZER_MSVC 1 +#else +#define LIBFUZZER_MSVC 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#define LIBFUZZER_POSIX \ + (LIBFUZZER_APPLE || LIBFUZZER_LINUX || LIBFUZZER_NETBSD || \ + LIBFUZZER_FREEBSD || LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) + +#ifdef __x86_64 +#if __has_attribute(target) +#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt"))) +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif +#else +#define ATTRIBUTE_TARGET_POPCNT +#endif + +#ifdef __clang__ // avoid gcc warning. +#if __has_attribute(no_sanitize) +#define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#endif +#define ALWAYS_INLINE __attribute__((always_inline)) +#else +#define ATTRIBUTE_NO_SANITIZE_MEMORY +#define ALWAYS_INLINE +#endif // __clang__ + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_NO_SANITIZE_ADDRESS +#else +#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +#endif + +#if LIBFUZZER_WINDOWS +#define ATTRIBUTE_ALIGNED(X) __declspec(align(X)) +#define ATTRIBUTE_INTERFACE __declspec(dllexport) +// This is used for __sancov_lowest_stack which is needed for +// -fsanitize-coverage=stack-depth. That feature is not yet available on +// Windows, so make the symbol static to avoid linking errors. +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC static +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define ATTRIBUTE_ALIGNED(X) __attribute__((aligned(X))) +#define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local + +#define ATTRIBUTE_NOINLINE __attribute__((noinline)) +#endif + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_ADDRESS +#elif __has_feature(memory_sanitizer) +#define ATTRIBUTE_NO_SANITIZE_ALL ATTRIBUTE_NO_SANITIZE_MEMORY +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif +#else +#define ATTRIBUTE_NO_SANITIZE_ALL +#endif + +#endif // LLVM_FUZZER_PLATFORM_H diff --git a/custom_mutators/libfuzzer/FuzzerRandom.h b/custom_mutators/libfuzzer/FuzzerRandom.h new file mode 100644 index 00000000..659283ee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerRandom.h @@ -0,0 +1,38 @@ +//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::Random +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_RANDOM_H +#define LLVM_FUZZER_RANDOM_H + +#include + +namespace fuzzer { +class Random : public std::minstd_rand { + public: + Random(unsigned int seed) : std::minstd_rand(seed) {} + result_type operator()() { return this->std::minstd_rand::operator()(); } + size_t Rand() { return this->operator()(); } + size_t RandBool() { return Rand() % 2; } + size_t SkewTowardsLast(size_t n) { + size_t T = this->operator()(n * n); + size_t Res = sqrt(T); + return Res; + } + size_t operator()(size_t n) { return n ? Rand() % n : 0; } + intptr_t operator()(intptr_t From, intptr_t To) { + assert(From < To); + intptr_t RangeSize = To - From + 1; + return operator()(RangeSize) + From; + } +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_RANDOM_H diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.cpp b/custom_mutators/libfuzzer/FuzzerSHA1.cpp new file mode 100644 index 00000000..0a58b661 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.cpp @@ -0,0 +1,269 @@ +//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This code is taken from public domain +// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c) +// and modified by adding anonymous namespace, adding an interface +// function fuzzer::ComputeSHA1() and removing unnecessary code. +// +// lib/Fuzzer can not use SHA1 implementation from openssl because +// openssl may not be available and because we may be fuzzing openssl itself. +// For the same reason we do not want to depend on SHA1 from LLVM tree. +//===----------------------------------------------------------------------===// + +#include "FuzzerSHA1.h" +#include "FuzzerDefs.h" +#include "FuzzerPlatform.h" + +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include +#include +#include + +namespace { // Added for LibFuzzer + +#ifdef __BIG_ENDIAN__ + #define SHA_BIG_ENDIAN +// Windows is always little endian and MSVC doesn't have +#elif defined __LITTLE_ENDIAN__ || LIBFUZZER_WINDOWS +/* override */ +#elif defined __BYTE_ORDER + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#else // ! defined __LITTLE_ENDIAN__ + #include // machine/endian.h + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define SHA_BIG_ENDIAN + #endif +#endif + +/* header */ + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +typedef struct sha1nfo { + + uint32_t buffer[BLOCK_LENGTH / 4]; + uint32_t state[HASH_LENGTH / 4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; + +} sha1nfo; + +/* public API - prototypes - TODO: doxygen*/ + +/** + */ +void sha1_init(sha1nfo *s); +/** + */ +void sha1_writebyte(sha1nfo *s, uint8_t data); +/** + */ +void sha1_write(sha1nfo *s, const char *data, size_t len); +/** + */ +uint8_t *sha1_result(sha1nfo *s); + +/* code */ +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +void sha1_init(sha1nfo *s) { + + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; + +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + + return ((number << bits) | (number >> (32 - bits))); + +} + +void sha1_hashBlock(sha1nfo *s) { + + uint8_t i; + uint32_t a, b, c, d, e, t; + + a = s->state[0]; + b = s->state[1]; + c = s->state[2]; + d = s->state[3]; + e = s->state[4]; + for (i = 0; i < 80; i++) { + + if (i >= 16) { + + t = s->buffer[(i + 13) & 15] ^ s->buffer[(i + 8) & 15] ^ + s->buffer[(i + 2) & 15] ^ s->buffer[i & 15]; + s->buffer[i & 15] = sha1_rol32(t, 1); + + } + + if (i < 20) { + + t = (d ^ (b & (c ^ d))) + SHA1_K0; + + } else if (i < 40) { + + t = (b ^ c ^ d) + SHA1_K20; + + } else if (i < 60) { + + t = ((b & c) | (d & (b | c))) + SHA1_K40; + + } else { + + t = (b ^ c ^ d) + SHA1_K60; + + } + + t += sha1_rol32(a, 5) + e + s->buffer[i & 15]; + e = d; + d = c; + c = sha1_rol32(b, 30); + b = a; + a = t; + + } + + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; + +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + + uint8_t *const b = (uint8_t *)s->buffer; +#ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; +#else + b[s->bufferOffset ^ 3] = data; +#endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + + sha1_hashBlock(s); + s->bufferOffset = 0; + + } + +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + + ++s->byteCount; + sha1_addUncounted(s, data); + +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + + for (; len--;) + sha1_writebyte(s, (uint8_t)*data++); + +} + +void sha1_pad(sha1nfo *s) { + + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) + sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted( + s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); + +} + +uint8_t *sha1_result(sha1nfo *s) { + + // Pad to complete the last block + sha1_pad(s); + +#ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i = 0; i < 5; i++) { + + s->state[i] = (((s->state[i]) << 24) & 0xff000000) | + (((s->state[i]) << 8) & 0x00ff0000) | + (((s->state[i]) >> 8) & 0x0000ff00) | + (((s->state[i]) >> 24) & 0x000000ff); + + } + +#endif + + // Return pointer to hash (20 characters) + return (uint8_t *)s->state; + +} + +} // namespace + +namespace fuzzer { + +// The rest is added for LibFuzzer +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) { + + sha1nfo s; + sha1_init(&s); + sha1_write(&s, (const char *)Data, Len); + memcpy(Out, sha1_result(&s), HASH_LENGTH); + +} + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) { + + std::stringstream SS; + for (int i = 0; i < kSHA1NumBytes; i++) + SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i]; + return SS.str(); + +} + +std::string Hash(const Unit &U) { + + uint8_t Hash[kSHA1NumBytes]; + ComputeSHA1(U.data(), U.size(), Hash); + return Sha1ToString(Hash); + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerSHA1.h b/custom_mutators/libfuzzer/FuzzerSHA1.h new file mode 100644 index 00000000..05cbacda --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerSHA1.h @@ -0,0 +1,32 @@ +//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// SHA1 utils. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_SHA1_H +#define LLVM_FUZZER_SHA1_H + +#include "FuzzerDefs.h" +#include +#include + +namespace fuzzer { + +// Private copy of SHA1 implementation. +static const int kSHA1NumBytes = 20; + +// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'. +void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out); + +std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]); + +std::string Hash(const Unit &U); + +} // namespace fuzzer + +#endif // LLVM_FUZZER_SHA1_H diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.cpp b/custom_mutators/libfuzzer/FuzzerTracePC.cpp new file mode 100644 index 00000000..1177325e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.cpp @@ -0,0 +1,819 @@ +//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Trace PCs. +// This module implements __sanitizer_cov_trace_pc_guard[_init], +// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation. +// +//===----------------------------------------------------------------------===// + +#include "FuzzerTracePC.h" +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCorpus.h" +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerExtFunctions.h" +#include "FuzzerIO.h" +#include "FuzzerPlatform.h" +#include "FuzzerUtil.h" +#include "FuzzerValueBitMap.h" +#include + +// Used by -fsanitize-coverage=stack-depth to track stack depth +ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack; + +namespace fuzzer { + +TracePC TPC; + +size_t TracePC::GetTotalPCCoverage() { + + return ObservedPCs.size(); + +} + +void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) { + + if (Start == Stop) return; + if (NumModules && Modules[NumModules - 1].Start() == Start) return; + assert(NumModules < sizeof(Modules) / sizeof(Modules[0])); + auto & M = Modules[NumModules++]; + uint8_t *AlignedStart = RoundUpByPage(Start); + uint8_t *AlignedStop = RoundDownByPage(Stop); + size_t NumFullPages = AlignedStop > AlignedStart + ? (AlignedStop - AlignedStart) / PageSize() + : 0; + bool NeedFirst = Start < AlignedStart || !NumFullPages; + bool NeedLast = Stop > AlignedStop && AlignedStop >= AlignedStart; + M.NumRegions = NumFullPages + NeedFirst + NeedLast; + ; + assert(M.NumRegions > 0); + M.Regions = new Module::Region[M.NumRegions]; + assert(M.Regions); + size_t R = 0; + if (NeedFirst) + M.Regions[R++] = {Start, std::min(Stop, AlignedStart), true, false}; + for (uint8_t *P = AlignedStart; P < AlignedStop; P += PageSize()) + M.Regions[R++] = {P, P + PageSize(), true, true}; + if (NeedLast) M.Regions[R++] = {AlignedStop, Stop, true, false}; + assert(R == M.NumRegions); + assert(M.Size() == (size_t)(Stop - Start)); + assert(M.Stop() == Stop); + assert(M.Start() == Start); + NumInline8bitCounters += M.Size(); + +} + +void TracePC::HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop) { + + const PCTableEntry *B = reinterpret_cast(Start); + const PCTableEntry *E = reinterpret_cast(Stop); + if (NumPCTables && ModulePCTable[NumPCTables - 1].Start == B) return; + assert(NumPCTables < sizeof(ModulePCTable) / sizeof(ModulePCTable[0])); + ModulePCTable[NumPCTables++] = {B, E}; + NumPCsInPCTables += E - B; + +} + +void TracePC::PrintModuleInfo() { + + if (NumModules) { + + Printf("INFO: Loaded %zd modules (%zd inline 8-bit counters): ", + NumModules, NumInline8bitCounters); + for (size_t i = 0; i < NumModules; i++) + Printf("%zd [%p, %p), ", Modules[i].Size(), Modules[i].Start(), + Modules[i].Stop()); + Printf("\n"); + + } + + if (NumPCTables) { + + Printf("INFO: Loaded %zd PC tables (%zd PCs): ", NumPCTables, + NumPCsInPCTables); + for (size_t i = 0; i < NumPCTables; i++) { + + Printf("%zd [%p,%p), ", ModulePCTable[i].Stop - ModulePCTable[i].Start, + ModulePCTable[i].Start, ModulePCTable[i].Stop); + + } + + Printf("\n"); + + if (NumInline8bitCounters && NumInline8bitCounters != NumPCsInPCTables) { + + Printf( + "ERROR: The size of coverage PC tables does not match the\n" + "number of instrumented PCs. This might be a compiler bug,\n" + "please contact the libFuzzer developers.\n" + "Also check https://bugs.llvm.org/show_bug.cgi?id=34636\n" + "for possible workarounds (tl;dr: don't use the old GNU ld)\n"); + _Exit(1); + + } + + } + + if (size_t NumExtraCounters = ExtraCountersEnd() - ExtraCountersBegin()) + Printf("INFO: %zd Extra Counters\n", NumExtraCounters); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) { + + const uintptr_t kBits = 12; + const uintptr_t kMask = (1 << kBits) - 1; + uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits); + ValueProfileMap.AddValueModPrime(Idx); + +} + +/// \return the address of the previous instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h` +inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) { + +#if defined(__arm__) + // T32 (Thumb) branch instructions might be 16 or 32 bit long, + // so we return (pc-2) in that case in order to be safe. + // For A32 mode we return (pc-4) because all instructions are 32 bit long. + return (PC - 3) & (~1); +#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__) + // PCs are always 4 byte aligned. + return PC - 4; +#elif defined(__sparc__) || defined(__mips__) + return PC - 8; +#else + return PC - 1; +#endif + +} + +/// \return the address of the next instruction. +/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cpp` +ALWAYS_INLINE uintptr_t TracePC::GetNextInstructionPc(uintptr_t PC) { + +#if defined(__mips__) + return PC + 8; +#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \ + defined(__aarch64__) + return PC + 4; +#else + return PC + 1; +#endif + +} + +void TracePC::UpdateObservedPCs() { + + Vector CoveredFuncs; + auto ObservePC = [&](const PCTableEntry *TE) { + + if (ObservedPCs.insert(TE).second && DoPrintNewPCs) { + + PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", + GetNextInstructionPc(TE->PC)); + Printf("\n"); + + } + + }; + + auto Observe = [&](const PCTableEntry *TE) { + + if (PcIsFuncEntry(TE)) + if (++ObservedFuncs[TE->PC] == 1 && NumPrintNewFuncs) + CoveredFuncs.push_back(TE->PC); + ObservePC(TE); + + }; + + if (NumPCsInPCTables) { + + if (NumInline8bitCounters == NumPCsInPCTables) { + + for (size_t i = 0; i < NumModules; i++) { + + auto &M = Modules[i]; + assert(M.Size() == + (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start)); + for (size_t r = 0; r < M.NumRegions; r++) { + + auto &R = M.Regions[r]; + if (!R.Enabled) continue; + for (uint8_t *P = R.Start; P < R.Stop; P++) + if (*P) Observe(&ModulePCTable[i].Start[M.Idx(P)]); + + } + + } + + } + + } + + for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N; + i++) { + + Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size()); + PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i])); + Printf("\n"); + + } + +} + +uintptr_t TracePC::PCTableEntryIdx(const PCTableEntry *TE) { + + size_t TotalTEs = 0; + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + if (TE >= M.Start && TE < M.Stop) return TotalTEs + TE - M.Start; + TotalTEs += M.Stop - M.Start; + + } + + assert(0); + return 0; + +} + +const TracePC::PCTableEntry *TracePC::PCTableEntryByIdx(uintptr_t Idx) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto & M = ModulePCTable[i]; + size_t Size = M.Stop - M.Start; + if (Idx < Size) return &M.Start[Idx]; + Idx -= Size; + + } + + return nullptr; + +} + +static std::string GetModuleName(uintptr_t PC) { + + char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++? + void *OffsetRaw = nullptr; + if (!EF->__sanitizer_get_module_and_offset_for_pc( + reinterpret_cast(PC), ModulePathRaw, sizeof(ModulePathRaw), + &OffsetRaw)) + return ""; + return ModulePathRaw; + +} + +template +void TracePC::IterateCoveredFunctions(CallBack CB) { + + for (size_t i = 0; i < NumPCTables; i++) { + + auto &M = ModulePCTable[i]; + assert(M.Start < M.Stop); + auto ModuleName = GetModuleName(M.Start->PC); + for (auto NextFE = M.Start; NextFE < M.Stop;) { + + auto FE = NextFE; + assert(PcIsFuncEntry(FE) && "Not a function entry point"); + do { + + NextFE++; + + } while (NextFE < M.Stop && !(PcIsFuncEntry(NextFE))); + + CB(FE, NextFE, ObservedFuncs[FE->PC]); + + } + + } + +} + +void TracePC::SetFocusFunction(const std::string &FuncName) { + + // This function should be called once. + assert(!FocusFunctionCounterPtr); + // "auto" is not a valid function name. If this function is called with "auto" + // that means the auto focus functionality failed. + if (FuncName.empty() || FuncName == "auto") return; + for (size_t M = 0; M < NumModules; M++) { + + auto & PCTE = ModulePCTable[M]; + size_t N = PCTE.Stop - PCTE.Start; + for (size_t I = 0; I < N; I++) { + + if (!(PcIsFuncEntry(&PCTE.Start[I]))) continue; // not a function entry. + auto Name = DescribePC("%F", GetNextInstructionPc(PCTE.Start[I].PC)); + if (Name[0] == 'i' && Name[1] == 'n' && Name[2] == ' ') + Name = Name.substr(3, std::string::npos); + if (FuncName != Name) continue; + Printf("INFO: Focus function is set to '%s'\n", Name.c_str()); + FocusFunctionCounterPtr = Modules[M].Start() + I; + return; + + } + + } + + Printf( + "ERROR: Failed to set focus function. Make sure the function name is " + "valid (%s) and symbolization is enabled.\n", + FuncName.c_str()); + exit(1); + +} + +bool TracePC::ObservedFocusFunction() { + + return FocusFunctionCounterPtr && *FocusFunctionCounterPtr; + +} + +void TracePC::PrintCoverage() { + + if (!EF->__sanitizer_symbolize_pc || + !EF->__sanitizer_get_module_and_offset_for_pc) { + + Printf( + "INFO: __sanitizer_symbolize_pc or " + "__sanitizer_get_module_and_offset_for_pc is not available," + " not printing coverage\n"); + return; + + } + + Printf("COVERAGE:\n"); + auto CoveredFunctionCallback = [&](const PCTableEntry *First, + const PCTableEntry *Last, + uintptr_t Counter) { + + assert(First < Last); + auto VisualizePC = GetNextInstructionPc(First->PC); + std::string FileStr = DescribePC("%s", VisualizePC); + if (!IsInterestingCoverageFile(FileStr)) return; + std::string FunctionStr = DescribePC("%F", VisualizePC); + if (FunctionStr.find("in ") == 0) FunctionStr = FunctionStr.substr(3); + std::string LineStr = DescribePC("%l", VisualizePC); + size_t NumEdges = Last - First; + Vector UncoveredPCs; + for (auto TE = First; TE < Last; TE++) + if (!ObservedPCs.count(TE)) UncoveredPCs.push_back(TE->PC); + Printf("%sCOVERED_FUNC: hits: %zd", Counter ? "" : "UN", Counter); + Printf(" edges: %zd/%zd", NumEdges - UncoveredPCs.size(), NumEdges); + Printf(" %s %s:%s\n", FunctionStr.c_str(), FileStr.c_str(), + LineStr.c_str()); + if (Counter) + for (auto PC : UncoveredPCs) + Printf(" UNCOVERED_PC: %s\n", + DescribePC("%s:%l", GetNextInstructionPc(PC)).c_str()); + + }; + + IterateCoveredFunctions(CoveredFunctionCallback); + +} + +// Value profile. +// We keep track of various values that affect control flow. +// These values are inserted into a bit-set-based hash map. +// Every new bit in the map is treated as a new coverage. +// +// For memcmp/strcmp/etc the interesting value is the length of the common +// prefix of the parameters. +// For cmp instructions the interesting value is a XOR of the parameters. +// The interesting value is mixed up with the PC and is then added to the map. + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero) { + + if (!n) return; + size_t Len = std::min(n, Word::GetMaxSize()); + const uint8_t *A1 = reinterpret_cast(s1); + const uint8_t *A2 = reinterpret_cast(s2); + uint8_t B1[Word::kMaxSize]; + uint8_t B2[Word::kMaxSize]; + // Copy the data into locals in this non-msan-instrumented function + // to avoid msan complaining further. + size_t Hash = 0; // Compute some simple hash of both strings. + for (size_t i = 0; i < Len; i++) { + + B1[i] = A1[i]; + B2[i] = A2[i]; + size_t T = B1[i]; + Hash ^= (T << 8) | B2[i]; + + } + + size_t I = 0; + uint8_t HammingDistance = 0; + for (; I < Len; I++) { + + if (B1[I] != B2[I] || (StopAtZero && B1[I] == 0)) { + + HammingDistance = Popcountll(B1[I] ^ B2[I]); + break; + + } + + } + + size_t PC = reinterpret_cast(caller_pc); + size_t Idx = (PC & 4095) | (I << 12); + Idx += HammingDistance; + ValueProfileMap.AddValue(Idx); + TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len)); + +} + +template +ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE ATTRIBUTE_NO_SANITIZE_ALL void +TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) { + + uint64_t ArgXor = Arg1 ^ Arg2; + if (sizeof(T) == 4) + TORC4.Insert(ArgXor, Arg1, Arg2); + else if (sizeof(T) == 8) + TORC8.Insert(ArgXor, Arg1, Arg2); + uint64_t HammingDistance = Popcountll(ArgXor); // [0,64] + uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1); + ValueProfileMap.AddValue(PC * 128 + HammingDistance); + ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance); + +} + +static size_t InternalStrnlen(const char *S, size_t MaxLen) { + + size_t Len = 0; + for (; Len < MaxLen && S[Len]; Len++) {} + return Len; + +} + +// Finds min of (strlen(S1), strlen(S2)). +// Needed bacause one of these strings may actually be non-zero terminated. +static size_t InternalStrnlen2(const char *S1, const char *S2) { + + size_t Len = 0; + for (; S1[Len] && S2[Len]; Len++) {} + return Len; + +} + +void TracePC::ClearInlineCounters() { + + IterateCounterRegions([](const Module::Region &R) { + + if (R.Enabled) memset(R.Start, 0, R.Stop - R.Start); + + }); + +} + +ATTRIBUTE_NO_SANITIZE_ALL +void TracePC::RecordInitialStack() { + + int stack; + __sancov_lowest_stack = InitialStack = reinterpret_cast(&stack); + +} + +uintptr_t TracePC::GetMaxStackOffset() const { + + return InitialStack - __sancov_lowest_stack; // Stack grows down + +} + +void WarnAboutDeprecatedInstrumentation(const char *flag) { + + // Use RawPrint because Printf cannot be used on Windows before OutputFile is + // initialized. + RawPrint(flag); + RawPrint( + " is no longer supported by libFuzzer.\n" + "Please either migrate to a compiler that supports -fsanitize=fuzzer\n" + "or use an older version of libFuzzer\n"); + exit(1); + +} + +} // namespace fuzzer + +extern "C" { + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +// Best-effort support for -fsanitize-coverage=trace-pc, which is available +// in both Clang and GCC. +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc() { + + fuzzer::WarnAboutDeprecatedInstrumentation("-fsanitize-coverage=trace-pc"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) { + + fuzzer::WarnAboutDeprecatedInstrumentation( + "-fsanitize-coverage=trace-pc-guard"); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) { + + fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop); + +} + +ATTRIBUTE_INTERFACE +void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, + const uintptr_t *pcs_end) { + + fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCallerCallee(PC, Callee); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +// Now the __sanitizer_cov_trace_const_cmp[1248] callbacks just mimic +// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however, +// should be changed later to make full use of instrumentation. +void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Arg1, Arg2); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) { + + uint64_t N = Cases[0]; + uint64_t ValSizeInBits = Cases[1]; + uint64_t *Vals = Cases + 2; + // Skip the most common and the most boring case: all switch values are small. + // We may want to skip this at compile-time, but it will make the + // instrumentation less general. + if (Vals[N - 1] < 256) return; + // Also skip small inputs values, they won't give good signal. + if (Val < 256) return; + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + size_t i; + uint64_t Smaller = 0; + uint64_t Larger = ~(uint64_t)0; + // Find two switch values such that Smaller < Val < Larger. + // Use 0 and 0xfff..f as the defaults. + for (i = 0; i < N; i++) { + + if (Val < Vals[i]) { + + Larger = Vals[i]; + break; + + } + + if (Val > Vals[i]) Smaller = Vals[i]; + + } + + // Apply HandleCmp to {Val,Smaller} and {Val, Larger}, + // use i as the PC modifier for HandleCmp. + if (ValSizeInBits == 16) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint16_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint16_t)(Larger)); + + } else if (ValSizeInBits == 32) { + + fuzzer::TPC.HandleCmp(PC + 2 * i, static_cast(Val), + (uint32_t)(Smaller)); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, static_cast(Val), + (uint32_t)(Larger)); + + } else { + + fuzzer::TPC.HandleCmp(PC + 2 * i, Val, Smaller); + fuzzer::TPC.HandleCmp(PC + 2 * i + 1, Val, Larger); + + } + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div4(uint32_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_div8(uint64_t Val) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0); + +} + +ATTRIBUTE_INTERFACE +ATTRIBUTE_NO_SANITIZE_ALL +ATTRIBUTE_TARGET_POPCNT +void __sanitizer_cov_trace_gep(uintptr_t Idx) { + + uintptr_t PC = reinterpret_cast(GET_CALLER_PC()); + fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ false); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, + size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t Len1 = fuzzer::InternalStrnlen(s1, n); + size_t Len2 = fuzzer::InternalStrnlen(s2, n); + n = std::min(n, Len1); + n = std::min(n, Len2); + if (n <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1, const char *s2, + int result) { + + if (!fuzzer::RunningUserCallback) return; + if (result == 0) return; // No reason to mutate. + size_t N = fuzzer::InternalStrnlen2(s1, s2); + if (N <= 1) return; // Not interesting. + fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, N, /*StopAtZero*/ true); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result) { + + if (!fuzzer::RunningUserCallback) return; + return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strstr(void *called_pc, const char *s1, const char *s2, + char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), strlen(s2)); + +} + +ATTRIBUTE_INTERFACE ATTRIBUTE_NO_SANITIZE_MEMORY void +__sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1, + const void *s2, size_t len2, void *result) { + + if (!fuzzer::RunningUserCallback) return; + fuzzer::TPC.MMT.Add(reinterpret_cast(s2), len2); + +} + +} // extern "C" + diff --git a/custom_mutators/libfuzzer/FuzzerTracePC.h b/custom_mutators/libfuzzer/FuzzerTracePC.h new file mode 100644 index 00000000..4601300c --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerTracePC.h @@ -0,0 +1,291 @@ +//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// fuzzer::TracePC +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_TRACE_PC +#define LLVM_FUZZER_TRACE_PC + +#include "FuzzerDefs.h" +#include "FuzzerDictionary.h" +#include "FuzzerValueBitMap.h" + +#include +#include + +namespace fuzzer { + +// TableOfRecentCompares (TORC) remembers the most recently performed +// comparisons of type T. +// We record the arguments of CMP instructions in this table unconditionally +// because it seems cheaper this way than to compute some expensive +// conditions inside __sanitizer_cov_trace_cmp*. +// After the unit has been executed we may decide to use the contents of +// this table to populate a Dictionary. +template +struct TableOfRecentCompares { + static const size_t kSize = kSizeT; + struct Pair { + T A, B; + }; + ATTRIBUTE_NO_SANITIZE_ALL + void Insert(size_t Idx, const T &Arg1, const T &Arg2) { + Idx = Idx % kSize; + Table[Idx].A = Arg1; + Table[Idx].B = Arg2; + } + + Pair Get(size_t I) { return Table[I % kSize]; } + + Pair Table[kSize]; +}; + +template +struct MemMemTable { + static const size_t kSize = kSizeT; + Word MemMemWords[kSize]; + Word EmptyWord; + + void Add(const uint8_t *Data, size_t Size) { + if (Size <= 2) return; + Size = std::min(Size, Word::GetMaxSize()); + size_t Idx = SimpleFastHash(Data, Size) % kSize; + MemMemWords[Idx].Set(Data, Size); + } + const Word &Get(size_t Idx) { + for (size_t i = 0; i < kSize; i++) { + const Word &W = MemMemWords[(Idx + i) % kSize]; + if (W.size()) return W; + } + EmptyWord.Set(nullptr, 0); + return EmptyWord; + } +}; + +class TracePC { + public: + void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop); + void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop); + void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee); + template void HandleCmp(uintptr_t PC, T Arg1, T Arg2); + size_t GetTotalPCCoverage(); + void SetUseCounters(bool UC) { UseCounters = UC; } + void SetUseValueProfileMask(uint32_t VPMask) { UseValueProfileMask = VPMask; } + void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; } + void SetPrintNewFuncs(size_t P) { NumPrintNewFuncs = P; } + void UpdateObservedPCs(); + template void CollectFeatures(Callback CB) const; + + void ResetMaps() { + ValueProfileMap.Reset(); + ClearExtraCounters(); + ClearInlineCounters(); + } + + void ClearInlineCounters(); + + void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize); + void PrintFeatureSet(); + + void PrintModuleInfo(); + + void PrintCoverage(); + + template + void IterateCoveredFunctions(CallBack CB); + + void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2, + size_t n, bool StopAtZero); + + TableOfRecentCompares TORC4; + TableOfRecentCompares TORC8; + TableOfRecentCompares TORCW; + MemMemTable<1024> MMT; + + void RecordInitialStack(); + uintptr_t GetMaxStackOffset() const; + + template + void ForEachObservedPC(CallBack CB) { + for (auto PC : ObservedPCs) + CB(PC); + } + + void SetFocusFunction(const std::string &FuncName); + bool ObservedFocusFunction(); + + struct PCTableEntry { + uintptr_t PC, PCFlags; + }; + + uintptr_t PCTableEntryIdx(const PCTableEntry *TE); + const PCTableEntry *PCTableEntryByIdx(uintptr_t Idx); + static uintptr_t GetNextInstructionPc(uintptr_t PC); + bool PcIsFuncEntry(const PCTableEntry *TE) { return TE->PCFlags & 1; } + +private: + bool UseCounters = false; + uint32_t UseValueProfileMask = false; + bool DoPrintNewPCs = false; + size_t NumPrintNewFuncs = 0; + + // Module represents the array of 8-bit counters split into regions + // such that every region, except maybe the first and the last one, is one + // full page. + struct Module { + struct Region { + uint8_t *Start, *Stop; + bool Enabled; + bool OneFullPage; + }; + Region *Regions; + size_t NumRegions; + uint8_t *Start() { return Regions[0].Start; } + uint8_t *Stop() { return Regions[NumRegions - 1].Stop; } + size_t Size() { return Stop() - Start(); } + size_t Idx(uint8_t *P) { + assert(P >= Start() && P < Stop()); + return P - Start(); + } + }; + + Module Modules[4096]; + size_t NumModules; // linker-initialized. + size_t NumInline8bitCounters; + + template + void IterateCounterRegions(Callback CB) { + for (size_t m = 0; m < NumModules; m++) + for (size_t r = 0; r < Modules[m].NumRegions; r++) + CB(Modules[m].Regions[r]); + } + + struct { const PCTableEntry *Start, *Stop; } ModulePCTable[4096]; + size_t NumPCTables; + size_t NumPCsInPCTables; + + Set ObservedPCs; + std::unordered_map ObservedFuncs; // PC => Counter. + + uint8_t *FocusFunctionCounterPtr = nullptr; + + ValueBitMap ValueProfileMap; + uintptr_t InitialStack; +}; + +template +// void Callback(size_t FirstFeature, size_t Idx, uint8_t Value); +ATTRIBUTE_NO_SANITIZE_ALL +size_t ForEachNonZeroByte(const uint8_t *Begin, const uint8_t *End, + size_t FirstFeature, Callback Handle8bitCounter) { + typedef uintptr_t LargeType; + const size_t Step = sizeof(LargeType) / sizeof(uint8_t); + const size_t StepMask = Step - 1; + auto P = Begin; + // Iterate by 1 byte until either the alignment boundary or the end. + for (; reinterpret_cast(P) & StepMask && P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + + // Iterate by Step bytes at a time. + for (; P < End; P += Step) + if (LargeType Bundle = *reinterpret_cast(P)) { + Bundle = HostToLE(Bundle); + for (size_t I = 0; I < Step; I++, Bundle >>= 8) + if (uint8_t V = Bundle & 0xff) + Handle8bitCounter(FirstFeature, P - Begin + I, V); + } + + // Iterate by 1 byte until the end. + for (; P < End; P++) + if (uint8_t V = *P) + Handle8bitCounter(FirstFeature, P - Begin, V); + return End - Begin; +} + +// Given a non-zero Counter returns a number in the range [0,7]. +template +unsigned CounterToFeature(T Counter) { + // Returns a feature number by placing Counters into buckets as illustrated + // below. + // + // Counter bucket: [1] [2] [3] [4-7] [8-15] [16-31] [32-127] [128+] + // Feature number: 0 1 2 3 4 5 6 7 + // + // This is a heuristic taken from AFL (see + // http://lcamtuf.coredump.cx/afl/technical_details.txt). + // + // This implementation may change in the future so clients should + // not rely on it. + assert(Counter); + unsigned Bit = 0; + /**/ if (Counter >= 128) Bit = 7; + else if (Counter >= 32) Bit = 6; + else if (Counter >= 16) Bit = 5; + else if (Counter >= 8) Bit = 4; + else if (Counter >= 4) Bit = 3; + else if (Counter >= 3) Bit = 2; + else if (Counter >= 2) Bit = 1; + return Bit; +} + +template // void Callback(size_t Feature) +ATTRIBUTE_NO_SANITIZE_ADDRESS +ATTRIBUTE_NOINLINE +void TracePC::CollectFeatures(Callback HandleFeature) const { + auto Handle8bitCounter = [&](size_t FirstFeature, + size_t Idx, uint8_t Counter) { + if (UseCounters) + HandleFeature(FirstFeature + Idx * 8 + CounterToFeature(Counter)); + else + HandleFeature(FirstFeature + Idx); + }; + + size_t FirstFeature = 0; + + for (size_t i = 0; i < NumModules; i++) { + for (size_t r = 0; r < Modules[i].NumRegions; r++) { + if (!Modules[i].Regions[r].Enabled) continue; + FirstFeature += 8 * ForEachNonZeroByte(Modules[i].Regions[r].Start, + Modules[i].Regions[r].Stop, + FirstFeature, Handle8bitCounter); + } + } + + FirstFeature += + 8 * ForEachNonZeroByte(ExtraCountersBegin(), ExtraCountersEnd(), + FirstFeature, Handle8bitCounter); + + if (UseValueProfileMask) { + ValueProfileMap.ForEach([&](size_t Idx) { + HandleFeature(FirstFeature + Idx); + }); + FirstFeature += ValueProfileMap.SizeInBits(); + } + + // Step function, grows similar to 8 * Log_2(A). + auto StackDepthStepFunction = [](uint32_t A) -> uint32_t { + if (!A) return A; + uint32_t Log2 = Log(A); + if (Log2 < 3) return A; + Log2 -= 3; + return (Log2 + 1) * 8 + ((A >> Log2) & 7); + }; + assert(StackDepthStepFunction(1024) == 64); + assert(StackDepthStepFunction(1024 * 4) == 80); + assert(StackDepthStepFunction(1024 * 1024) == 144); + + if (auto MaxStackOffset = GetMaxStackOffset()) + HandleFeature(FirstFeature + StackDepthStepFunction(MaxStackOffset / 8)); +} + +extern TracePC TPC; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_TRACE_PC diff --git a/custom_mutators/libfuzzer/FuzzerUtil.cpp b/custom_mutators/libfuzzer/FuzzerUtil.cpp new file mode 100644 index 00000000..7c395f7d --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.cpp @@ -0,0 +1,314 @@ +//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils. +//===----------------------------------------------------------------------===// + +#include "FuzzerUtil.h" +#include "FuzzerIO.h" +#include "FuzzerInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fuzzer { + +void PrintHexArray(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + Printf("0x%x,", (unsigned)Data[i]); + Printf("%s", PrintAfter); + +} + +void Print(const Unit &v, const char *PrintAfter) { + + PrintHexArray(v.data(), v.size(), PrintAfter); + +} + +void PrintASCIIByte(uint8_t Byte) { + + if (Byte == '\\') + Printf("\\\\"); + else if (Byte == '"') + Printf("\\\""); + else if (Byte >= 32 && Byte < 127) + Printf("%c", Byte); + else + Printf("\\x%02x", Byte); + +} + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { + + for (size_t i = 0; i < Size; i++) + PrintASCIIByte(Data[i]); + Printf("%s", PrintAfter); + +} + +void PrintASCII(const Unit &U, const char *PrintAfter) { + + PrintASCII(U.data(), U.size(), PrintAfter); + +} + +bool ToASCII(uint8_t *Data, size_t Size) { + + bool Changed = false; + for (size_t i = 0; i < Size; i++) { + + uint8_t &X = Data[i]; + auto NewX = X; + NewX &= 127; + if (!isspace(NewX) && !isprint(NewX)) NewX = ' '; + Changed |= NewX != X; + X = NewX; + + } + + return Changed; + +} + +bool IsASCII(const Unit &U) { + + return IsASCII(U.data(), U.size()); + +} + +bool IsASCII(const uint8_t *Data, size_t Size) { + + for (size_t i = 0; i < Size; i++) + if (!(isprint(Data[i]) || isspace(Data[i]))) return false; + return true; + +} + +bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { + + U->clear(); + if (Str.empty()) return false; + size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. + // Skip spaces from both sides. + while (L < R && isspace(Str[L])) + L++; + while (R > L && isspace(Str[R])) + R--; + if (R - L < 2) return false; + // Check the closing " + if (Str[R] != '"') return false; + R--; + // Find the opening " + while (L < R && Str[L] != '"') + L++; + if (L >= R) return false; + assert(Str[L] == '\"'); + L++; + assert(L <= R); + for (size_t Pos = L; Pos <= R; Pos++) { + + uint8_t V = (uint8_t)Str[Pos]; + if (!isprint(V) && !isspace(V)) return false; + if (V == '\\') { + + // Handle '\\' + if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { + + U->push_back(Str[Pos + 1]); + Pos++; + continue; + + } + + // Handle '\xAB' + if (Pos + 3 <= R && Str[Pos + 1] == 'x' && isxdigit(Str[Pos + 2]) && + isxdigit(Str[Pos + 3])) { + + char Hex[] = "0xAA"; + Hex[2] = Str[Pos + 2]; + Hex[3] = Str[Pos + 3]; + U->push_back(strtol(Hex, nullptr, 16)); + Pos += 3; + continue; + + } + + return false; // Invalid escape. + + } else { + + // Any other character. + U->push_back(V); + + } + + } + + return true; + +} + +bool ParseDictionaryFile(const std::string &Text, Vector *Units) { + + if (Text.empty()) { + + Printf("ParseDictionaryFile: file does not exist or is empty\n"); + return false; + + } + + std::istringstream ISS(Text); + Units->clear(); + Unit U; + int LineNo = 0; + std::string S; + while (std::getline(ISS, S, '\n')) { + + LineNo++; + size_t Pos = 0; + while (Pos < S.size() && isspace(S[Pos])) + Pos++; // Skip spaces. + if (Pos == S.size()) continue; // Empty line. + if (S[Pos] == '#') continue; // Comment line. + if (ParseOneDictionaryEntry(S, &U)) { + + Units->push_back(U); + + } else { + + Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo, + S.c_str()); + return false; + + } + + } + + return true; + +} + +// Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h +std::string Base64(const Unit &U) { + + static const char Table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + std::string Buffer; + Buffer.resize(((U.size() + 2) / 3) * 4); + + size_t i = 0, j = 0; + for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) | + (unsigned char)U[i + 2]; + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = Table[x & 63]; + + } + + if (i + 1 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = '='; + Buffer[j + 3] = '='; + + } else if (i + 2 == U.size()) { + + uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8); + Buffer[j + 0] = Table[(x >> 18) & 63]; + Buffer[j + 1] = Table[(x >> 12) & 63]; + Buffer[j + 2] = Table[(x >> 6) & 63]; + Buffer[j + 3] = '='; + + } + + return Buffer; + +} + +static std::mutex SymbolizeMutex; + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) + return ""; + char PcDescr[1024] = {}; + EF->__sanitizer_symbolize_pc(reinterpret_cast(PC), SymbolizedFMT, + PcDescr, sizeof(PcDescr)); + PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. + return PcDescr; + +} + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { + + if (EF->__sanitizer_symbolize_pc) + Printf("%s", DescribePC(SymbolizedFMT, PC).c_str()); + else + Printf(FallbackFMT, PC); + +} + +void PrintStackTrace() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_stack_trace && l.owns_lock()) + EF->__sanitizer_print_stack_trace(); + +} + +void PrintMemoryProfile() { + + std::unique_lock l(SymbolizeMutex, std::try_to_lock); + if (EF->__sanitizer_print_memory_profile && l.owns_lock()) + EF->__sanitizer_print_memory_profile(95, 8); + +} + +unsigned NumberOfCpuCores() { + + unsigned N = std::thread::hardware_concurrency(); + if (!N) { + + Printf( + "WARNING: std::thread::hardware_concurrency not well defined for " + "your platform. Assuming CPU count of 1.\n"); + N = 1; + + } + + return N; + +} + +size_t SimpleFastHash(const uint8_t *Data, size_t Size) { + + size_t Res = 0; + for (size_t i = 0; i < Size; i++) + Res = Res * 11 + Data[i]; + return Res; + +} + +} // namespace fuzzer + diff --git a/custom_mutators/libfuzzer/FuzzerUtil.h b/custom_mutators/libfuzzer/FuzzerUtil.h new file mode 100644 index 00000000..e90be085 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtil.h @@ -0,0 +1,117 @@ +//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Util functions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_UTIL_H +#define LLVM_FUZZER_UTIL_H + +#include "FuzzerBuiltins.h" +#include "FuzzerBuiltinsMsvc.h" +#include "FuzzerCommand.h" +#include "FuzzerDefs.h" + +namespace fuzzer { + +void PrintHexArray(const Unit &U, const char *PrintAfter = ""); + +void PrintHexArray(const uint8_t *Data, size_t Size, + const char *PrintAfter = ""); + +void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = ""); + +void PrintASCII(const Unit &U, const char *PrintAfter = ""); + +// Changes U to contain only ASCII (isprint+isspace) characters. +// Returns true iff U has been changed. +bool ToASCII(uint8_t *Data, size_t Size); + +bool IsASCII(const Unit &U); + +bool IsASCII(const uint8_t *Data, size_t Size); + +std::string Base64(const Unit &U); + +void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC); + +std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC); + +void PrintStackTrace(); + +void PrintMemoryProfile(); + +unsigned NumberOfCpuCores(); + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions& Options); + +void SleepSeconds(int Seconds); + +unsigned long GetPid(); + +size_t GetPeakRSSMb(); + +int ExecuteCommand(const Command &Cmd); +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput); + +// Fuchsia does not have popen/pclose. +FILE *OpenProcessPipe(const char *Command, const char *Mode); +int CloseProcessPipe(FILE *F); + +const void *SearchMemory(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +std::string CloneArgsWithoutX(const Vector &Args, + const char *X1, const char *X2); + +inline std::string CloneArgsWithoutX(const Vector &Args, + const char *X) { + return CloneArgsWithoutX(Args, X, X); +} + +inline std::pair SplitBefore(std::string X, + std::string S) { + auto Pos = S.find(X); + if (Pos == std::string::npos) + return std::make_pair(S, ""); + return std::make_pair(S.substr(0, Pos), S.substr(Pos)); +} + +void DiscardOutput(int Fd); + +std::string DisassembleCmd(const std::string &FileName); + +std::string SearchRegexCmd(const std::string &Regex); + +size_t SimpleFastHash(const uint8_t *Data, size_t Size); + +inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; } + +inline size_t PageSize() { return 4096; } +inline uint8_t *RoundUpByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = (X + Mask) & ~Mask; + return reinterpret_cast(X); +} +inline uint8_t *RoundDownByPage(uint8_t *P) { + uintptr_t X = reinterpret_cast(P); + size_t Mask = PageSize() - 1; + X = X & ~Mask; + return reinterpret_cast(X); +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +template T HostToLE(T X) { return X; } +#else +template T HostToLE(T X) { return Bswap(X); } +#endif + +} // namespace fuzzer + +#endif // LLVM_FUZZER_UTIL_H diff --git a/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp new file mode 100644 index 00000000..420d8c23 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilDarwin.cpp @@ -0,0 +1,205 @@ +//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Darwin. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_APPLE + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include + #include + #include + #include + #include + #include + #include + +// There is no header for this on macOS so declare here +extern "C" char **environ; + +namespace fuzzer { + +static std::mutex SignalMutex; +// Global variables used to keep track of how signal handling should be +// restored. They should **not** be accessed without holding `SignalMutex`. +static int ActiveThreadCount = 0; +static struct sigaction OldSigIntAction; +static struct sigaction OldSigQuitAction; +static sigset_t OldBlockedSignalsSet; + +// This is a reimplementation of Libc's `system()`. On Darwin the Libc +// implementation contains a mutex which prevents it from being used +// concurrently. This implementation **can** be used concurrently. It sets the +// signal handlers when the first thread enters and restores them when the last +// thread finishes execution of the function and ensures this is not racey by +// using a mutex. +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + posix_spawnattr_t SpawnAttributes; + if (posix_spawnattr_init(&SpawnAttributes)) return -1; + // Block and ignore signals of the current process when the first thread + // enters. + { + + std::lock_guard Lock(SignalMutex); + if (ActiveThreadCount == 0) { + + static struct sigaction IgnoreSignalAction; + sigset_t BlockedSignalsSet; + memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction)); + IgnoreSignalAction.sa_handler = SIG_IGN; + + if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) { + + Printf("Failed to ignore SIGINT\n"); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) { + + Printf("Failed to ignore SIGQUIT\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + (void)sigemptyset(&BlockedSignalsSet); + (void)sigaddset(&BlockedSignalsSet, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) == + -1) { + + Printf("Failed to block SIGCHLD\n"); + // Try our best to restore the signal handlers. + (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL); + (void)sigaction(SIGINT, &OldSigIntAction, NULL); + (void)posix_spawnattr_destroy(&SpawnAttributes); + return -1; + + } + + } + + ++ActiveThreadCount; + + } + + // NOTE: Do not introduce any new `return` statements past this + // point. It is important that `ActiveThreadCount` always be decremented + // when leaving this function. + + // Make sure the child process uses the default handlers for the + // following signals rather than inheriting what the parent has. + sigset_t DefaultSigSet; + (void)sigemptyset(&DefaultSigSet); + (void)sigaddset(&DefaultSigSet, SIGQUIT); + (void)sigaddset(&DefaultSigSet, SIGINT); + (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet); + // Make sure the child process doesn't block SIGCHLD + (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet); + short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags); + + pid_t Pid; + char ** Environ = environ; // Read from global + const char *CommandCStr = CmdLine.c_str(); + char *const Argv[] = {strdup("sh"), strdup("-c"), strdup(CommandCStr), NULL}; + int ErrorCode = 0, ProcessStatus = 0; + // FIXME: We probably shouldn't hardcode the shell path. + ErrorCode = + posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes, Argv, Environ); + (void)posix_spawnattr_destroy(&SpawnAttributes); + if (!ErrorCode) { + + pid_t SavedPid = Pid; + do { + + // Repeat until call completes uninterrupted. + Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0); + + } while (Pid == -1 && errno == EINTR); + + if (Pid == -1) { + + // Fail for some other reason. + ProcessStatus = -1; + + } + + } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) { + + // Fork failure. + ProcessStatus = -1; + + } else { + + // Shell execution failure. + ProcessStatus = W_EXITCODE(127, 0); + + } + + for (unsigned i = 0, n = sizeof(Argv) / sizeof(Argv[0]); i < n; ++i) + free(Argv[i]); + + // Restore the signal handlers of the current process when the last thread + // using this function finishes. + { + + std::lock_guard Lock(SignalMutex); + --ActiveThreadCount; + if (ActiveThreadCount == 0) { + + bool FailedRestore = false; + if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) { + + Printf("Failed to restore SIGINT handling\n"); + FailedRestore = true; + + } + + if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) { + + Printf("Failed to restore SIGQUIT handling\n"); + FailedRestore = true; + + } + + if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) { + + Printf("Failed to unblock SIGCHLD\n"); + FailedRestore = true; + + } + + if (FailedRestore) ProcessStatus = -1; + + } + + } + + return ProcessStatus; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_APPLE + diff --git a/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp new file mode 100644 index 00000000..45ecbca8 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilFuchsia.cpp @@ -0,0 +1,658 @@ +//===- FuzzerUtilFuchsia.cpp - Misc utils for Fuchsia. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Fuchsia/Zircon APIs. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" + +#if LIBFUZZER_FUCHSIA + + #include "FuzzerInternal.h" + #include "FuzzerUtil.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + +namespace fuzzer { + +// Given that Fuchsia doesn't have the POSIX signals that libFuzzer was written +// around, the general approach is to spin up dedicated threads to watch for +// each requested condition (alarm, interrupt, crash). Of these, the crash +// handler is the most involved, as it requires resuming the crashed thread in +// order to invoke the sanitizers to get the needed state. + +// Forward declaration of assembly trampoline needed to resume crashed threads. +// This appears to have external linkage to C++, which is why it's not in the +// anonymous namespace. The assembly definition inside MakeTrampoline() +// actually defines the symbol with internal linkage only. +void CrashTrampolineAsm() __asm__("CrashTrampolineAsm"); + +namespace { + +// Helper function to handle Zircon syscall failures. +void ExitOnErr(zx_status_t Status, const char *Syscall) { + + if (Status != ZX_OK) { + + Printf("libFuzzer: %s failed: %s\n", Syscall, + _zx_status_get_string(Status)); + exit(1); + + } + +} + +void AlarmHandler(int Seconds) { + + while (true) { + + SleepSeconds(Seconds); + Fuzzer::StaticAlarmCallback(); + + } + +} + +void InterruptHandler() { + + fd_set readfds; + // Ctrl-C sends ETX in Zircon. + do { + + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + select(STDIN_FILENO + 1, &readfds, nullptr, nullptr, nullptr); + + } while (!FD_ISSET(STDIN_FILENO, &readfds) || getchar() != 0x03); + + Fuzzer::StaticInterruptCallback(); + +} + + // CFAOffset is used to reference the stack pointer before entering the + // trampoline (Stack Pointer + CFAOffset = prev Stack Pointer). Before jumping + // to the trampoline we copy all the registers onto the stack. We need to make + // sure that the new stack has enough space to store all the registers. + // + // The trampoline holds CFI information regarding the registers stored in the + // stack, which is then used by the unwinder to restore them. + #if defined(__x86_64__) +// In x86_64 the crashing function might also be using the red zone (128 bytes +// on top of their rsp). +constexpr size_t CFAOffset = 128 + sizeof(zx_thread_state_general_regs_t); + #elif defined(__aarch64__) +// In aarch64 we need to always have the stack pointer aligned to 16 bytes, so +// we make sure that we are keeping that same alignment. +constexpr size_t CFAOffset = + (sizeof(zx_thread_state_general_regs_t) + 15) & -(uintptr_t)16; + #endif + + // For the crash handler, we need to call Fuzzer::StaticCrashSignalCallback + // without POSIX signal handlers. To achieve this, we use an assembly + // function to add the necessary CFI unwinding information and a C function to + // bridge from that back into C++. + + // FIXME: This works as a short-term solution, but this code really shouldn't + // be architecture dependent. A better long term solution is to implement + // remote unwinding and expose the necessary APIs through sanitizer_common + // and/or ASAN to allow the exception handling thread to gather the crash + // state directly. + // + // Alternatively, Fuchsia may in future actually implement basic signal + // handling for the machine trap signals. + #if defined(__x86_64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_REG(rax) \ + OP_REG(rbx) \ + OP_REG(rcx) \ + OP_REG(rdx) \ + OP_REG(rsi) \ + OP_REG(rdi) \ + OP_REG(rbp) \ + OP_REG(rsp) \ + OP_REG(r8) \ + OP_REG(r9) \ + OP_REG(r10) \ + OP_REG(r11) \ + OP_REG(r12) \ + OP_REG(r13) \ + OP_REG(r14) \ + OP_REG(r15) \ + OP_REG(rip) + + #elif defined(__aarch64__) + #define FOREACH_REGISTER(OP_REG, OP_NUM) \ + OP_NUM(0) \ + OP_NUM(1) \ + OP_NUM(2) \ + OP_NUM(3) \ + OP_NUM(4) \ + OP_NUM(5) \ + OP_NUM(6) \ + OP_NUM(7) \ + OP_NUM(8) \ + OP_NUM(9) \ + OP_NUM(10) \ + OP_NUM(11) \ + OP_NUM(12) \ + OP_NUM(13) \ + OP_NUM(14) \ + OP_NUM(15) \ + OP_NUM(16) \ + OP_NUM(17) \ + OP_NUM(18) \ + OP_NUM(19) \ + OP_NUM(20) \ + OP_NUM(21) \ + OP_NUM(22) \ + OP_NUM(23) \ + OP_NUM(24) \ + OP_NUM(25) \ + OP_NUM(26) \ + OP_NUM(27) \ + OP_NUM(28) \ + OP_NUM(29) \ + OP_REG(sp) + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Produces a CFI directive for the named or numbered register. + // The value used refers to an assembler immediate operand with the same name + // as the register (see ASM_OPERAND_REG). + #define CFI_OFFSET_REG(reg) ".cfi_offset " #reg ", %c[" #reg "]\n" + #define CFI_OFFSET_NUM(num) CFI_OFFSET_REG(x##num) + + // Produces an assembler immediate operand for the named or numbered register. + // This operand contains the offset of the register relative to the CFA. + #define ASM_OPERAND_REG(reg) \ + [reg] "i"(offsetof(zx_thread_state_general_regs_t, reg) - CFAOffset), + #define ASM_OPERAND_NUM(num) \ + [x##num] "i"(offsetof(zx_thread_state_general_regs_t, r[num]) - CFAOffset), + +// Trampoline to bridge from the assembly below to the static C++ crash +// callback. +__attribute__((noreturn)) static void StaticCrashHandler() { + + Fuzzer::StaticCrashSignalCallback(); + for (;;) { + + _Exit(1); + + } + +} + +// Creates the trampoline with the necessary CFI information to unwind through +// to the crashing call stack: +// * Defining the CFA so that it points to the stack pointer at the point +// of crash. +// * Storing all registers at the point of crash in the stack and refer to them +// via CFI information (relative to the CFA). +// * Setting the return column so the unwinder knows how to continue unwinding. +// * (x86_64) making sure rsp is aligned before calling StaticCrashHandler. +// * Calling StaticCrashHandler that will trigger the unwinder. +// +// The __attribute__((used)) is necessary because the function +// is never called; it's just a container around the assembly to allow it to +// use operands for compile-time computed constants. +__attribute__((used)) void MakeTrampoline() { + + __asm__(".cfi_endproc\n" + ".pushsection .text.CrashTrampolineAsm\n" + ".type CrashTrampolineAsm,STT_FUNC\n" +"CrashTrampolineAsm:\n" + ".cfi_startproc simple\n" + ".cfi_signal_frame\n" + #if defined(__x86_64__) + ".cfi_return_column rip\n" + ".cfi_def_cfa rsp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + "mov %%rsp, %%rbp\n" + ".cfi_def_cfa_register rbp\n" + "andq $-16, %%rsp\n" + "call %c[StaticCrashHandler]\n" + "ud2\n" + #elif defined(__aarch64__) + ".cfi_return_column 33\n" + ".cfi_def_cfa sp, %c[CFAOffset]\n" + FOREACH_REGISTER(CFI_OFFSET_REG, CFI_OFFSET_NUM) + ".cfi_offset 33, %c[pc]\n" + ".cfi_offset 30, %c[lr]\n" + "bl %c[StaticCrashHandler]\n" + "brk 1\n" + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + ".cfi_endproc\n" + ".size CrashTrampolineAsm, . - CrashTrampolineAsm\n" + ".popsection\n" + ".cfi_startproc\n" + : // No outputs + : FOREACH_REGISTER(ASM_OPERAND_REG, ASM_OPERAND_NUM) + #if defined(__aarch64__) + ASM_OPERAND_REG(pc) + ASM_OPERAND_REG(lr) + #endif + [StaticCrashHandler] "i" (StaticCrashHandler), + [CFAOffset] "i" (CFAOffset)); + +} + +void CrashHandler(zx_handle_t *Event) { + + // This structure is used to ensure we close handles to objects we create in + // this handler. + struct ScopedHandle { + + ~ScopedHandle() { + + _zx_handle_close(Handle); + + } + + zx_handle_t Handle = ZX_HANDLE_INVALID; + + }; + + // Create the exception channel. We need to claim to be a "debugger" so the + // kernel will allow us to modify and resume dying threads (see below). Once + // the channel is set, we can signal the main thread to continue and wait + // for the exception to arrive. + ScopedHandle Channel; + zx_handle_t Self = _zx_process_self(); + ExitOnErr(_zx_task_create_exception_channel( + Self, ZX_EXCEPTION_CHANNEL_DEBUGGER, &Channel.Handle), + "_zx_task_create_exception_channel"); + + ExitOnErr(_zx_object_signal(*Event, 0, ZX_USER_SIGNAL_0), + "_zx_object_signal"); + + // This thread lives as long as the process in order to keep handling + // crashes. In practice, the first crashed thread to reach the end of the + // StaticCrashHandler will end the process. + while (true) { + + ExitOnErr(_zx_object_wait_one(Channel.Handle, ZX_CHANNEL_READABLE, + ZX_TIME_INFINITE, nullptr), + "_zx_object_wait_one"); + + zx_exception_info_t ExceptionInfo; + ScopedHandle Exception; + ExitOnErr( + _zx_channel_read(Channel.Handle, 0, &ExceptionInfo, &Exception.Handle, + sizeof(ExceptionInfo), 1, nullptr, nullptr), + "_zx_channel_read"); + + // Ignore informational synthetic exceptions. + if (ZX_EXCP_THREAD_STARTING == ExceptionInfo.type || + ZX_EXCP_THREAD_EXITING == ExceptionInfo.type || + ZX_EXCP_PROCESS_STARTING == ExceptionInfo.type) { + + continue; + + } + + // At this point, we want to get the state of the crashing thread, but + // libFuzzer and the sanitizers assume this will happen from that same + // thread via a POSIX signal handler. "Resurrecting" the thread in the + // middle of the appropriate callback is as simple as forcibly setting the + // instruction pointer/program counter, provided we NEVER EVER return from + // that function (since otherwise our stack will not be valid). + ScopedHandle Thread; + ExitOnErr(_zx_exception_get_thread(Exception.Handle, &Thread.Handle), + "_zx_exception_get_thread"); + + zx_thread_state_general_regs_t GeneralRegisters; + ExitOnErr( + _zx_thread_read_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_read_state"); + + // To unwind properly, we need to push the crashing thread's register state + // onto the stack and jump into a trampoline with CFI instructions on how + // to restore it. + #if defined(__x86_64__) + uintptr_t StackPtr = GeneralRegisters.rsp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.rsp = StackPtr; + GeneralRegisters.rip = reinterpret_cast(CrashTrampolineAsm); + + #elif defined(__aarch64__) + uintptr_t StackPtr = GeneralRegisters.sp - CFAOffset; + __unsanitized_memcpy(reinterpret_cast(StackPtr), &GeneralRegisters, + sizeof(GeneralRegisters)); + GeneralRegisters.sp = StackPtr; + GeneralRegisters.pc = reinterpret_cast(CrashTrampolineAsm); + + #else + #error "Unsupported architecture for fuzzing on Fuchsia" + #endif + + // Now force the crashing thread's state. + ExitOnErr( + _zx_thread_write_state(Thread.Handle, ZX_THREAD_STATE_GENERAL_REGS, + &GeneralRegisters, sizeof(GeneralRegisters)), + "_zx_thread_write_state"); + + // Set the exception to HANDLED so it resumes the thread on close. + uint32_t ExceptionState = ZX_EXCEPTION_STATE_HANDLED; + ExitOnErr(_zx_object_set_property(Exception.Handle, ZX_PROP_EXCEPTION_STATE, + &ExceptionState, sizeof(ExceptionState)), + "zx_object_set_property"); + + } + +} + +} // namespace + +// Platform specific functions. +void SetSignalHandler(const FuzzingOptions &Options) { + + // Make sure information from libFuzzer and the sanitizers are easy to + // reassemble. `__sanitizer_log_write` has the added benefit of ensuring the + // DSO map is always available for the symbolizer. + // A uint64_t fits in 20 chars, so 64 is plenty. + char Buf[64]; + memset(Buf, 0, sizeof(Buf)); + snprintf(Buf, sizeof(Buf), "==%lu== INFO: libFuzzer starting.\n", GetPid()); + if (EF->__sanitizer_log_write) __sanitizer_log_write(Buf, sizeof(Buf)); + Printf("%s", Buf); + + // Set up alarm handler if needed. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) { + + std::thread T(AlarmHandler, Options.UnitTimeoutSec / 2 + 1); + T.detach(); + + } + + // Set up interrupt handler if needed. + if (Options.HandleInt || Options.HandleTerm) { + + std::thread T(InterruptHandler); + T.detach(); + + } + + // Early exit if no crash handler needed. + if (!Options.HandleSegv && !Options.HandleBus && !Options.HandleIll && + !Options.HandleFpe && !Options.HandleAbrt) + return; + + // Set up the crash handler and wait until it is ready before proceeding. + zx_handle_t Event; + ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create"); + + std::thread T(CrashHandler, &Event); + zx_status_t Status = + _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr); + _zx_handle_close(Event); + ExitOnErr(Status, "_zx_object_wait_one"); + + T.detach(); + +} + +void SleepSeconds(int Seconds) { + + _zx_nanosleep(_zx_deadline_after(ZX_SEC(Seconds))); + +} + +unsigned long GetPid() { + + zx_status_t rc; + zx_info_handle_basic_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return Info.koid; + +} + +size_t GetPeakRSSMb() { + + zx_status_t rc; + zx_info_task_stats_t Info; + if ((rc = _zx_object_get_info(_zx_process_self(), ZX_INFO_TASK_STATS, &Info, + sizeof(Info), NULL, NULL)) != ZX_OK) { + + Printf("libFuzzer: unable to get info about self: %s\n", + _zx_status_get_string(rc)); + exit(1); + + } + + return (Info.mem_private_bytes + Info.mem_shared_bytes) >> 20; + +} + +template +class RunOnDestruction { + + public: + explicit RunOnDestruction(Fn fn) : fn_(fn) { + + } + + ~RunOnDestruction() { + + fn_(); + + } + + private: + Fn fn_; + +}; + +template +RunOnDestruction at_scope_exit(Fn fn) { + + return RunOnDestruction(fn); + +} + +static fdio_spawn_action_t clone_fd_action(int localFd, int targetFd) { + + return { + + .action = FDIO_SPAWN_ACTION_CLONE_FD, + .fd = + { + + .local_fd = localFd, + .target_fd = targetFd, + + }, + + }; + +} + +int ExecuteCommand(const Command &Cmd) { + + zx_status_t rc; + + // Convert arguments to C array + auto Args = Cmd.getArguments(); + size_t Argc = Args.size(); + assert(Argc != 0); + std::unique_ptr Argv(new const char *[Argc + 1]); + for (size_t i = 0; i < Argc; ++i) + Argv[i] = Args[i].c_str(); + Argv[Argc] = nullptr; + + // Determine output. On Fuchsia, the fuzzer is typically run as a component + // that lacks a mutable working directory. Fortunately, when this is the case + // a mutable output directory must be specified using "-artifact_prefix=...", + // so write the log file(s) there. + // However, we don't want to apply this logic for absolute paths. + int FdOut = STDOUT_FILENO; + bool discardStdout = false; + bool discardStderr = false; + + if (Cmd.hasOutputFile()) { + + std::string Path = Cmd.getOutputFile(); + if (Path == getDevNull()) { + + // On Fuchsia, there's no "/dev/null" like-file, so we + // just don't copy the FDs into the spawned process. + discardStdout = true; + + } else { + + bool IsAbsolutePath = Path.length() > 1 && Path[0] == '/'; + if (!IsAbsolutePath && Cmd.hasFlag("artifact_prefix")) + Path = Cmd.getFlagValue("artifact_prefix") + "/" + Path; + + FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0); + if (FdOut == -1) { + + Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(), + strerror(errno)); + return ZX_ERR_IO; + + } + + } + + } + + auto CloseFdOut = at_scope_exit([FdOut]() { + + if (FdOut != STDOUT_FILENO) close(FdOut); + + }); + + // Determine stderr + int FdErr = STDERR_FILENO; + if (Cmd.isOutAndErrCombined()) { + + FdErr = FdOut; + if (discardStdout) discardStderr = true; + + } + + // Clone the file descriptors into the new process + std::vector SpawnActions; + SpawnActions.push_back(clone_fd_action(STDIN_FILENO, STDIN_FILENO)); + + if (!discardStdout) + SpawnActions.push_back(clone_fd_action(FdOut, STDOUT_FILENO)); + if (!discardStderr) + SpawnActions.push_back(clone_fd_action(FdErr, STDERR_FILENO)); + + // Start the process. + char ErrorMsg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; + zx_handle_t ProcessHandle = ZX_HANDLE_INVALID; + rc = fdio_spawn_etc(ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_ALL & (~FDIO_SPAWN_CLONE_STDIO), Argv[0], + Argv.get(), nullptr, SpawnActions.size(), + SpawnActions.data(), &ProcessHandle, ErrorMsg); + + if (rc != ZX_OK) { + + Printf("libFuzzer: failed to launch '%s': %s, %s\n", Argv[0], ErrorMsg, + _zx_status_get_string(rc)); + return rc; + + } + + auto CloseHandle = at_scope_exit([&]() { _zx_handle_close(ProcessHandle); }); + + // Now join the process and return the exit status. + if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED, + ZX_TIME_INFINITE, nullptr)) != ZX_OK) { + + Printf("libFuzzer: failed to join '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + zx_info_process_t Info; + if ((rc = _zx_object_get_info(ProcessHandle, ZX_INFO_PROCESS, &Info, + sizeof(Info), nullptr, nullptr)) != ZX_OK) { + + Printf("libFuzzer: unable to get return code from '%s': %s\n", Argv[0], + _zx_status_get_string(rc)); + return rc; + + } + + return Info.return_code; + +} + +bool ExecuteCommand(const Command &BaseCmd, std::string *CmdOutput) { + + auto LogFilePath = TempPath("SimPopenOut", ".txt"); + Command Cmd(BaseCmd); + Cmd.setOutputFile(LogFilePath); + int Ret = ExecuteCommand(Cmd); + *CmdOutput = FileToString(LogFilePath); + RemoveFile(LogFilePath); + return Ret == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +// In fuchsia, accessing /dev/null is not supported. There's nothing +// similar to a file that discards everything that is written to it. +// The way of doing something similar in fuchsia is by using +// fdio_null_create and binding that to a file descriptor. +void DiscardOutput(int Fd) { + + fdio_t *fdio_null = fdio_null_create(); + if (fdio_null == nullptr) return; + int nullfd = fdio_bind_to_fd(fdio_null, -1, 0); + if (nullfd < 0) return; + dup2(nullfd, Fd); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_FUCHSIA + diff --git a/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp new file mode 100644 index 00000000..f2531bee --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilLinux.cpp @@ -0,0 +1,43 @@ +//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils for Linux. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_LINUX || LIBFUZZER_NETBSD || LIBFUZZER_FREEBSD || \ + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN + #include "FuzzerCommand.h" + + #include + #include + #include + #include + +namespace fuzzer { + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + int exit_code = system(CmdLine.c_str()); + if (WIFEXITED(exit_code)) return WEXITSTATUS(exit_code); + return exit_code; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("/dev/null", "w"); + if (!Temp) return; + dup2(fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif + diff --git a/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp new file mode 100644 index 00000000..372bfa5e --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilPosix.cpp @@ -0,0 +1,239 @@ +//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation using Posix API. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_POSIX + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include "FuzzerTracePC.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +namespace fuzzer { + +static void AlarmHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticAlarmCallback(); + +} + +static void (*upstream_segv_handler)(int, siginfo_t *, void *); + +static void SegvHandler(int sig, siginfo_t *si, void *ucontext) { + + assert(si->si_signo == SIGSEGV); + if (upstream_segv_handler) return upstream_segv_handler(sig, si, ucontext); + Fuzzer::StaticCrashSignalCallback(); + +} + +static void CrashHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +static void InterruptHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticInterruptCallback(); + +} + +static void GracefulExitHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticGracefulExitCallback(); + +} + +static void FileSizeExceedHandler(int, siginfo_t *, void *) { + + Fuzzer::StaticFileSizeExceedCallback(); + +} + +static void SetSigaction(int signum, + void (*callback)(int, siginfo_t *, void *)) { + + struct sigaction sigact = {}; + if (sigaction(signum, nullptr, &sigact)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + + if (sigact.sa_flags & SA_SIGINFO) { + + if (sigact.sa_sigaction) { + + if (signum != SIGSEGV) return; + upstream_segv_handler = sigact.sa_sigaction; + + } + + } else { + + if (sigact.sa_handler != SIG_DFL && sigact.sa_handler != SIG_IGN && + sigact.sa_handler != SIG_ERR) + return; + + } + + sigact = {}; + sigact.sa_flags = SA_SIGINFO; + sigact.sa_sigaction = callback; + if (sigaction(signum, &sigact, 0)) { + + Printf("libFuzzer: sigaction failed with %d\n", errno); + exit(1); + + } + +} + +// Return true on success, false otherwise. +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return pclose(Pipe) == 0; + +} + +void SetTimer(int Seconds) { + + struct itimerval T { + + {Seconds, 0}, { + + Seconds, 0 + + } + + }; + + if (setitimer(ITIMER_REAL, &T, nullptr)) { + + Printf("libFuzzer: setitimer failed with %d\n", errno); + exit(1); + + } + + SetSigaction(SIGALRM, AlarmHandler); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + // setitimer is not implemented in emscripten. + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0 && !LIBFUZZER_EMSCRIPTEN) + SetTimer(Options.UnitTimeoutSec / 2 + 1); + if (Options.HandleInt) SetSigaction(SIGINT, InterruptHandler); + if (Options.HandleTerm) SetSigaction(SIGTERM, InterruptHandler); + if (Options.HandleSegv) SetSigaction(SIGSEGV, SegvHandler); + if (Options.HandleBus) SetSigaction(SIGBUS, CrashHandler); + if (Options.HandleAbrt) SetSigaction(SIGABRT, CrashHandler); + if (Options.HandleIll) SetSigaction(SIGILL, CrashHandler); + if (Options.HandleFpe) SetSigaction(SIGFPE, CrashHandler); + if (Options.HandleXfsz) SetSigaction(SIGXFSZ, FileSizeExceedHandler); + if (Options.HandleUsr1) SetSigaction(SIGUSR1, GracefulExitHandler); + if (Options.HandleUsr2) SetSigaction(SIGUSR2, GracefulExitHandler); + +} + +void SleepSeconds(int Seconds) { + + sleep(Seconds); // Use C API to avoid coverage from instrumented libc++. + +} + +unsigned long GetPid() { + + return (unsigned long)getpid(); + +} + +size_t GetPeakRSSMb() { + + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) return 0; + if (LIBFUZZER_LINUX || LIBFUZZER_FREEBSD || LIBFUZZER_NETBSD || + LIBFUZZER_OPENBSD || LIBFUZZER_EMSCRIPTEN) { + + // ru_maxrss is in KiB + return usage.ru_maxrss >> 10; + + } else if (LIBFUZZER_APPLE) { + + // ru_maxrss is in bytes + return usage.ru_maxrss >> 20; + + } + + assert(0 && "GetPeakRSSMb() is not implemented for your platform"); + return 0; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return pclose(F); + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + return memmem(Data, DataLen, Patt, PattLen); + +} + +std::string DisassembleCmd(const std::string &FileName) { + + return "objdump -d " + FileName; + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "grep '" + Regex + "'"; + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_POSIX + diff --git a/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp new file mode 100644 index 00000000..dca5630f --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerUtilWindows.cpp @@ -0,0 +1,279 @@ +//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Misc utils implementation for Windows. +//===----------------------------------------------------------------------===// +#include "FuzzerPlatform.h" +#if LIBFUZZER_WINDOWS + #include "FuzzerCommand.h" + #include "FuzzerIO.h" + #include "FuzzerInternal.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // This must be included after windows.h. + #include + +namespace fuzzer { + +static const FuzzingOptions *HandlerOpt = nullptr; + +static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { + + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { + + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_STACK_OVERFLOW: + if (HandlerOpt->HandleSegv) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_IN_PAGE_ERROR: + if (HandlerOpt->HandleBus) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + if (HandlerOpt->HandleIll) Fuzzer::StaticCrashSignalCallback(); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + if (HandlerOpt->HandleFpe) Fuzzer::StaticCrashSignalCallback(); + break; + // TODO: handle (Options.HandleXfsz) + + } + + return EXCEPTION_CONTINUE_SEARCH; + +} + +BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { + + switch (dwCtrlType) { + + case CTRL_C_EVENT: + if (HandlerOpt->HandleInt) Fuzzer::StaticInterruptCallback(); + return TRUE; + case CTRL_BREAK_EVENT: + if (HandlerOpt->HandleTerm) Fuzzer::StaticInterruptCallback(); + return TRUE; + + } + + return FALSE; + +} + +void CALLBACK AlarmHandler(PVOID, BOOLEAN) { + + Fuzzer::StaticAlarmCallback(); + +} + +class TimerQ { + + HANDLE TimerQueue; + + public: + TimerQ() : TimerQueue(NULL) { + + } + + ~TimerQ() { + + if (TimerQueue) DeleteTimerQueueEx(TimerQueue, NULL); + + } + + void SetTimer(int Seconds) { + + if (!TimerQueue) { + + TimerQueue = CreateTimerQueue(); + if (!TimerQueue) { + + Printf("libFuzzer: CreateTimerQueue failed.\n"); + exit(1); + + } + + } + + HANDLE Timer; + if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, + Seconds * 1000, Seconds * 1000, 0)) { + + Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); + exit(1); + + } + + } + +}; + +static TimerQ Timer; + +static void CrashHandler(int) { + + Fuzzer::StaticCrashSignalCallback(); + +} + +void SetSignalHandler(const FuzzingOptions &Options) { + + HandlerOpt = &Options; + + if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) + Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); + + if (Options.HandleInt || Options.HandleTerm) + if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + + DWORD LastError = GetLastError(); + Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", + LastError); + exit(1); + + } + + if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || + Options.HandleFpe) + SetUnhandledExceptionFilter(ExceptionHandler); + + if (Options.HandleAbrt) + if (SIG_ERR == signal(SIGABRT, CrashHandler)) { + + Printf("libFuzzer: signal failed with %d\n", errno); + exit(1); + + } + +} + +void SleepSeconds(int Seconds) { + + Sleep(Seconds * 1000); + +} + +unsigned long GetPid() { + + return GetCurrentProcessId(); + +} + +size_t GetPeakRSSMb() { + + PROCESS_MEMORY_COUNTERS info; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) return 0; + return info.PeakWorkingSetSize >> 20; + +} + +FILE *OpenProcessPipe(const char *Command, const char *Mode) { + + return _popen(Command, Mode); + +} + +int CloseProcessPipe(FILE *F) { + + return _pclose(F); + +} + +int ExecuteCommand(const Command &Cmd) { + + std::string CmdLine = Cmd.toString(); + return system(CmdLine.c_str()); + +} + +bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { + + FILE *Pipe = _popen(Cmd.toString().c_str(), "r"); + if (!Pipe) return false; + + if (CmdOutput) { + + char TmpBuffer[128]; + while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) + CmdOutput->append(TmpBuffer); + + } + + return _pclose(Pipe) == 0; + +} + +const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, + size_t PattLen) { + + // TODO: make this implementation more efficient. + const char *Cdata = (const char *)Data; + const char *Cpatt = (const char *)Patt; + + if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) + return NULL; + + if (PattLen == 1) return memchr(Data, *Cpatt, DataLen); + + const char *End = Cdata + DataLen - PattLen + 1; + + for (const char *It = Cdata; It < End; ++It) + if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) return It; + + return NULL; + +} + +std::string DisassembleCmd(const std::string &FileName) { + + Vector command_vector; + command_vector.push_back("dumpbin /summary > nul"); + if (ExecuteCommand(Command(command_vector)) == 0) + return "dumpbin /disasm " + FileName; + Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); + exit(1); + +} + +std::string SearchRegexCmd(const std::string &Regex) { + + return "findstr /r \"" + Regex + "\""; + +} + +void DiscardOutput(int Fd) { + + FILE *Temp = fopen("nul", "w"); + if (!Temp) return; + _dup2(_fileno(Temp), Fd); + fclose(Temp); + +} + +} // namespace fuzzer + +#endif // LIBFUZZER_WINDOWS + diff --git a/custom_mutators/libfuzzer/FuzzerValueBitMap.h b/custom_mutators/libfuzzer/FuzzerValueBitMap.h new file mode 100644 index 00000000..ddbfe200 --- /dev/null +++ b/custom_mutators/libfuzzer/FuzzerValueBitMap.h @@ -0,0 +1,73 @@ +//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// ValueBitMap. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H +#define LLVM_FUZZER_VALUE_BIT_MAP_H + +#include "FuzzerPlatform.h" +#include + +namespace fuzzer { + +// A bit map containing kMapSizeInWords bits. +struct ValueBitMap { + static const size_t kMapSizeInBits = 1 << 16; + static const size_t kMapPrimeMod = 65371; // Largest Prime < kMapSizeInBits; + static const size_t kBitsInWord = (sizeof(uintptr_t) * 8); + static const size_t kMapSizeInWords = kMapSizeInBits / kBitsInWord; + public: + + // Clears all bits. + void Reset() { memset(Map, 0, sizeof(Map)); } + + // Computes a hash function of Value and sets the corresponding bit. + // Returns true if the bit was changed from 0 to 1. + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValue(uintptr_t Value) { + uintptr_t Idx = Value % kMapSizeInBits; + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + uintptr_t Old = Map[WordIdx]; + uintptr_t New = Old | (1ULL << BitIdx); + Map[WordIdx] = New; + return New != Old; + } + + ATTRIBUTE_NO_SANITIZE_ALL + inline bool AddValueModPrime(uintptr_t Value) { + return AddValue(Value % kMapPrimeMod); + } + + inline bool Get(uintptr_t Idx) { + assert(Idx < kMapSizeInBits); + uintptr_t WordIdx = Idx / kBitsInWord; + uintptr_t BitIdx = Idx % kBitsInWord; + return Map[WordIdx] & (1ULL << BitIdx); + } + + size_t SizeInBits() const { return kMapSizeInBits; } + + template + ATTRIBUTE_NO_SANITIZE_ALL + void ForEach(Callback CB) const { + for (size_t i = 0; i < kMapSizeInWords; i++) + if (uintptr_t M = Map[i]) + for (size_t j = 0; j < sizeof(M) * 8; j++) + if (M & ((uintptr_t)1 << j)) + CB(i * sizeof(M) * 8 + j); + } + + private: + ATTRIBUTE_ALIGNED(512) uintptr_t Map[kMapSizeInWords]; +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_VALUE_BIT_MAP_H diff --git a/custom_mutators/libfuzzer/Makefile b/custom_mutators/libfuzzer/Makefile new file mode 100644 index 00000000..f0c80392 --- /dev/null +++ b/custom_mutators/libfuzzer/Makefile @@ -0,0 +1,81 @@ + +#CFLAGS = -O3 -funroll-loops -fPIC -fpermissive -std=c++11 +CFLAGS = -g -O0 -fPIC -fpermissive -std=c++11 +CC := clang++ + +all: libfuzzer-mutator.so + +FuzzerCrossOver.o: FuzzerCrossOver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDataFlowTrace.o: FuzzerDataFlowTrace.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerDriver.o: FuzzerDriver.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsDlsym.o: FuzzerExtFunctionsDlsym.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWeak.o: FuzzerExtFunctionsWeak.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtFunctionsWindows.o: FuzzerExtFunctionsWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerExtraCounters.o: FuzzerExtraCounters.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerFork.o: FuzzerFork.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIO.o: FuzzerIO.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOPosix.o: FuzzerIOPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerIOWindows.o: FuzzerIOWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerLoop.o: FuzzerLoop.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMerge.o: FuzzerMerge.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerMutate.o: FuzzerMutate.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerSHA1.o: FuzzerSHA1.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerTracePC.o: FuzzerTracePC.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtil.o: FuzzerUtil.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilDarwin.o: FuzzerUtilDarwin.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilFuchsia.o: FuzzerUtilFuchsia.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilLinux.o: FuzzerUtilLinux.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilPosix.o: FuzzerUtilPosix.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +FuzzerUtilWindows.o: FuzzerUtilWindows.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer.o: libfuzzer.cpp + $(CC) $(CFLAGS) -I../../include -I. -c $^ + +libfuzzer-mutator.so: FuzzerCrossOver.o FuzzerDataFlowTrace.o FuzzerDriver.o FuzzerExtFunctionsDlsym.o FuzzerExtFunctionsWeak.o FuzzerExtFunctionsWindows.o FuzzerExtraCounters.o FuzzerFork.o FuzzerIO.o FuzzerIOPosix.o FuzzerIOWindows.o FuzzerLoop.o FuzzerMerge.o FuzzerMutate.o FuzzerSHA1.o FuzzerTracePC.o FuzzerUtil.o FuzzerUtilDarwin.o FuzzerUtilFuchsia.o FuzzerUtilLinux.o FuzzerUtilPosix.o FuzzerUtilWindows.o libfuzzer.o + $(CC) $(CFLAGS) -I../../include -I. -shared -o libfuzzer-mutator.so *.o + +clean: + rm -f *.o *~ *.so core diff --git a/custom_mutators/libfuzzer/README.md b/custom_mutators/libfuzzer/README.md new file mode 100644 index 00000000..a773da02 --- /dev/null +++ b/custom_mutators/libfuzzer/README.md @@ -0,0 +1,24 @@ +# custum mutator: libfuzzer LLVMFuzzerMutate() + +This uses the libfuzzer LLVMFuzzerMutate() function in llvm 12. + +just type `make` to build + +```AFL_CUSTOM_MUTATOR_LIBRARY=custom_mutators/libfuzzer/libfuzzer-mutator.so afl-fuzz ...``` + +Note that is is currently simple and is missing two features: + * Splicing ("Crossover") + * Dictionary support + +To update the source, all that is needed is that FuzzerDriver.cpp has to receive +``` +#include "libfuzzer.inc" +``` +before the closing namespace bracket. + +It is also libfuzzer.inc where the configuration of the libfuzzer mutations +are done. + +> Original repository: https://github.com/llvm/llvm-project +> Path: compiler-rt/lib/fuzzer/*.{h|cpp} +> Source commit: d4b88ac1658d681e143482336cac27c6a74b8b24 diff --git a/custom_mutators/libfuzzer/libfuzzer.cpp b/custom_mutators/libfuzzer/libfuzzer.cpp new file mode 100644 index 00000000..cf41af2d --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +//#include "config.h" +//#include "debug.h" +#include "afl-fuzz.h" + +afl_state_t *afl_struct; + +extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv, + int (*UserCb)(const uint8_t *Data, + size_t Size)); +extern "C" void LLVMFuzzerMyInit(int (*UserCb)(const uint8_t *Data, + size_t Size), + unsigned int Seed); + +typedef struct my_mutator { + + afl_state_t *afl; + u8 * mutator_buf; + unsigned int seed; + unsigned int extras_cnt, a_extras_cnt; + +} my_mutator_t; + +extern "C" int dummy(const uint8_t *Data, size_t Size) { + + (void)(Data); + (void)(Size); + fprintf(stderr, "dummy() called\n"); + return 0; + +} + +extern "C" my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { + + my_mutator_t *data = (my_mutator_t *)calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + if ((data->mutator_buf = (u8 *)malloc(MAX_FILE)) == NULL) { + + perror("mutator_buf alloc"); + return NULL; + + } + + data->afl = afl; + data->seed = seed; + afl_struct = afl; + + /* + char **argv; + argv = (char**)malloc(sizeof(size_t) * 2); + argv[0] = (char*)"foo"; + argv[1] = NULL; + int eins = 1; + LLVMFuzzerRunDriver(&eins, &argv, dummy); + */ + + LLVMFuzzerMyInit(dummy, seed); + + return data; + +} + +/* When a new queue entry is added we check if there are new dictionary + entries to add to honggfuzz structure */ +#if ß +extern "C" void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + while (data->extras_cnt < afl_struct->extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->extras[data->extras_cnt].data, + afl_struct->extras[data->extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->extras[data->extras_cnt].len; + run.global->mutate.dictionaryCnt++; + */ + data->extras_cnt++; + + } + + while (data->a_extras_cnt < afl_struct->a_extras_cnt) { + + /* + memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val, + afl_struct->a_extras[data->a_extras_cnt].data, + afl_struct->a_extras[data->a_extras_cnt].len); + run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len = + afl_struct->a_extras[data->a_extras_cnt].len; + run.global->mutate.dictionaryCnt++; + data->a_extras_cnt++; + */ + + } + +} + +#endif +/* we could set only_printable if is_ascii is set ... let's see +uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) { + + //run.global->cfg.only_printable = ... + +} + +*/ + +/* here we run the honggfuzz mutator, which is really good */ + +extern "C" size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, + size_t buf_size, u8 **out_buf, + uint8_t *add_buf, size_t add_buf_size, + size_t max_size) { + + memcpy(data->mutator_buf, buf, buf_size); + size_t ret = LLVMFuzzerMutate(data->mutator_buf, buf_size, max_size); + + /* return size of mutated data */ + *out_buf = data->mutator_buf; + return ret; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +extern "C" void afl_custom_deinit(my_mutator_t *data) { + + free(data->mutator_buf); + free(data); + +} + diff --git a/custom_mutators/libfuzzer/libfuzzer.inc b/custom_mutators/libfuzzer/libfuzzer.inc new file mode 100644 index 00000000..01f21dbe --- /dev/null +++ b/custom_mutators/libfuzzer/libfuzzer.inc @@ -0,0 +1,36 @@ + + +extern "C" ATTRIBUTE_INTERFACE void +LLVMFuzzerMyInit(int (*Callback)(const uint8_t *Data, size_t Size), unsigned int Seed) { + Random Rand(Seed); + FuzzingOptions Options; + Options.Verbosity = 3; + Options.MaxLen = 1024000; + Options.LenControl = true; + Options.DoCrossOver = false; + Options.MutateDepth = 6; + Options.UseCounters = false; + Options.UseMemmem = false; + Options.UseCmp = false; + Options.UseValueProfile = false; + Options.Shrink = false; + Options.ReduceInputs = false; + Options.PreferSmall = false; + Options.ReloadIntervalSec = 0; + Options.OnlyASCII = false; + Options.DetectLeaks = false; + Options.PurgeAllocatorIntervalSec = 0; + Options.TraceMalloc = false; + Options.RssLimitMb = 100; + Options.MallocLimitMb = 100; + Options.MaxNumberOfRuns = 0; + Options.ReportSlowUnits = false; + Options.Entropic = false; + + struct EntropicOptions Entropic; + Entropic.Enabled = Options.Entropic; + EF = new ExternalFunctions(); + auto *MD = new MutationDispatcher(Rand, Options); + auto *Corpus = new InputCorpus(Options.OutputCorpus, Entropic); + auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); +} diff --git a/custom_mutators/symcc/symcc.c b/custom_mutators/symcc/symcc.c index d0572f4e..6f14052f 100644 --- a/custom_mutators/symcc/symcc.c +++ b/custom_mutators/symcc/symcc.c @@ -12,7 +12,8 @@ afl_state_t *afl_struct; #ifdef DEBUG #define DBG(x...) fprintf(stderr, x) #else - #define DBG(x...) {} + #define DBG(x...) \ + {} #endif typedef struct my_mutator { @@ -177,8 +178,8 @@ size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, size_t max_size) { struct dirent **nl; - int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); - size_t size = 0; + int32_t i, done = 0, items = scandir(data->out_dir, &nl, NULL, NULL); + size_t size = 0; if (items <= 0) return 0; diff --git a/docs/Changelog.md b/docs/Changelog.md index 1e5c1b95..af52b955 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -33,6 +33,8 @@ sending a mail to . - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/include/afl-prealloc.h b/include/afl-prealloc.h index edf69a67..fa6c9b70 100644 --- a/include/afl-prealloc.h +++ b/include/afl-prealloc.h @@ -60,7 +60,7 @@ typedef enum prealloc_status { \ if ((prealloc_counter) >= (prealloc_size)) { \ \ - el_ptr = (void *)malloc(sizeof(*el_ptr)); \ + el_ptr = (element_t *)malloc(sizeof(*el_ptr)); \ if (!el_ptr) { FATAL("error in list.h -> out of memory for element!"); } \ el_ptr->pre_status = PRE_STATUS_MALLOC; \ \ diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 90701d18..36e47810 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -668,7 +668,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { if (likely(*buf)) { /* the size is always stored at buf - 1*size_t */ - new_buf = afl_alloc_bufptr(*buf); + new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); current_size = new_buf->complete_size; } @@ -694,7 +694,7 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } /* alloc */ - new_buf = realloc(new_buf, next_size); + new_buf = (struct afl_alloc_buf *)realloc(new_buf, next_size); if (unlikely(!new_buf)) { *buf = NULL; diff --git a/include/list.h b/include/list.h index 88cbe062..7ec81cbe 100644 --- a/include/list.h +++ b/include/list.h @@ -81,6 +81,7 @@ static inline void list_append(list_t *list, void *el) { } element_t *el_box = NULL; + PRE_ALLOC(el_box, list->element_prealloc_buf, LIST_PREALLOC_SIZE, list->element_prealloc_count); if (!el_box) { FATAL("failed to allocate list element"); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 6bee8b38..a00b240d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1508,9 +1508,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < argc; i++) - SAYF(" \"%s\"", argv[i]); + SAYF(" '%s'", argv[i]); SAYF("\n"); } @@ -1536,9 +1536,9 @@ int main(int argc, char **argv, char **envp) { if (debug) { - SAYF(cMGN "[D]" cRST " cd \"%s\";", getthecwd()); + SAYF(cMGN "[D]" cRST " cd '%s';", getthecwd()); for (i = 0; i < cc_par_cnt; i++) - SAYF(" \"%s\"", cc_params[i]); + SAYF(" '%s'", cc_params[i]); SAYF("\n"); } -- cgit 1.4.1 From 639372b6441cf961ff91f6427201e1156b8511e3 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Sep 2020 02:11:10 +0200 Subject: code-format, and no code-format for custom mutators --- GNUmakefile | 4 ++-- docs/Changelog.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index d47f8247..9e8b1a47 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -465,8 +465,8 @@ code-format: ./.custom-format.py -i instrumentation/*.h ./.custom-format.py -i instrumentation/*.cc ./.custom-format.py -i instrumentation/*.c - ./.custom-format.py -i custom_mutators/*/*.c* - @#./.custom-format.py -i custom_mutators/*/*.h # destroys input.h :-( + @#./.custom-format.py -i custom_mutators/*/*.c* # destroys libfuzzer :-( + @#./.custom-format.py -i custom_mutators/*/*.h # destroys honggfuzz :-( ./.custom-format.py -i examples/*/*.c* ./.custom-format.py -i examples/*/*.h ./.custom-format.py -i test/*.c diff --git a/docs/Changelog.md b/docs/Changelog.md index af52b955..88d43e62 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,7 @@ sending a mail to . -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp + - fix crash in dict2file for integers > 64 bit - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ - added a new custom mutator: libfuzzer that integrates libfuzzer mutations - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ -- cgit 1.4.1 From 60ef1f730551eab66cdfecf4e9815cd841582561 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 23 Sep 2020 02:28:19 +0200 Subject: Update Changelog.md --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 88d43e62..789b1f74 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -33,6 +33,7 @@ sending a mail to . - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - fix crash in dict2file for integers > 64 bit + - unicornafl synced with upstream (arm64 fix, better rust bindings) - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ - added a new custom mutator: libfuzzer that integrates libfuzzer mutations - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ -- cgit 1.4.1 From 383cd487a2c28012c80341f8517e473120af4d19 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 29 Sep 2020 15:02:57 +0200 Subject: small improvements to Marcel's patch, fix laf-intel + redqueen crashes --- docs/Changelog.md | 1 + include/afl-fuzz.h | 5 +-- instrumentation/afl-llvm-dict2file.so.cc | 14 ++++++--- instrumentation/cmplog-instructions-pass.cc | 6 ++-- instrumentation/cmplog-routines-pass.cc | 11 ++++--- instrumentation/compare-transform-pass.so.cc | 24 ++++++++------ instrumentation/split-compares-pass.so.cc | 47 +++++++++++++++++----------- instrumentation/split-switches-pass.so.cc | 21 +++++++------ src/afl-fuzz-bitmap.c | 13 +++++--- src/afl-fuzz-init.c | 4 +-- src/afl-fuzz-queue.c | 30 +++++++++--------- src/afl-fuzz.c | 2 +- 12 files changed, 104 insertions(+), 74 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 789b1f74..0f923423 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,7 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz + - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index f65fc40f..fb661ce5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -151,7 +151,8 @@ struct queue_entry { is_ascii; /* Is the input just ascii text? */ u32 bitmap_size, /* Number of bits set in bitmap */ - fuzz_level; /* Number of fuzzing iterations */ + fuzz_level, /* Number of fuzzing iterations */ + n_fuzz_entry; /* offset in n_fuzz */ u64 exec_us, /* Execution time (us) */ handicap, /* Number of queue cycles behind */ @@ -491,7 +492,7 @@ typedef struct afl_state { u8 *var_bytes; /* Bytes that appear to be variable */ - #define n_fuzz_size (1 << 21) +#define N_FUZZ_SIZE (1 << 21) u32 *n_fuzz; volatile u8 stop_soon, /* Ctrl-C pressed? */ diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index e04ebda8..bd8eb27a 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -381,8 +381,9 @@ bool AFLdict2filePass::runOnModule(Module &M) { if (debug) fprintf(stderr, "F:%s %p(%s)->\"%s\"(%s) %p(%s)->\"%s\"(%s)\n", - FuncName.c_str(), (void*)Str1P, Str1P->getName().str().c_str(), - Str1.c_str(), HasStr1 == true ? "true" : "false", (void*)Str2P, + FuncName.c_str(), (void *)Str1P, + Str1P->getName().str().c_str(), Str1.c_str(), + HasStr1 == true ? "true" : "false", (void *)Str2P, Str2P->getName().str().c_str(), Str2.c_str(), HasStr2 == true ? "true" : "false"); @@ -436,7 +437,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { valueMap[Str1P] = new std::string(Str2); if (debug) - fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), (void*)Str1P); + fprintf(stderr, "Saved: %s for %p\n", Str2.c_str(), + (void *)Str1P); continue; } @@ -455,7 +457,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { Str2 = *strng; HasStr2 = true; if (debug) - fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), (void*)Str2P); + fprintf(stderr, "Filled2: %s for %p\n", strng->c_str(), + (void *)Str2P); } @@ -497,7 +500,8 @@ bool AFLdict2filePass::runOnModule(Module &M) { Str1 = *strng; HasStr1 = true; if (debug) - fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), (void*)Str1P); + fprintf(stderr, "Filled1: %s for %p\n", strng->c_str(), + (void *)Str1P); } diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index d5de3dbb..9921de0c 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -210,7 +210,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } if (!icomps.size()) return false; - if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp instructions\n"; + // if (!be_quiet) errs() << "Hooking " << icomps.size() << " cmp + // instructions\n"; for (auto &selectcmpInst : icomps) { @@ -259,8 +260,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { bool CmpLogInstructions::runOnModule(Module &M) { if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"; + printf("Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; hookInstrs(M); diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc index c44f38c4..e92883ae 100644 --- a/instrumentation/cmplog-routines-pass.cc +++ b/instrumentation/cmplog-routines-pass.cc @@ -149,9 +149,11 @@ bool CmpLogRoutines::hookRtns(Module &M) { } if (!calls.size()) return false; - if (!be_quiet) - errs() << "Hooking " << calls.size() - << " calls with pointers as arguments\n"; + /* + if (!be_quiet) + errs() << "Hooking " << calls.size() + << " calls with pointers as arguments\n"; + */ for (auto &callInst : calls) { @@ -179,8 +181,7 @@ bool CmpLogRoutines::hookRtns(Module &M) { bool CmpLogRoutines::runOnModule(Module &M) { if (getenv("AFL_QUIET") == NULL) - llvm::errs() - << "Running cmplog-routines-pass by andreafioraldi@gmail.com\n"; + printf("Running cmplog-routines-pass by andreafioraldi@gmail.com\n"); else be_quiet = 1; hookRtns(M); diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 9d2f4a92..3a4abd6e 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -339,8 +339,9 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, if (!calls.size()) return false; if (!be_quiet) - errs() << "Replacing " << calls.size() - << " calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n"; + printf( + "Replacing %lu calls to strcmp/memcmp/strncmp/strcasecmp/strncasecmp\n", + calls.size()); for (auto &callInst : calls) { @@ -426,11 +427,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, else unrollLen = constStrLen; - if (!be_quiet) - errs() << callInst->getCalledFunction()->getName() << ": unroll len " - << unrollLen - << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": " - << ConstStr << "\n"; + /* + if (!be_quiet) + errs() << callInst->getCalledFunction()->getName() << ": unroll len " + << unrollLen + << ((isSizedcmp && !isConstSized) ? ", variable n" : "") << ": + " + << ConstStr << "\n"; + */ /* split before the call instruction */ BasicBlock *bb = callInst->getParent(); @@ -556,10 +560,12 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool CompareTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running compare-transform-pass by laf.intel@gmail.com, " - "extended by heiko@hexco.de\n"; + printf( + "Running compare-transform-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de\n"); else be_quiet = 1; + transformCmps(M, true, true, true, true, true); verifyModule(M); diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 2fb90e5e..6d0c52a4 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -1262,8 +1262,9 @@ bool SplitComparesTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) { - errs() << "Split-compare-pass by laf.intel@gmail.com, extended by " - "heiko@hexco.de\n"; + printf( + "Split-compare-pass by laf.intel@gmail.com, extended by " + "heiko@hexco.de\n"); } else { @@ -1275,13 +1276,15 @@ bool SplitComparesTransform::runOnModule(Module &M) { count = splitFPCompares(M); - if (!be_quiet) { + /* + if (!be_quiet) { - errs() << "Split-floatingpoint-compare-pass: " << count - << " FP comparisons split\n"; + errs() << "Split-floatingpoint-compare-pass: " << count + << " FP comparisons split\n"; - } + } + */ simplifyFPCompares(M); } @@ -1294,10 +1297,12 @@ bool SplitComparesTransform::runOnModule(Module &M) { case 64: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1305,10 +1310,12 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 32: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 7) @@ -1316,15 +1323,17 @@ bool SplitComparesTransform::runOnModule(Module &M) { #endif case 16: count = splitIntCompares(M, bitw); - if (!be_quiet) - errs() << "Split-integer-compare-pass " << bitw << "bit: " << count - << " split\n"; - + /* + if (!be_quiet) + errs() << "Split-integer-compare-pass " << bitw << "bit: " << + count + << " split\n"; + */ bitw >>= 1; break; default: - if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; + // if (!be_quiet) errs() << "NOT Running split-compare-pass \n"; return false; break; diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc index a79d4114..97ab04a4 100644 --- a/instrumentation/split-switches-pass.so.cc +++ b/instrumentation/split-switches-pass.so.cc @@ -327,10 +327,11 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { } if (!switches.size()) return false; - if (!be_quiet) - errs() << "Rewriting " << switches.size() << " switch statements " - << "\n"; - + /* + if (!be_quiet) + errs() << "Rewriting " << switches.size() << " switch statements " + << "\n"; + */ for (auto &SI : switches) { BasicBlock *CurBlock = SI->getParent(); @@ -341,15 +342,17 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { BasicBlock *Default = SI->getDefaultDest(); unsigned bitw = Val->getType()->getIntegerBitWidth(); - if (!be_quiet) - errs() << "switch: " << SI->getNumCases() << " cases " << bitw - << " bit\n"; + /* + if (!be_quiet) + errs() << "switch: " << SI->getNumCases() << " cases " << bitw + << " bit\n"; + */ /* If there is only the default destination or the condition checks 8 bit or * less, don't bother with the code below. */ if (!SI->getNumCases() || bitw <= 8) { - if (!be_quiet) errs() << "skip trivial switch..\n"; + // if (!be_quiet) errs() << "skip trivial switch..\n"; continue; } @@ -415,7 +418,7 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { bool SplitSwitchesTransform::runOnModule(Module &M) { if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) - llvm::errs() << "Running split-switches-pass by laf.intel@gmail.com\n"; + printf("Running split-switches-pass by laf.intel@gmail.com\n"); else be_quiet = 1; splitSwitches(M); diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 64de86a2..a22223b9 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -556,8 +556,8 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); /* Saturated increment */ - if (afl->n_fuzz[cksum % n_fuzz_size] < 0xFFFFFFFF) - afl->n_fuzz[cksum % n_fuzz_size]++; + if (afl->n_fuzz[cksum % N_FUZZ_SIZE] < 0xFFFFFFFF) + afl->n_fuzz[cksum % N_FUZZ_SIZE]++; } @@ -597,10 +597,15 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (cksum) afl->queue_top->exec_cksum = cksum; else - afl->queue_top->exec_cksum = + cksum = afl->queue_top->exec_cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - afl->n_fuzz[cksum % n_fuzz_size] = 1; + if (afl->schedule >= FAST && afl->schedule <= RARE) { + + afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + + } /* Try to calibrate inline; this also calls update_bitmap_score() when successful. */ diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index b825837f..65478a78 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -732,8 +732,8 @@ void read_testcases(afl_state_t *afl, u8 *directory) { if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - - afl->n_fuzz[cksum % n_fuzz_size] = 1; + afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; } diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index dfabba7b..0d7d0314 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -308,9 +308,9 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule < RARE)) - fuzz_p2 = 0; // Skip the fuzz_p2 comparison + fuzz_p2 = 0; // Skip the fuzz_p2 comparison else if (unlikely(afl->schedule == RARE)) - fuzz_p2 = next_pow2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); + fuzz_p2 = next_pow2(afl->n_fuzz[q->n_fuzz_entry]); else fuzz_p2 = q->fuzz_level; @@ -336,7 +336,8 @@ void update_bitmap_score(afl_state_t *afl, struct queue_entry *q) { u64 top_rated_fav_factor; u64 top_rated_fuzz_p2; if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) - top_rated_fuzz_p2 = next_pow2(afl->n_fuzz[afl->top_rated[i]->exec_cksum % n_fuzz_size]); + top_rated_fuzz_p2 = + next_pow2(afl->n_fuzz[afl->top_rated[i]->n_fuzz_entry]); else top_rated_fuzz_p2 = afl->top_rated[i]->fuzz_level; @@ -607,11 +608,10 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - u32 n_paths; - double factor = 1.0; + u32 n_paths; + double factor = 1.0; long double fuzz_mu; - switch (afl->schedule) { case EXPLORE: @@ -634,7 +634,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { struct queue_entry *queue_it = afl->queue; while (queue_it) { - fuzz_mu += log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]); + fuzz_mu += log2(afl->n_fuzz[q->n_fuzz_entry]); n_paths++; queue_it = queue_it->next; @@ -645,7 +645,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { fuzz_mu = fuzz_mu / n_paths; - if (log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size]) > fuzz_mu) { + if (log2(afl->n_fuzz[q->n_fuzz_entry]) > fuzz_mu) { /* Never skip favourites */ if (!q->favored) factor = 0; @@ -660,7 +660,7 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { // Don't modify unfuzzed seeds if (q->fuzz_level == 0) break; - switch ((u32)log2(afl->n_fuzz[q->exec_cksum % n_fuzz_size])) { + switch ((u32)log2(afl->n_fuzz[q->n_fuzz_entry])) { case 0 ... 1: factor = 4; @@ -691,17 +691,17 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { } - if (q->favored) - factor *= 1.15; + if (q->favored) factor *= 1.15; break; case LIN: - factor = q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); + factor = q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case QUAD: - factor = q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->exec_cksum % n_fuzz_size] + 1); + factor = + q->fuzz_level * q->fuzz_level / (afl->n_fuzz[q->n_fuzz_entry] + 1); break; case MMOPT: @@ -726,8 +726,8 @@ u32 calculate_score(afl_state_t *afl, struct queue_entry *q) { perf_score += (q->tc_ref * 10); // the more often fuzz result paths are equal to this queue entry, // reduce its value - perf_score *= - (1 - (double)((double)afl->n_fuzz[q->exec_cksum % n_fuzz_size] / (double)afl->fsrv.total_execs)); + perf_score *= (1 - (double)((double)afl->n_fuzz[q->n_fuzz_entry] / + (double)afl->fsrv.total_execs)); break; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 889f753d..273d1c14 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -939,7 +939,7 @@ int main(int argc, char **argv_orig, char **envp) { /* Dynamically allocate memory for AFLFast schedules */ if (afl->schedule >= FAST && afl->schedule <= RARE) { - afl->n_fuzz = ck_alloc(n_fuzz_size * sizeof(u32)); + afl->n_fuzz = ck_alloc(N_FUZZ_SIZE * sizeof(u32)); } -- cgit 1.4.1 From 125f8b6ba71fba91735374b1bd07333b19aae635 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 9 Oct 2020 23:23:44 +0200 Subject: -m none is the default now --- docs/Changelog.md | 1 + examples/persistent_demo/persistent_demo_new.c | 3 ++- include/config.h | 24 ++++++++---------------- src/afl-fuzz-state.c | 2 +- src/afl-fuzz.c | 4 ++-- 5 files changed, 14 insertions(+), 20 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 0f923423..ba7028df 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,7 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz + - memory limits are now disabled by default, set them with -m if required - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options diff --git a/examples/persistent_demo/persistent_demo_new.c b/examples/persistent_demo/persistent_demo_new.c index 13123d33..b8b4cda0 100644 --- a/examples/persistent_demo/persistent_demo_new.c +++ b/examples/persistent_demo/persistent_demo_new.c @@ -37,7 +37,8 @@ unsigned char fuzz_buf[1024000]; #define __AFL_FUZZ_TESTCASE_LEN fuzz_len #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) + #define __AFL_LOOP(x) \ + ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) #define __AFL_INIT() sync() #endif diff --git a/include/config.h b/include/config.h index 3f5c5448..5df604e7 100644 --- a/include/config.h +++ b/include/config.h @@ -66,25 +66,17 @@ #define WORD_SIZE_64 1 #endif -/* Default memory limit for child process (MB): */ - -#ifndef __NetBSD__ - #ifndef WORD_SIZE_64 - #define MEM_LIMIT 50 - #else - #define MEM_LIMIT 75 - #endif /* ^!WORD_SIZE_64 */ -#else /* NetBSD's kernel needs more space for stack, see discussion for issue \ - #165 */ - #define MEM_LIMIT 250 -#endif -/* Default memory limit when running in QEMU mode (MB): */ +/* Default memory limit for child process (MB) 0 = disabled : */ + +#define MEM_LIMIT 0 + +/* Default memory limit when running in QEMU mode (MB) 0 = disabled : */ -#define MEM_LIMIT_QEMU 250 +#define MEM_LIMIT_QEMU 0 -/* Default memory limit when running in Unicorn mode (MB): */ +/* Default memory limit when running in Unicorn mode (MB) 0 = disabled : */ -#define MEM_LIMIT_UNICORN 250 +#define MEM_LIMIT_UNICORN 0 /* Number of calibration cycles per every new test case (and for test cases that show variable behavior): */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 4a1e739f..a8e56e60 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = COE; /* Power schedule (default: COE) */ + afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8458b50f..cf0a30c9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -90,13 +90,13 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" " -t msec - timeout for each run (auto-scaled, 50-%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" + " -m megs - memory limit for child process (%d MB, 0 = no limit)\n" " -Q - use binary-only instrumentation (QEMU mode)\n" " -U - use unicorn-based instrumentation (Unicorn mode)\n" " -W - use qemu-based instrumentation with Wine (Wine " -- cgit 1.4.1 From 5dc3bc175b664f0921ebd1265d4419d611aa4a74 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 10 Oct 2020 10:41:30 +0200 Subject: fix typo --- docs/Changelog.md | 1 + src/afl-fuzz-stats.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ba7028df..aa55fbde 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,7 @@ sending a mail to . - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - memory limits are now disabled by default, set them with -m if required + - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 0cd6f399..76f24977 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -429,7 +429,7 @@ void show_stats(afl_state_t *afl) { /* reset counter, even if send failed. */ afl->statsd_last_send_ms = cur_ms; - if (statsd_send_metric(afl)) { WARNF("coundln't send statsd metric."); } + if (statsd_send_metric(afl)) { WARNF("could not send statsd metric."); } } -- cgit 1.4.1 From 445aba9221471eebd7ffc2c35b97accd00b40557 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 10 Oct 2020 10:55:56 +0200 Subject: determinstic fuzzing is now disabled by default --- README.md | 8 ++++++-- docs/Changelog.md | 2 ++ docs/life_pro_tips.md | 3 --- docs/status_screen.md | 8 +------- src/afl-fuzz-state.c | 2 ++ src/afl-fuzz.c | 7 +++---- 6 files changed, 14 insertions(+), 16 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index f63b0c1e..819da093 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,13 @@ behaviours: worth it. * When instrumenting targets, afl-cc will not supersede optimizations. This allows to fuzz targets as same as they are built for debug or release. - * afl-fuzz' `-i` option now descends into subdirectories. + * afl-fuzz': + * `-i` option now descends into subdirectories. + * -m none is now default, set memory limits (in MB) with e.g. -m 250 + * deterministic fuzzing is now disabled by default (unless using -M) and + can be enabled with -D * afl-fuzz will skip over empty dictionaries and too-large test cases instead - of failing. + of failing, and use them as a source for splicing mutations ## Contents diff --git a/docs/Changelog.md b/docs/Changelog.md index aa55fbde..9eb47e18 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,8 @@ sending a mail to . - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - memory limits are now disabled by default, set them with -m if required + - deterministic fuzzing is now disabled by default and can be enabled with + -D. It is still enabled by default for -M. - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index 0004c297..323f16f1 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -85,6 +85,3 @@ You can find a simple solution in examples/argv_fuzzing. Remove the checksum-checking code or use a postprocessor! See examples/custom_mutators/ for more. -## Dealing with a very slow target or hoping for instant results? - -Specify `-d` when calling afl-fuzz! diff --git a/docs/status_screen.md b/docs/status_screen.md index 2eeb8f3f..f7655bf4 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -86,10 +86,7 @@ Every fuzzing session should be allowed to complete at least one cycle; and ideally, should run much longer than that. As noted earlier, the first pass can take a day or longer, so sit back and -relax. If you want to get broader but more shallow coverage right away, try -the `-d` option - it gives you a more familiar experience by skipping the -deterministic fuzzing steps. It is, however, inferior to the standard mode in -a couple of subtle ways. +relax. To help make the call on when to hit `Ctrl-C`, the cycle counter is color-coded. It is shown in magenta during the first pass, progresses to yellow if new finds @@ -118,9 +115,6 @@ inputs it decided to ditch because they were persistently timing out. The "*" suffix sometimes shown in the first line means that the currently processed path is not "favored" (a property discussed later on). -If you feel that the fuzzer is progressing too slowly, see the note about the -`-d` option in this doc. - ### Map coverage ``` diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index b7d44dbf..a0a2795e 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -101,6 +101,8 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->hang_tmout = EXEC_TIMEOUT; afl->stats_update_freq = 1; afl->stats_avg_exec = -1; + afl->skip_deterministic = 1; + afl->use_splicing = 1; #ifdef HAVE_AFFINITY afl->cpu_aff = -1; /* Selected CPU core */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index dc0eb4a7..24df2997 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,6 +103,7 @@ static void usage(u8 *argv0, int more_help) { "mode)\n\n" "Mutator settings:\n" + " -D - enable deterministic fuzzing (once per queue entry)\n" " -L minutes - use MOpt(imize) mode and set the time limit for " "entering the\n" " pacemaker mode (minutes of no new paths). 0 = " @@ -116,7 +117,6 @@ static void usage(u8 *argv0, int more_help) { "Fuzzing behavior settings:\n" " -N - do not unlink the fuzzing input file (for devices " "etc.)\n" - " -d - quick & dirty mode (skips deterministic steps)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" " -x dict_file - fuzzer dictionary (see README.md, specify up to 4 " "times)\n\n" @@ -136,6 +136,7 @@ static void usage(u8 *argv0, int more_help) { " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" + " -d - skip deterministic fuzzing in -M mode\n" " -T text - text banner to show on the screen\n" " -I command - execute this command/script when a new crash is " "found\n" @@ -403,6 +404,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); + afl->skip_deterministic = 0; if ((c = strchr(afl->sync_id, ':'))) { @@ -431,8 +433,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; - afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'F': /* foreign sync dir */ @@ -557,7 +557,6 @@ int main(int argc, char **argv_orig, char **envp) { case 'd': /* skip deterministic */ afl->skip_deterministic = 1; - afl->use_splicing = 1; break; case 'B': /* load bitmap */ -- cgit 1.4.1 From 6a397d6111a21ebbf736237609c1c69d47c40f03 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 11 Oct 2020 14:31:31 +0200 Subject: add new seed selection algo and make it the default --- docs/Changelog.md | 4 ++ include/afl-fuzz.h | 14 +++++- src/afl-fuzz-init.c | 8 ++++ src/afl-fuzz-one.c | 10 ++++- src/afl-fuzz-queue.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/afl-fuzz.c | 100 +++++++++++++++++++++++++++++------------ 6 files changed, 227 insertions(+), 33 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 9eb47e18..f15f1d93 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,10 @@ sending a mail to . - memory limits are now disabled by default, set them with -m if required - deterministic fuzzing is now disabled by default and can be enabled with -D. It is still enabled by default for -M. + - a new seed selection was implemented that uses weighted randoms based on + a schedule performance score, which is much better that the previous + walk the whole queue approach. Select the old mode with -Z (auto enabled + with -M) - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - reading testcases from -i now descends into subdirectories diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e9d148e9..45de197d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -151,7 +151,8 @@ struct queue_entry { favored, /* Currently favored? */ fs_redundant, /* Marked as redundant in the fs? */ fully_colorized, /* Do not run redqueen stage again */ - is_ascii; /* Is the input just ascii text? */ + is_ascii, /* Is the input just ascii text? */ + disabled; /* Is disabled from fuzz selection */ u32 bitmap_size, /* Number of bits set in bitmap */ fuzz_level, /* Number of fuzzing iterations */ @@ -165,6 +166,8 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ + double perf_score; /* performance score */ + struct queue_entry *next; /* Next element, if any */ }; @@ -488,12 +491,17 @@ typedef struct afl_state { disable_trim, /* Never trim in fuzz_one */ shmem_testcase_mode, /* If sharedmem testcases are used */ expand_havoc, /* perform expensive havoc after no find */ - cycle_schedules; /* cycle power schedules? */ + cycle_schedules, /* cycle power schedules? */ + old_seed_selection; /* use vanilla afl seed selection */ u8 *virgin_bits, /* Regions yet untouched by fuzzing */ *virgin_tmout, /* Bits we haven't seen in tmouts */ *virgin_crash; /* Bits we haven't seen in crashes */ + double *alias_probability; /* alias weighted probabilities */ + u32 * alias_table; /* alias weighted random lookup table */ + u32 active_paths; /* enabled entries in the queue */ + u8 *var_bytes; /* Bytes that appear to be variable */ #define N_FUZZ_SIZE (1 << 21) @@ -1009,6 +1017,8 @@ void find_timeout(afl_state_t *); double get_runnable_processes(void); void nuke_resume_dir(afl_state_t *); int check_main_node_exists(afl_state_t *); +u32 select_next_queue_entry(afl_state_t *afl); +void create_alias_table(afl_state_t *afl); void setup_dirs_fds(afl_state_t *); void setup_cmdline_file(afl_state_t *, char **); void setup_stdio_file(afl_state_t *); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 65478a78..881bf10f 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -959,6 +959,8 @@ void perform_dry_run(afl_state_t *afl) { /* Remove from fuzzing queue but keep for splicing */ struct queue_entry *p = afl->queue; + p->disabled = 1; + p->perf_score = 0; while (p && p->next != q) p = p->next; @@ -968,6 +970,7 @@ void perform_dry_run(afl_state_t *afl) { afl->queue = q->next; --afl->pending_not_fuzzed; + --afl->active_paths; afl->max_depth = 0; p = afl->queue; @@ -1054,6 +1057,7 @@ restart_outer_cull_loop: duplicates = 1; --afl->pending_not_fuzzed; + afl->active_paths--; // We do not remove any of the memory allocated because for // splicing the data might still be interesting. @@ -1063,11 +1067,15 @@ restart_outer_cull_loop: // we keep the shorter file if (p->len >= q->len) { + p->disabled = 1; + p->perf_score = 0; q->next = p->next; goto restart_inner_cull_loop; } else { + q->disabled = 1; + q->perf_score = 0; if (prev) prev->next = q = p; else diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index c04b492b..6ef728e0 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -554,7 +554,10 @@ u8 fuzz_one_original(afl_state_t *afl) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + orig_perf = perf_score = calculate_score(afl, afl->queue_cur); if (unlikely(perf_score == 0)) { goto abandon_entry; } @@ -2769,7 +2772,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * PERFORMANCE SCORE * *********************/ - orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (likely(!afl->old_seed_selection)) + orig_perf = perf_score = afl->queue_cur->perf_score; + else + orig_perf = perf_score = calculate_score(afl, afl->queue_cur); if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 0d7d0314..d608e890 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -27,6 +27,129 @@ #include #include +inline u32 select_next_queue_entry(afl_state_t *afl) { + + u32 r = rand_below(afl, 0xffffffff); + u32 s = r % afl->queued_paths; + // fprintf(stderr, "select: r=%u s=%u ... r < prob[s]=%f ? s=%u : + // alias[%u]=%u\n", r, s, afl->alias_probability[s], s, s, + // afl->alias_table[s]); + return (r < afl->alias_probability[s] ? s : afl->alias_table[s]); + +} + +void create_alias_table(afl_state_t *afl) { + + u32 n = afl->queued_paths, i = 0, a, g; + + afl->alias_table = + (u32 *)afl_realloc((void **)&afl->alias_table, n * sizeof(u32)); + afl->alias_probability = (double *)afl_realloc( + (void **)&afl->alias_probability, n * sizeof(double)); + double *P = (double *)afl_realloc(AFL_BUF_PARAM(out), n * sizeof(double)); + int * S = (u32 *)afl_realloc(AFL_BUF_PARAM(out_scratch), n * sizeof(u32)); + int * L = (u32 *)afl_realloc(AFL_BUF_PARAM(in_scratch), n * sizeof(u32)); + + if (!P || !S || !L) FATAL("could not aquire memory for alias table"); + memset((void *)afl->alias_table, 0, n * sizeof(u32)); + memset((void *)afl->alias_probability, 0, n * sizeof(double)); + + double sum = 0; + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + if (!q->disabled) q->perf_score = calculate_score(afl, q); + + sum += q->perf_score; + /* + if (afl->debug) + fprintf(stderr, "entry %u: score=%f %s (sum: %f)\n", i, q->perf_score, + q->disabled ? "disabled" : "", sum); + */ + + } + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + P[i] = q->perf_score * n / sum; + + } + + int nS = 0, nL = 0, s; + for (s = (s32)n - 1; s >= 0; --s) { + + if (P[s] < 1) + S[nS++] = s; + else + L[nL++] = s; + + } + + while (nS && nL) { + + a = S[--nS]; + g = L[--nL]; + afl->alias_probability[a] = P[a]; + afl->alias_table[a] = g; + P[g] = P[g] + P[a] - 1; + if (P[g] < 1) + S[nS++] = g; + else + L[nL++] = g; + + } + + while (nL) + afl->alias_probability[L[--nL]] = 1; + + while (nS) + afl->alias_probability[S[--nS]] = 1; + + /* + if (afl->debug) { + + fprintf(stderr, " %-3s %-3s %-9s\n", "entry", "alias", "prob"); + for (u32 i = 0; i < n; ++i) + fprintf(stderr, " %3i %3i %9.7f\n", i, afl->alias_table[i], + afl->alias_probability[i]); + + } + + int prob = 0; + fprintf(stderr, "Alias:"); + for (i = 0; i < n; i++) { + + fprintf(stderr, " [%u]=%u", i, afl->alias_table[i]); + if (afl->alias_table[i] >= n) + prob = i; + + } + + fprintf(stderr, "\n"); + + if (prob) { + + fprintf(stderr, "PROBLEM! alias[%u] = %u\n", prob, + afl->alias_table[prob]); + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + + fprintf(stderr, "%u: score=%f\n", i, q->perf_score); + + } + + } + + */ + +} + /* Mark deterministic checks as done for a particular queue entry. We use the .state file to avoid repeating deterministic fuzzing when resuming aborted scans. */ @@ -237,6 +360,7 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { if (likely(q->len > 4)) afl->ready_for_splicing_count++; ++afl->queued_paths; + ++afl->active_paths; ++afl->pending_not_fuzzed; afl->cycles_wo_finds = 0; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 24df2997..004adffe 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -115,6 +115,8 @@ static void usage(u8 *argv0, int more_help) { " if using QEMU, just use -c 0.\n\n" "Fuzzing behavior settings:\n" + " -Z - sequential queue selection instead of weighted " + "random\n" " -N - do not unlink the fuzzing input file (for devices " "etc.)\n" " -n - fuzz without instrumentation (non-instrumented mode)\n" @@ -131,8 +133,7 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " use -D to force -S secondary to perform deterministic " - "fuzzing\n" + " -M auto-sets -D and -Z (use -d to disable -D)\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -250,7 +251,7 @@ int main(int argc, char **argv_orig, char **envp) { s32 opt, i; u64 prev_queued = 0; - u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE; + u32 sync_interval_cnt = 0, seek_to = 0, show_help = 0, map_size = MAP_SIZE; u8 *extras_dir[4]; u8 mem_limit_given = 0, exit_1 = 0, debug = 0, extras_dir_cnt = 0 /*, have_p = 0*/; @@ -287,10 +288,14 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) { + "+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:Z")) > 0) { switch (opt) { + case 'Z': + afl->old_seed_selection = 1; + break; + case 'I': afl->infoexec = optarg; break; @@ -355,14 +360,16 @@ int main(int argc, char **argv_orig, char **envp) { afl->schedule = RARE; - } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl")) { - - afl->schedule = EXPLORE; + } else if (!stricmp(optarg, "explore") || !stricmp(optarg, "afl") || - } else if (!stricmp(optarg, "seek") || !stricmp(optarg, "default") || + !stricmp(optarg, "default") || !stricmp(optarg, "normal")) { + afl->schedule = EXPLORE; + + } else if (!stricmp(optarg, "seek")) { + afl->schedule = SEEK; } else { @@ -404,7 +411,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); - afl->skip_deterministic = 0; + afl->skip_deterministic = 0; // force determinsitic fuzzing + afl->old_seed_selection = 1; // force old queue walking seed selection if ((c = strchr(afl->sync_id, ':'))) { @@ -1131,8 +1139,10 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { - WARNF("no -M main node found. You need to run one main instance!"); - sleep(3); + WARNF( + "no -M main node found. It is recommended to run exactly one main " + "instance."); + sleep(1); } @@ -1302,7 +1312,7 @@ int main(int argc, char **argv_orig, char **envp) { show_init_stats(afl); - seek_to = find_start_position(afl); + if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); write_stats_file(afl, 0, 0, 0); maybe_update_plot_file(afl, 0, 0); @@ -1324,28 +1334,37 @@ int main(int argc, char **argv_orig, char **envp) { // real start time, we reset, so this works correctly with -V afl->start_time = get_cur_time(); + u32 runs_in_current_cycle = (u32)-1; + u32 prev_queued_paths = 0; + while (1) { u8 skipped_fuzz; cull_queue(afl); - if (!afl->queue_cur) { + if (unlikely((!afl->old_seed_selection && + runs_in_current_cycle > afl->queued_paths) || + (afl->old_seed_selection && !afl->queue_cur))) { ++afl->queue_cycle; - afl->current_entry = 0; + runs_in_current_cycle = 0; afl->cur_skipped_paths = 0; - afl->queue_cur = afl->queue; - if (seek_to) { + if (unlikely(afl->old_seed_selection)) { - afl->current_entry = seek_to; - afl->queue_cur = afl->queue_buf[seek_to]; - seek_to = 0; + afl->current_entry = 0; + afl->queue_cur = afl->queue; - } + if (unlikely(seek_to)) { - // show_stats(afl); + afl->current_entry = seek_to; + afl->queue_cur = afl->queue_buf[seek_to]; + seek_to = 0; + + } + + } if (unlikely(afl->not_on_tty)) { @@ -1366,9 +1385,11 @@ int main(int argc, char **argv_orig, char **envp) { switch (afl->expand_havoc) { case 0: + // this adds extra splicing mutation options to havoc mode afl->expand_havoc = 1; break; case 1: + // add MOpt mutator if (afl->limit_time_sig == 0 && !afl->custom_only && !afl->python_only) { @@ -1381,25 +1402,26 @@ int main(int argc, char **argv_orig, char **envp) { break; case 2: // if (!have_p) afl->schedule = EXPLOIT; + // increase havoc mutations per fuzz attempt afl->havoc_stack_pow2++; afl->expand_havoc = 3; break; case 3: + // further increase havoc mutations per fuzz attempt afl->havoc_stack_pow2++; afl->expand_havoc = 4; break; case 4: + // if not in sync mode, enable deterministic mode? + // if (!afl->sync_dir) afl->skip_deterministic = 0; + afl->expand_havoc = 5; + break; + case 5: // nothing else currently break; } - if (afl->expand_havoc) { - - } else - - afl->expand_havoc = 1; - } else { afl->use_splicing = 1; @@ -1470,6 +1492,22 @@ int main(int argc, char **argv_orig, char **envp) { } + if (likely(!afl->old_seed_selection)) { + + ++runs_in_current_cycle; + if (unlikely(prev_queued_paths < afl->queued_paths)) { + + // we have new queue entries since the last run, recreate alias table + prev_queued_paths = afl->queued_paths; + create_alias_table(afl); + + } + + afl->current_entry = select_next_queue_entry(afl); + afl->queue_cur = afl->queue_buf[afl->current_entry]; + + } + skipped_fuzz = fuzz_one(afl); if (!skipped_fuzz && !afl->stop_soon && afl->sync_id) { @@ -1490,8 +1528,12 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->stop_soon) { break; } - afl->queue_cur = afl->queue_cur->next; - ++afl->current_entry; + if (unlikely(afl->old_seed_selection)) { + + afl->queue_cur = afl->queue_cur->next; + ++afl->current_entry; + + } } -- cgit 1.4.1 From 59815cd4473bdf6568e0eee673d49d6236132b41 Mon Sep 17 00:00:00 2001 From: Edznux Date: Sun, 11 Oct 2020 23:06:37 +0200 Subject: Add documentation for StatsD --- docs/env_variables.md | 8 ++++++++ docs/status_screen.md | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 9d289f6d..ebb7521e 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -393,6 +393,14 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. + - Setting AFL_STATSD enable StatsD metrics collection. + By default AFL will send these metrics over UDP to 127.0.0.1:8125. + The host and port are configurable with AFL_STATSD_HOST and AFL_STATSD_PORT + respectively. + To get the most out of this, you should provide AFL_STATSD_TAGS_FLAVOR that + matches your StatsD server. + Available flavors are `dogstatsd`, `librato`, `signalfx` and `influxdb`. + - Outdated environment variables that are that not supported anymore: AFL_DEFER_FORKSRV AFL_PERSISTENT diff --git a/docs/status_screen.md b/docs/status_screen.md index f7655bf4..0cede6ff 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -406,3 +406,27 @@ Most of these map directly to the UI elements discussed earlier on. On top of that, you can also find an entry called `plot_data`, containing a plottable history for most of these fields. If you have gnuplot installed, you can turn this into a nice progress report with the included `afl-plot` tool. + + +### Addendum: Automatically send metrics with StatsD + +In a CI environment or when running multiple fuzzers, it can be tedious to +log into each of them or deploy scripts to read the fuzzer statistics. +Using `AFL_STATSD` (and the other related environment variables `AFL_STATSD_HOST`, +`AFL_STATSD_PORT`, `AFL_STATSD_TAGS_FLAVOR`) you can automatically send metrics +to your favorite StatsD server. Depending on your StatsD server you will be able +to monitor, trigger alerts or perform actions based on these metrics (e.g: alert on +slow exec/s for a new build, threshold of crashes, time since last crash > X, etc). + +The selected metrics are a subset of all the metrics found in the status and in +the plot file. The list is the following: `cycle_done`, `cycles_wo_finds`, +`execs_done`,`execs_per_sec`, `paths_total`, `paths_favored`, `paths_found`, +`paths_imported`, `max_depth`, `cur_path`, `pending_favs`, `pending_total`, +`variable_paths`, `unique_crashes`, `unique_hangs`, `total_crashes`, +`slowest_exec_ms`, `edges_found`, `var_byte_count`, `havoc_expansion`. +Their definitions can be found in the addendum above. + +When using multiple fuzzer instances with StatsD it is *strongly* recommended to setup +the flavor (AFL_STATSD_TAGS_FLAVOR) to match your StatsD server. This will allow you +to see individual fuzzer performance, detect bad ones, see the progress of each +strategy... -- cgit 1.4.1 From dab017dddaaab6d836a590f7bba3eea3549758d2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 12 Oct 2020 02:26:14 +0200 Subject: no -M/-S: auto-set -S default --- README.md | 20 +++++++++++--------- docs/Changelog.md | 1 + src/afl-fuzz.c | 16 +++++++++++----- 3 files changed, 23 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 819da093..384ae830 100644 --- a/README.md +++ b/README.md @@ -28,28 +28,30 @@ ## Major changes in afl++ 3.0 With afl++ 3.0 we introduced changes that break some previous afl and afl++ -behaviours: +behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is only one compiler: afl-cc. All previous compilers now symlink to this one compiler. All instrumentation source code is now in the `instrumentation/` folder. - * The gcc_plugin was replaced with a new version submitted by AdaCore, that - supports more features, thank you! + * The gcc_plugin was replaced with a new version submitted by AdaCore that + supports more features. thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current ninja build tool version and python3 setuptools are required. qemu_mode also got new options like snapshotting, instrumenting specific - shared libraries, etc. and QEMU 5.1 supports more CPU targets so this is - worth it. + shared libraries, etc. Additionally QEMU 5.1 supports more CPU targets so + this is really worth it. * When instrumenting targets, afl-cc will not supersede optimizations. This allows to fuzz targets as same as they are built for debug or release. - * afl-fuzz': - * `-i` option now descends into subdirectories. + * afl-fuzz: + * if neither -M or -S is specified, `-S default` is assumed, so more + fuzzers can easily be added later + * `-i` input directory option now descends into subdirectories. It also + does not fatal on crashes and too large files, instead it skips them + and uses them for splicing mutations * -m none is now default, set memory limits (in MB) with e.g. -m 250 * deterministic fuzzing is now disabled by default (unless using -M) and can be enabled with -D - * afl-fuzz will skip over empty dictionaries and too-large test cases instead - of failing, and use them as a source for splicing mutations ## Contents diff --git a/docs/Changelog.md b/docs/Changelog.md index f15f1d93..36022399 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -23,6 +23,7 @@ sending a mail to . with -M) - statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) + - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - allow up to 4 -x command line options - loaded extras now have a duplicate protection diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 004adffe..d42a0d36 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -411,8 +411,8 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } afl->sync_id = ck_strdup(optarg); - afl->skip_deterministic = 0; // force determinsitic fuzzing - afl->old_seed_selection = 1; // force old queue walking seed selection + afl->skip_deterministic = 0; // force determinsitic fuzzing + afl->old_seed_selection = 1; // force old queue walking seed selection if ((c = strchr(afl->sync_id, ':'))) { @@ -847,6 +847,8 @@ int main(int argc, char **argv_orig, char **envp) { "Eißfeldt, Andrea Fioraldi and Dominik Maier"); OKF("afl++ is open source, get it at " "https://github.com/AFLplusplus/AFLplusplus"); + OKF("NOTE: This is v3.x which changes several defaults and behaviours - see " + "README.md"); if (afl->sync_id && afl->is_main_node && afl->afl_env.afl_custom_mutator_only) { @@ -1135,15 +1137,19 @@ int main(int argc, char **argv_orig, char **envp) { WARNF("it is wasteful to run more than one main node!"); sleep(1); - } - - if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { + } else if (afl->is_secondary_node && check_main_node_exists(afl) == 0) { WARNF( "no -M main node found. It is recommended to run exactly one main " "instance."); sleep(1); + } else if (!afl->sync_id) { + + afl->sync_id = "default"; + afl->is_secondary_node = 1; + OKF("no -M/-S set, autoconfiguring for \"-S %s\"", afl->sync_id); + } #ifdef RAND_TEST_VALUES -- cgit 1.4.1 From c20ba2c2c5694c3a7ab0deac977e53fb576f8b37 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 13 Oct 2020 15:51:58 +0200 Subject: fix custom mutator doc --- docs/custom_mutators.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'docs') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index a128f587..81ee9de4 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -31,17 +31,17 @@ performed with the custom mutator. C/C++: ```c -void *afl_custom_init(afl_t *afl, unsigned int seed); -uint32_t afl_custom_fuzz_count(void *data, const u8 *buf, size_t buf_size); -size_t afl_custom_fuzz(void *data, uint8_t *buf, size_t buf_size, u8 **out_buf, uint8_t *add_buf, size_t add_buf_size, size_t max_size); -size_t afl_custom_post_process(void *data, uint8_t *buf, size_t buf_size, uint8_t **out_buf); -int32_t afl_custom_init_trim(void *data, uint8_t *buf, size_t buf_size); -size_t afl_custom_trim(void *data, uint8_t **out_buf); -int32_t afl_custom_post_trim(void *data, int success); -size_t afl_custom_havoc_mutation(void *data, u8 *buf, size_t buf_size, u8 **out_buf, size_t max_size); -uint8_t afl_custom_havoc_mutation_probability(void *data); -uint8_t afl_custom_queue_get(void *data, const uint8_t *filename); -void afl_custom_queue_new_entry(void *data, const uint8_t *filename_new_queue, const uint8_t *filename_orig_queue); +void *afl_custom_init(afl_state_t *afl, unsigned int seed); +unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size); +size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size); +size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf); +int afl_custom_init_trim(void *data, unsigned char *buf, size_t buf_size); +size_t afl_custom_trim(void *data, unsigned char **out_buf); +int afl_custom_post_trim(void *data, unsigned char success); +size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, size_t max_size); +unsigned char afl_custom_havoc_mutation_probability(void *data); +unsigned char afl_custom_queue_get(void *data, const unsigned char *filename); +void afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue); void afl_custom_deinit(void *data); ``` -- cgit 1.4.1 From 23872d6f2c0ae77beb832f44d392f86cc8530e1a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 14 Oct 2020 16:38:29 +0200 Subject: add documentation --- README.md | 12 ++++++++++-- docs/env_variables.md | 4 ++++ include/afl-fuzz.h | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 384ae830..c7793dff 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ behaviours and defaults: * -m none is now default, set memory limits (in MB) with e.g. -m 250 * deterministic fuzzing is now disabled by default (unless using -M) and can be enabled with -D + * a caching of testcases can now be performed and can be enabled by + editing config.h for TESTCASE_CACHE or by specifying the env variable + `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500. ## Contents @@ -555,7 +558,7 @@ is: All labels are explained in [docs/status_screen.md](docs/status_screen.md). -#### b) Using multiple cores/threads +#### b) Using multiple cores If you want to seriously fuzz then use as many cores/threads as possible to fuzz your target. @@ -563,7 +566,12 @@ fuzz your target. On the same machine - due to the design of how afl++ works - there is a maximum number of CPU cores/threads that are useful, use more and the overall performance degrades instead. This value depends on the target, and the limit is between 32 -and 64 cores/threads per machine. +and 64 cores per machine. + +If you have the RAM, it is highly recommended run the instances with a caching +of the testcases. Depending on the average testcase size (and those found +during fuzzing) and their number, a value between 50-500MB is recommended. +You can set the cache size (in MB) by setting the environment variable `AFL_TESTCACHE_SIZE`. There should be one main fuzzer (`-M main` option) and as many secondary fuzzers (eg `-S variant1`) as you have cores that you use. diff --git a/docs/env_variables.md b/docs/env_variables.md index ebb7521e..49eadcaa 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -282,6 +282,10 @@ checks or alter some of the more exotic semantics of the tool: the target. This must be equal or larger than the size the target was compiled with. + - AFL_TESTCACHE_SIZE allows you to overrider the define in config.h for + TESTCASE_CACHE. Recommended values are 50-250MB - or more if your + fuzzing finds a huge amount of paths for large inputs. + - Setting AFL_DISABLE_TRIM tells afl-fuzz to no trim test cases. This is usually a bad idea! diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 940c5602..514d558b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1164,5 +1164,9 @@ u8 *queue_testcase_get(afl_state_t *afl, struct queue_entry *q); void queue_testcase_retake(afl_state_t *afl, struct queue_entry *q, u32 old_len); +#if TESTCASE_CACHE == 1 +#error define of TESTCASE_CACHE must be zero or larger than 1 +#endif + #endif -- cgit 1.4.1 From 44c65fa0a0eb0a0382d8b80fa0c8fd3bf25b687d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 26 Oct 2020 14:44:05 +0100 Subject: add no splicing compile option and print used compile options in afl-fuzz help --- GNUmakefile | 7 +++++++ README.md | 1 + docs/Changelog.md | 2 ++ src/afl-fuzz-state.c | 2 ++ src/afl-fuzz.c | 24 ++++++++++++++++++++++++ 5 files changed, 36 insertions(+) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index ce0e1247..c8d155e4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -36,6 +36,10 @@ SH_PROGS = afl-plot afl-cmin afl-cmin.bash afl-whatsup afl-system-config MANPAGES=$(foreach p, $(PROGS) $(SH_PROGS), $(p).8) afl-as.8 ASAN_OPTIONS=detect_leaks=0 +ifdef NO_SPLICING + override CFLAGS += -DNO_SPLICING +endif + ifdef ASAN_BUILD $(info Compiling ASAN version of binaries) override CFLAGS+=$(ASAN_CFLAGS) @@ -344,7 +348,10 @@ help: @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information + @echo NO_PYTHON - disable python support + @echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing @echo AFL_NO_X86 - if compiling on non-intel/amd platforms + @echo "LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian)" @echo "==========================================" @echo e.g.: make ASAN_BUILD=1 diff --git a/README.md b/README.md index eac8b677..f09d9163 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,7 @@ These build options exist: * DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) * NO_PYTHON - disable python support +* NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing * AFL_NO_X86 - if compiling on non-intel/amd platforms * LLVM_CONFIG - if your distro doesn't use the standard name for llvm-config (e.g. Debian) diff --git a/docs/Changelog.md b/docs/Changelog.md index 36022399..f8f15fc8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -34,6 +34,8 @@ sending a mail to . - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule + - added NO_SPLICING compile option and makefile define + - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 3ce16cad..61bd06b7 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -102,7 +102,9 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->stats_update_freq = 1; afl->stats_avg_exec = -1; afl->skip_deterministic = 1; +#ifndef NO_SPLICING afl->use_splicing = 1; +#endif afl->q_testcase_max_cache_size = TESTCASE_CACHE_SIZE * 1048576UL; afl->q_testcase_max_cache_entries = 64 * 1024; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 22e6d577..cad26841 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -224,6 +224,26 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled without python module support\n"); #endif +#ifdef ASAN_BUILD + SAYF("Compiled with ASAN_BUILD\n\n"); +#endif + +#ifdef NO_SPLICING + SAYF("Compiled with NO_SPLICING\n\n"); +#endif + +#ifdef PROFILING + SAYF("Compiled with PROFILING\n\n"); +#endif + +#ifdef _DEBUG + SAYF("Compiled with _DEBUG\n\n"); +#endif + +#ifdef _AFL_DOCUMENT_MUTATIONS + SAYF("Compiled with _AFL_DOCUMENT_MUTATIONS\n\n"); +#endif + SAYF("For additional help please consult %s/README.md\n\n", doc_path); exit(1); @@ -1527,7 +1547,11 @@ int main(int argc, char **argv_orig, char **envp) { } else { + #ifndef NO_SPLICING afl->use_splicing = 1; + #else + afl->use_splicing = 0; + #endif } -- cgit 1.4.1 From efd27bd8cec501798b719d56a4bc516f4703a787 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 30 Oct 2020 14:01:59 +0100 Subject: recover heikos faq changes --- docs/FAQ.md | 104 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 53 insertions(+), 51 deletions(-) (limited to 'docs') diff --git a/docs/FAQ.md b/docs/FAQ.md index 24942492..064638f4 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -4,11 +4,11 @@ * [What is the difference between afl and afl++?](#what-is-the-difference-between-afl-and-afl) * [How to improve the fuzzing speed?](#how-to-improve-the-fuzzing-speed) - * [How do I fuzz a network service?](#how-to-fuzz-a-network-service) - * [How do I fuzz a GUI program?](#how-to-fuzz-a-gui-program) + * [How do I fuzz a network service?](#how-do-i-fuzz-a-network-service) + * [How do I fuzz a GUI program?](#how-do-i-fuzz-a-gui-program) * [What is an edge?](#what-is-an-edge) * [Why is my stability below 100%?](#why-is-my-stability-below-100) - * [How can I improve the stability value](#how-can-i-improve-the-stability-value) + * [How can I improve the stability value?](#how-can-i-improve-the-stability-value) If you find an interesting or important question missing, submit it via [https://github.com/AFLplusplus/AFLplusplus/issues](https://github.com/AFLplusplus/AFLplusplus/issues) @@ -18,51 +18,52 @@ If you find an interesting or important question missing, submit it via American Fuzzy Lop (AFL) was developed by Michał "lcamtuf" Zalewski starting in 2013/2014, and when he left Google end of 2017 he stopped developing it. -At the end of 2019 the Google fuzzing team took over maintance of AFL, however -it is only accepting PR from the community and is not developing enhancements +At the end of 2019 the Google fuzzing team took over maintenance of AFL, however +it is only accepting PRs from the community and is not developing enhancements anymore. -In the second quarter of 2019, 1 1/2 years after no further development of -AFL had happened and it became clear there would be none coming, afl++ -was born, where initially first community patches were collected and applied -for bugs and enhancements. Then from various AFL spin-offs - mostly academic +In the second quarter of 2019, 1 1/2 year later when no further development of +AFL had happened and it became clear there would none be coming, afl++ +was born, where initially community patches were collected and applied +for bug fixes and enhancements. Then from various AFL spin-offs - mostly academic research - features were integrated. This already resulted in a much advanced AFL. Until the end of 2019 the afl++ team had grown to four active developers which -then implemented their own research and feature, making it now by far the most +then implemented their own research and features, making it now by far the most flexible and feature rich guided fuzzer available as open source. And in independent fuzzing benchmarks it is one of the best fuzzers available, e.g. [Fuzzbench Report](https://www.fuzzbench.com/reports/2020-08-03/index.html) -## How to improve the fuzzing speed +## How to improve the fuzzing speed? - 1. use [instrumentation](docs/README.llvm.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) - 2. Use [persistent mode](instrumentation/README.persistent_mode.md) (x2-x20 speed increase) + 1. Use [llvm_mode](docs/llvm_mode/README.md): afl-clang-lto (llvm >= 11) or afl-clang-fast (llvm >= 9 recommended) + 2. Use [persistent mode](llvm_mode/README.persistent_mode.md) (x2-x20 speed increase) 3. Use the [afl++ snapshot module](https://github.com/AFLplusplus/AFL-Snapshot-LKM) (x2 speed increase) - 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to point the input file on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) - 5. Improve kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system more insecure) + 4. If you do not use shmem persistent mode, use `AFL_TMPDIR` to put the input file directory on a tempfs location, see [docs/env_variables.md](docs/env_variables.md) + 5. Improve Linux kernel performance: modify `/etc/default/grub`, set `GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off mds=off mitigations=off no_stf_barrier noibpb noibrs nopcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=off pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off"`; then `update-grub` and `reboot` (warning: makes the system less secure) 6. Running on an `ext2` filesystem with `noatime` mount option will be a bit faster than on any other journaling filesystem 7. Use your cores! [README.md:3.b) Using multiple cores/threads](../README.md#b-using-multiple-coresthreads) ## How do I fuzz a network service? -The short answer is - you cannot, at least "out of the box". +The short answer is - you cannot, at least not "out of the box". -Using network has a slow-down of x10-20 on the fuzzing speed, does not scale, -and finally usually it is more than one initial data packet but a back-and-forth -which is totally unsupported by most coverage aware fuzzers. +Using a network channel is inadequate for several reasons: +- it has a slow-down of x10-20 on the fuzzing speed +- it does not scale to fuzzing multiple instances easily, +- instead of one initial data packet often a back-and-forth interplay of packets is needed for stateful protocols (which is totally unsupported by most coverage aware fuzzers). The established method to fuzz network services is to modify the source code to read from a file or stdin (fd 0) (or even faster via shared memory, combine -this with persistent mode [instrumentation/README.persistent_mode.md](instrumentation/README.persistent_mode.md) +this with persistent mode [llvm_mode/README.persistent_mode.md](llvm_mode/README.persistent_mode.md) and you have a performance gain of x10 instead of a performance loss of over -x10 - that is a x100 difference! +x10 - that is a x100 difference!). If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD -to emulate the network. This is also much faster than network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/) +to emulate the network. This is also much faster than the real network would be. +See [examples/socket_fuzzing/](../examples/socket_fuzzing/). There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - @@ -73,7 +74,7 @@ which allows you to define network state with different type of data packets. If the GUI program can read the fuzz data from a file (via the command line, a fixed location or via an environment variable) without needing any user -interaction then then yes. +interaction then it would be suitable for fuzzing. Otherwise it is not possible without modifying the source code - which is a very good idea anyway as the GUI functionality is a huge CPU/time overhead @@ -82,13 +83,13 @@ for the fuzzing. So create a new `main()` that just reads the test case and calls the functionality for processing the input that the GUI program is using. -## What is an "edge" +## What is an "edge"? A program contains `functions`, `functions` contain the compiled machine code. The compiled machine code in a `function` can be in a single or many `basic blocks`. A `basic block` is the largest possible number of subsequent machine code -instructions that runs independent, meaning it does not split up to different -locations nor is it jumped into it from a different location: +instructions that has exactly one entrypoint (which can be be entered by multiple other basic blocks) +and runs linearly without branching or jumping to other addresses (except at the end). ``` function() { A: @@ -98,7 +99,7 @@ function() { if (x) goto C; else goto D; C: some code - goto D + goto E D: some code goto B @@ -108,7 +109,7 @@ function() { ``` Every code block between two jump locations is a `basic block`. -An `edge` is then the unique relationship between two `basic blocks` (from the +An `edge` is then the unique relationship between two directly connected `basic blocks` (from the code example above): ``` Block A @@ -123,8 +124,9 @@ code example above): Block E ``` Every line between two blocks is an `edge`. +Note that a few basic block loop to itself, this too would be an edge. -## Why is my stability below 100% +## Why is my stability below 100%? Stability is measured by how many percent of the edges in the target are "stable". Sending the same input again and again should take the exact same @@ -132,37 +134,37 @@ path through the target every time. If that is the case, the stability is 100%. If however randomness happens, e.g. a thread reading other external data, reaction to timing, etc. then in some of the re-executions with the same data -the result in the edge information will be different accross runs. +the edge coverage result will be different accross runs. Those edges that change are then flagged "unstable". The more "unstable" edges, the more difficult for afl++ to identify valid new paths. A value above 90% is usually fine and a value above 80% is also still ok, and -even above 20% can still result in successful finds of bugs. -However, it is recommended that below 90% or 80% you should take measures to -improve the stability. +even a value above 20% can still result in successful finds of bugs. +However, it is recommended that for values below 90% or 80% you should take +countermeasures to improve stability. -## How can I improve the stability value +## How can I improve the stability value? -For fuzzing a 100% stable target that covers all edges is the best. +For fuzzing a 100% stable target that covers all edges is the best case. A 90% stable target that covers all edges is however better than a 100% stable target that ignores 10% of the edges. With instability you basically have a partial coverage loss on an edge, with -ignore you have a full loss on that edge. +ignored functions you have a full loss on that edges. There are functions that are unstable, but also provide value to coverage, eg init functions that use fuzz data as input for example. -If however it is a function that has nothing to do with the input data is the -source, e.g. checking jitter, or is a hash map function etc. then it should -not be instrumented. +If however a function that has nothing to do with the input data is the +source of instability, e.g. checking jitter, or is a hash map function etc. +then it should not be instrumented. -To be able to make this decision the following process will allow you to -identify the functions with variable edges so you can make this decision. +To be able to exclude these functions (based on AFL++'s measured stability) +the following process will allow to identify functions with variable edges. -Four steps are required to do this and requires quite some knowledge of -coding and/or disassembly and it is only effectively possible with +Four steps are required to do this and it also requires quite some knowledge +of coding and/or disassembly and is effectively possible only with afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. 1. First step: Identify which edge ID numbers are unstable @@ -171,7 +173,7 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. The out/fuzzer_stats file will then show the edge IDs that were identified as unstable. - 2. Second step: Find the responsible function. + 2. Second step: Find the responsible function(s). a) For LTO instrumented binaries this can be documented during compile time, just set `export AFL_LLVM_DOCUMENT_IDS=/path/to/a/file`. @@ -180,10 +182,10 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. b) For PCGUARD instrumented binaries it is much more difficult. Here you can either modify the __sanitizer_cov_trace_pc_guard function in - instrumentation/afl-llvm-rt.o.c to write a backtrace to a file if the ID in + llvm_mode/afl-llvm-rt.o.c to write a backtrace to a file if the ID in __afl_area_ptr[*guard] is one of the unstable edge IDs. (Example code is already there). - Then recompile and reinstall instrumentation and rebuild your target. + Then recompile and reinstall llvm_mode and rebuild your target. Run the recompiled target with afl-fuzz for a while and then check the file that you wrote with the backtrace information. Alternatively you can use `gdb` to hook __sanitizer_cov_trace_pc_guard_init @@ -191,20 +193,20 @@ afl-clang-fast PCGUARD and afl-clang-lto LTO instrumentation. and set a write breakpoint to that address (`watch 0x.....`). c) in all other instrumentation types this is not possible. So just - recompile with the the two mentioned above. This is just for + recompile with the two mentioned above. This is just for identifying the functions that have unstable edges. 3. Third step: create a text file with the filenames/functions Identify which source code files contain the functions that you need to remove from instrumentation, or just specify the functions you want to - skip instrumenting. Note that optimization might inline functions! + skip for instrumentation. Note that optimization might inline functions! - Simply follow this document on how to do this: [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) + Simply follow this document on how to do this: [llvm_mode/README.instrument_list.md](llvm_mode/README.instrument_list.md) If PCGUARD is used, then you need to follow this guide (needs llvm 12+!): [http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation](http://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation) - Only deny those functions from instrumentation that provide no value + Only exclude those functions from instrumentation that provide no value for coverage - that is if it does not process any fuzz data directly or indirectly (e.g. hash maps, thread management etc.). If however a function directly or indirectly handles fuzz data then you -- cgit 1.4.1 From dfb847a51bb3dbcbe6237e92fa385b8c6c6c417c Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 30 Oct 2020 14:37:06 +0100 Subject: review, mark env variables, linkify --- docs/env_variables.md | 359 +++++++++++++++++++++++++------------------------- 1 file changed, 180 insertions(+), 179 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 49eadcaa..a36f2b4e 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -2,63 +2,64 @@ This document discusses the environment variables used by American Fuzzy Lop++ to expose various exotic functions that may be (rarely) useful for power - users or for some types of custom fuzzing setups. See README.md for the general + users or for some types of custom fuzzing setups. See [README.md](README.md) for the general instruction manual. ## 1) Settings for all compilers Starting with afl++ 3.0 there is only one compiler: afl-cc To select the different instrumentation modes this can be done by - 1. passing --afl-MODE command line options to the compiler - 2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, + 1. passing the --afl-MODE command line option to the compiler + 2. or using a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++, afl-gcc-fast, afl-g++-fast - 3. using the environment variable AFL_CC_COMPILER with MODE + 3. or using the environment variable `AFL_CC_COMPILER` with `MODE` -MODE can one of LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN -(afl-g*-fast) or GCC (afl-gcc/afl-g++). +`MODE` can be one of `LTO` (afl-clang-lto*), `LLVM` (afl-clang-fast*), `GCC_PLUGIN` +(afl-g*-fast) or `GCC` (afl-gcc/afl-g++). -Because beside the --afl-MODE command no afl specific command-line options -are accepted, the compile-time tools make fairly broad use of environmental -variables: - - Most afl tools do not print any ouput if stout/stderr are redirected. - If you want to have the output into a file then set the AFL_DEBUG +Because (with the exception of the --afl-MODE command line option) the +compile-time tools do not accept afl specific command-line options, they +make fairly broad use of environmental variables instead: + + - Most afl tools do not print any output if stdout/stderr are redirected. + If you want to get the output into a file then set the `AFL_DEBUG` environment variable. This is sadly necessary for various build processes which fail otherwise. - - Setting AFL_HARDEN automatically adds code hardening options when invoking - the downstream compiler. This currently includes -D_FORTIFY_SOURCE=2 and - -fstack-protector-all. The setting is useful for catching non-crashing + - Setting `AFL_HARDEN` automatically adds code hardening options when invoking + the downstream compiler. This currently includes `-D_FORTIFY_SOURCE=2` and + `-fstack-protector-all`. The setting is useful for catching non-crashing memory bugs at the expense of a very slight (sub-5%) performance loss. - - By default, the wrapper appends -O3 to optimize builds. Very rarely, this - will cause problems in programs built with -Werror, simply because -O3 + - By default, the wrapper appends `-O3` to optimize builds. Very rarely, this + will cause problems in programs built with -Werror, simply because `-O3` enables more thorough code analysis and can spew out additional warnings. - To disable optimizations, set AFL_DONT_OPTIMIZE. - However if -O... and/or -fno-unroll-loops are set, these are not - overriden. + To disable optimizations, set `AFL_DONT_OPTIMIZE`. + However if `-O...` and/or `-fno-unroll-loops` are set, these are not + overridden. - - Setting AFL_USE_ASAN automatically enables ASAN, provided that your + - Setting `AFL_USE_ASAN` automatically enables ASAN, provided that your compiler supports that. Note that fuzzing with ASAN is mildly challenging - see [notes_for_asan.md](notes_for_asan.md). - (You can also enable MSAN via AFL_USE_MSAN; ASAN and MSAN come with the + (You can also enable MSAN via `AFL_USE_MSAN`; ASAN and MSAN come with the same gotchas; the modes are mutually exclusive. UBSAN can be enabled - similarly by setting the environment variable AFL_USE_UBSAN=1. Finally + similarly by setting the environment variable `AFL_USE_UBSAN=1`. Finally there is the Control Flow Integrity sanitizer that can be activated by - AFL_USE_CFISAN=1) + `AFL_USE_CFISAN=1`) - - Setting AFL_CC, AFL_CXX, and AFL_AS lets you use alternate downstream + - Setting `AFL_CC`, `AFL_CXX`, and `AFL_AS` lets you use alternate downstream compilation tools, rather than the default 'clang', 'gcc', or 'as' binaries - in your $PATH. + in your `$PATH`. - - AFL_PATH can be used to point afl-gcc to an alternate location of afl-as. + - `AFL_PATH` can be used to point afl-gcc to an alternate location of afl-as. One possible use of this is examples/clang_asm_normalize/, which lets you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) - - Setting AFL_INST_RATIO to a percentage between 0 and 100% controls the + - Setting `AFL_INST_RATIO` to a percentage between 0 and 100 controls the probability of instrumenting every branch. This is (very rarely) useful when dealing with exceptionally complex programs that saturate the output bitmap. Examples include v8, ffmpeg, and perl. @@ -66,51 +67,51 @@ variables: (If this ever happens, afl-fuzz will warn you ahead of the time by displaying the "bitmap density" field in fiery red.) - Setting AFL_INST_RATIO to 0 is a valid choice. This will instrument only + Setting `AFL_INST_RATIO` to 0 is a valid choice. This will instrument only the transitions between function entry points, but not individual branches. Note that this is an outdated variable. A few instances (e.g. afl-gcc) still support these, but state-of-the-art (e.g. LLVM LTO and LLVM PCGUARD) do not need this. - - AFL_NO_BUILTIN causes the compiler to generate code suitable for use with + - `AFL_NO_BUILTIN` causes the compiler to generate code suitable for use with libtokencap.so (but perhaps running a bit slower than without the flag). - - TMPDIR is used by afl-as for temporary files; if this variable is not set, + - `TMPDIR` is used by afl-as for temporary files; if this variable is not set, the tool defaults to /tmp. - If you are a weird person that wants to compile and instrument asm - text files then use the AFL_AS_FORCE_INSTRUMENT variable: - AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo + text files then use the `AFL_AS_FORCE_INSTRUMENT` variable: + `AFL_AS_FORCE_INSTRUMENT=1 afl-gcc foo.s -o foo` - - Setting AFL_QUIET will prevent afl-cc and afl-as banners from being + - Setting `AFL_QUIET` will prevent afl-cc and afl-as banners from being displayed during compilation, in case you find them distracting. - - Setting AFL_CAL_FAST will speed up the initial calibration, if the - application is very slow + - Setting `AFL_CAL_FAST` will speed up the initial calibration, if the + application is very slow. ## 2) Settings for LLVM and LTO: afl-clang-fast / afl-clang-fast++ / afl-clang-lto / afl-clang-lto++ The native instrumentation helpers (instrumentation and gcc_plugin) accept a subset -of the settings discussed in section #1, with the exception of: +of the settings discussed in section 1, with the exception of: - LLVM modes support `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` which will - write all constant string comparisons to this file to be used with + write all constant string comparisons to this file to be used later with afl-fuzz' `-x` option. - - AFL_AS, since this toolchain does not directly invoke GNU as. + - `AFL_AS`, since this toolchain does not directly invoke GNU as. - - TMPDIR and AFL_KEEP_ASSEMBLY, since no temporary assembly files are + - `TMPDIR` and `AFL_KEEP_ASSEMBLY`, since no temporary assembly files are created. - - AFL_INST_RATIO, as we by default collision free instrumentation is used. + - `AFL_INST_RATIO`, as we by default use collision free instrumentation. Not all passes support this option though as it is an outdated feature. -Then there are a few specific features that are only available in instrumentation: +Then there are a few specific features that are only available in instrumentation mode: ### Select the instrumentation mode - - AFL_LLVM_INSTRUMENT - this configures the instrumentation mode. + - `AFL_LLVM_INSTRUMENT` - this configures the instrumentation mode. Available options: CLASSIC - classic AFL (map[cur_loc ^ prev_loc >> 1]++) (default) CFG - InsTrim instrumentation (see below) @@ -119,148 +120,148 @@ Then there are a few specific features that are only available in instrumentatio NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16) In CLASSIC (default) and CFG/INSTRIM you can also specify CTX and/or NGRAM, seperate the options with a comma "," then, e.g.: - AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4 + `AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4` Not that this is a good idea to use both CTX and NGRAM :) ### LTO - This is a different kind way of instrumentation: first it compiles all + This is a different kind way of instrumentation: first it compiles all code in LTO (link time optimization) and then performs an edge inserting instrumentation which is 100% collision free (collisions are a big issue in afl and afl-like instrumentations). This is performed by using afl-clang-lto/afl-clang-lto++ instead of afl-clang-fast, but is only built if LLVM 11 or newer is used. - - AFL_LLVM_INSTRUMENT=CFG will use Control Flow Graph instrumentation. + - `AFL_LLVM_INSTRUMENT=CFG` will use Control Flow Graph instrumentation. (not recommended for afl-clang-fast, default for afl-clang-lto as there it is a different and better kind of instrumentation.) - None of the following options are necessary to be used and are rather for + None of the following options are necessary to be used and are rather for manual use (which only ever the author of this LTO implementation will use). - These are used if several seperated instrumentation are performed which + These are used if several separated instrumentations are performed which are then later combined. - - AFL_LLVM_DOCUMENT_IDS=file will document to a file which edge ID was given + - `AFL_LLVM_DOCUMENT_IDS=file` will document to a file which edge ID was given to which function. This helps to identify functions with variable bytes or which functions were touched by an input. - - AFL_LLVM_MAP_ADDR sets the fixed map address to a different address than - the default 0x10000. A value of 0 or empty sets the map address to be + - `AFL_LLVM_MAP_ADDR` sets the fixed map address to a different address than + the default `0x10000`. A value of 0 or empty sets the map address to be dynamic (the original afl way, which is slower) - - AFL_LLVM_MAP_DYNAMIC sets the shared memory address to be dynamic - - AFL_LLVM_LTO_STARTID sets the starting location ID for the instrumentation. + - `AFL_LLVM_MAP_DYNAMIC` sets the shared memory address to be dynamic + - `AFL_LLVM_LTO_STARTID` sets the starting location ID for the instrumentation. This defaults to 1 - - AFL_LLVM_LTO_DONTWRITEID prevents that the highest location ID written + - `AFL_LLVM_LTO_DONTWRITEID` prevents that the highest location ID written into the instrumentation is set in a global variable - See instrumentation/README.LTO.md for more information. + See [instrumentation/README.lto.md](../instrumentation/README.lto.md) for more information. ### INSTRIM - This feature increases the speed by ~15% without any disadvantages to the + This feature increases the speed by ~15% without any disadvantages to the classic instrumentation. - Note that there is also an LTO version (if you have llvm 11 or higher) - + Note that there is also an LTO version (if you have llvm 11 or higher) - that is the best instrumentation we have. Use `afl-clang-lto` to activate. The InsTrim LTO version additionally has all the options and features of LTO (see above). - - Setting AFL_LLVM_INSTRIM or AFL_LLVM_INSTRUMENT=CFG to activates this mode + - Setting `AFL_LLVM_INSTRIM` or `AFL_LLVM_INSTRUMENT=CFG` activates this mode - - Setting AFL_LLVM_INSTRIM_LOOPHEAD=1 expands on INSTRIM to optimize loops. + - Setting `AFL_LLVM_INSTRIM_LOOPHEAD=1` expands on INSTRIM to optimize loops. afl-fuzz will only be able to see the path the loop took, but not how many times it was called (unless it is a complex loop). - See instrumentation/README.instrim.md + See [instrumentation/README.instrim.md](../instrumentation/README.instrim.md) ### NGRAM - - Setting AFL_LLVM_NGRAM_SIZE or AFL_LLVM_INSTRUMENT=NGRAM-{value} + - Setting `AFL_LLVM_NGRAM_SIZE` or `AFL_LLVM_INSTRUMENT=NGRAM-{value}` activates ngram prev_loc coverage, good values are 2, 4 or 8 (any value between 2 and 16 is valid). - It is highly recommended to increase the MAP_SIZE_POW2 definition in + It is highly recommended to increase the `MAP_SIZE_POW2` definition in config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See instrumentation/README.ctx.md + See [instrumentation/README.ngram.md](../instrumentation/README.ngram.md) ### CTX - - Setting AFL_LLVM_CTX or AFL_LLVM_INSTRUMENT=CTX + - Setting `AFL_LLVM_CTX` or `AFL_LLVM_INSTRUMENT=CTX` activates context sensitive branch coverage - meaning that each edge is additionally combined with its caller. - It is highly recommended to increase the MAP_SIZE_POW2 definition in + It is highly recommended to increase the `MAP_SIZE_POW2` definition in config.h to at least 18 and maybe up to 20 for this as otherwise too many map collisions occur. - See instrumentation/README.ngram.md + See [instrumentation/README.ctx.md](../instrumentation/README.ctx.md) ### LAF-INTEL - This great feature will split compares to series of single byte comparisons + This great feature will split compares into series of single byte comparisons to allow afl-fuzz to find otherwise rather impossible paths. It is not restricted to Intel CPUs ;-) - - Setting AFL_LLVM_LAF_TRANSFORM_COMPARES will split string compare functions + - Setting `AFL_LLVM_LAF_TRANSFORM_COMPARES` will split string compare functions - - Setting AFL_LLVM_LAF_SPLIT_SWITCHES will split switch()es + - Setting `AFL_LLVM_LAF_SPLIT_SWITCHES` will split all `switch` constructs - - Setting AFL_LLVM_LAF_SPLIT_COMPARES will split all floating point and + - Setting `AFL_LLVM_LAF_SPLIT_COMPARES` will split all floating point and 64, 32 and 16 bit integer CMP instructions - - Setting AFL_LLVM_LAF_SPLIT_FLOATS will split floating points, needs + - Setting `AFL_LLVM_LAF_SPLIT_FLOATS` will split floating points, needs AFL_LLVM_LAF_SPLIT_COMPARES to be set - - Setting AFL_LLVM_LAF_ALL sets all of the above + - Setting `AFL_LLVM_LAF_ALL` sets all of the above - See instrumentation/README.laf-intel.md for more information. + See [instrumentation/README.laf-intel.md](../instrumentation/README.laf-intel.md) for more information. ### INSTRUMENT LIST (selectively instrument files and functions) - This feature allows selectively instrumentation of the source + This feature allows selective instrumentation of the source - - Setting AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST with a filenames and/or + - Setting `AFL_LLVM_ALLOWLIST` or `AFL_LLVM_DENYLIST` with a filenames and/or function will only instrument (or skip) those files that match the names listed in the specified file. - See instrumentation/README.instrument_list.md for more information. + See [instrumentation/README.instrument_list.md](../instrumentation/README.instrument_list.md) for more information. ### NOT_ZERO - - Setting AFL_LLVM_NOT_ZERO=1 during compilation will use counters + - Setting `AFL_LLVM_NOT_ZERO=1` during compilation will use counters that skip zero on overflow. This is the default for llvm >= 9, however for llvm versions below that this will increase an unnecessary slowdown due a performance issue that is only fixed in llvm 9+. This feature increases path discovery by a little bit. - - Setting AFL_LLVM_SKIP_NEVERZERO=1 will not implement the skip zero + - Setting `AFL_LLVM_SKIP_NEVERZERO=1` will not implement the skip zero test. If the target performs only few loops then this will give a small performance boost. - See instrumentation/README.neverzero.md + See [instrumentation/README.neverzero.md](../instrumentation/README.neverzero.md) ### CMPLOG - - Setting AFL_LLVM_CMPLOG=1 during compilation will tell afl-clang-fast to - produce a CmpLog binary. See instrumentation/README.cmplog.md + - Setting `AFL_LLVM_CMPLOG=1` during compilation will tell afl-clang-fast to + produce a CmpLog binary. - See instrumentation/README.neverzero.md + See [instrumentation/README.cmplog.md](../instrumentation/README.cmplog.md) ## 3) Settings for GCC / GCC_PLUGIN modes Then there are a few specific features that are only available in GCC and GCC_PLUGIN mode. - - Setting AFL_KEEP_ASSEMBLY prevents afl-as from deleting instrumented + - Setting `AFL_KEEP_ASSEMBLY` prevents afl-as from deleting instrumented assembly files. Useful for troubleshooting problems or understanding how the tool works. (GCC mode only) To get them in a predictable place, try something like: - +``` mkdir assembly_here TMPDIR=$PWD/assembly_here AFL_KEEP_ASSEMBLY=1 make clean all - - - Setting AFL_GCC_INSTRUMENT_FILE with a filename will only instrument those +``` + - Setting `AFL_GCC_INSTRUMENT_FILE` with a filename will only instrument those files that match the names listed in this file (one filename per line). - See gcc_plugin/README.instrument_list.md for more information. + See [instrumentation/README.instrument_list.md](../instrumentation/README.instrument_list.md) for more information. (GCC_PLUGIN mode only) ## 4) Settings for afl-fuzz @@ -268,127 +269,127 @@ GCC_PLUGIN mode. The main fuzzer binary accepts several options that disable a couple of sanity checks or alter some of the more exotic semantics of the tool: - - Setting AFL_SKIP_CPUFREQ skips the check for CPU scaling policy. This is + - Setting `AFL_SKIP_CPUFREQ` skips the check for CPU scaling policy. This is useful if you can't change the defaults (e.g., no root access to the system) and are OK with some performance loss. - - AFL_EXIT_WHEN_DONE causes afl-fuzz to terminate when all existing paths + - `AFL_EXIT_WHEN_DONE` causes afl-fuzz to terminate when all existing paths have been fuzzed and there were no new finds for a while. This would be normally indicated by the cycle counter in the UI turning green. May be convenient for some types of automated jobs. - - AFL_MAP_SIZE sets the size of the shared map that afl-fuzz, afl-showmap, + - `AFL_MAP_SIZE` sets the size of the shared map that afl-fuzz, afl-showmap, afl-tmin and afl-analyze create to gather instrumentation data from the target. This must be equal or larger than the size the target was compiled with. - - AFL_TESTCACHE_SIZE allows you to overrider the define in config.h for - TESTCASE_CACHE. Recommended values are 50-250MB - or more if your - fuzzing finds a huge amount of paths for large inputs. + - `AFL_TESTCACHE_SIZE` allows you to override the size of `#define TESTCASE_CACHE` + in config.h. Recommended values are 50-250MB - or more if your fuzzing + finds a huge amount of paths for large inputs. - - Setting AFL_DISABLE_TRIM tells afl-fuzz to no trim test cases. This is + - Setting `AFL_DISABLE_TRIM` tells afl-fuzz not to trim test cases. This is usually a bad idea! - - Setting AFL_NO_AFFINITY disables attempts to bind to a specific CPU core + - Setting `AFL_NO_AFFINITY` disables attempts to bind to a specific CPU core on Linux systems. This slows things down, but lets you run more instances of afl-fuzz than would be prudent (if you really want to). - - AFL_SKIP_CRASHES causes AFL to tolerate crashing files in the input + - `AFL_SKIP_CRASHES` causes AFL++ to tolerate crashing files in the input queue. This can help with rare situations where a program crashes only intermittently, but it's not really recommended under normal operating conditions. - - Setting AFL_HANG_TMOUT allows you to specify a different timeout for + - Setting `AFL_HANG_TMOUT` allows you to specify a different timeout for deciding if a particular test case is a "hang". The default is 1 second - or the value of the -t parameter, whichever is larger. Dialing the value + or the value of the `-t` parameter, whichever is larger. Dialing the value down can be useful if you are very concerned about slow inputs, or if you - don't want AFL to spend too much time classifying that stuff and just + don't want AFL++ to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. - - AFL_NO_ARITH causes AFL to skip most of the deterministic arithmetics. + - `AFL_NO_ARITH` causes AFL++ to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. - - AFL_NO_SNAPSHOT will advice afl-fuzz not to use the snapshot feature + - `AFL_NO_SNAPSHOT` will advice afl-fuzz not to use the snapshot feature if the snapshot lkm is loaded - - AFL_SHUFFLE_QUEUE randomly reorders the input queue on startup. Requested + - `AFL_SHUFFLE_QUEUE` randomly reorders the input queue on startup. Requested by some users for unorthodox parallelized fuzzing setups, but not advisable otherwise. - - AFL_TMPDIR is used to write the .cur_input file to if exists, and in + - `AFL_TMPDIR` is used to write the `.cur_input` file to if exists, and in the normal output directory otherwise. You would use this to point to a ramdisk/tmpfs. This increases the speed by a small value but also reduces the stress on SSDs. - When developing custom instrumentation on top of afl-fuzz, you can use - AFL_SKIP_BIN_CHECK to inhibit the checks for non-instrumented binaries - and shell scripts; and AFL_DUMB_FORKSRV in conjunction with the -n + `AFL_SKIP_BIN_CHECK` to inhibit the checks for non-instrumented binaries + and shell scripts; and `AFL_DUMB_FORKSRV` in conjunction with the `-n` setting to instruct afl-fuzz to still follow the fork server protocol without expecting any instrumentation data in return. - - When running in the -M or -S mode, setting AFL_IMPORT_FIRST causes the + - When running in the `-M` or `-S` mode, setting `AFL_IMPORT_FIRST` causes the fuzzer to import test cases from other instances before doing anything else. This makes the "own finds" counter in the UI more accurate. Beyond counter aesthetics, not much else should change. - - Note that AFL_POST_LIBRARY is deprecated, use AFL_CUSTOM_MUTATOR_LIBRARY + - Note that `AFL_POST_LIBRARY` is deprecated, use `AFL_CUSTOM_MUTATOR_LIBRARY` instead (see below). - - Setting AFL_CUSTOM_MUTATOR_LIBRARY to a shared library with + - Setting `AFL_CUSTOM_MUTATOR_LIBRARY` to a shared library with afl_custom_fuzz() creates additional mutations through this library. If afl-fuzz is compiled with Python (which is autodetected during builing - afl-fuzz), setting AFL_PYTHON_MODULE to a Python module can also provide + afl-fuzz), setting `AFL_PYTHON_MODULE` to a Python module can also provide additional mutations. - If AFL_CUSTOM_MUTATOR_ONLY is also set, all mutations will solely be + If `AFL_CUSTOM_MUTATOR_ONLY` is also set, all mutations will solely be performed with the custom mutator. This feature allows to configure custom mutators which can be very helpful, e.g. fuzzing XML or other highly flexible structured input. Please see [custom_mutators.md](custom_mutators.md). - - AFL_FAST_CAL keeps the calibration stage about 2.5x faster (albeit less + - `AFL_FAST_CAL` keeps the calibration stage about 2.5x faster (albeit less precise), which can help when starting a session against a slow target. - The CPU widget shown at the bottom of the screen is fairly simplistic and may complain of high load prematurely, especially on systems with low core - counts. To avoid the alarming red color, you can set AFL_NO_CPU_RED. + counts. To avoid the alarming red color, you can set `AFL_NO_CPU_RED`. - - In QEMU mode (-Q), AFL_PATH will be searched for afl-qemu-trace. + - In QEMU mode (-Q), `AFL_PATH` will be searched for afl-qemu-trace. - - Setting AFL_CYCLE_SCHEDULES will switch to a different schedule everytime + - Setting `AFL_CYCLE_SCHEDULES` will switch to a different schedule everytime a cycle is finished. - - Setting AFL_EXPAND_HAVOC_NOW will start in the extended havoc mode that + - Setting `AFL_EXPAND_HAVOC_NOW` will start in the extended havoc mode that includes costly mutations. afl-fuzz automatically enables this mode when deemed useful otherwise. - - Setting AFL_PRELOAD causes AFL to set LD_PRELOAD for the target binary + - Setting `AFL_PRELOAD` causes AFL++ to set `LD_PRELOAD` for the target binary without disrupting the afl-fuzz process itself. This is useful, among other things, for bootstrapping libdislocator.so. - - Setting AFL_NO_UI inhibits the UI altogether, and just periodically prints + - Setting `AFL_NO_UI` inhibits the UI altogether, and just periodically prints some basic stats. This behavior is also automatically triggered when the output from afl-fuzz is redirected to a file or to a pipe. - - Setting AFL_FORCE_UI will force painting the UI on the screen even if + - Setting `AFL_FORCE_UI` will force painting the UI on the screen even if no valid terminal was detected (for virtual consoles) - - If you are Jakub, you may need AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES. + - If you are Jakub, you may need `AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES`. Others need not apply. - - Benchmarking only: AFL_BENCH_JUST_ONE causes the fuzzer to exit after - processing the first queue entry; and AFL_BENCH_UNTIL_CRASH causes it to + - Benchmarking only: `AFL_BENCH_JUST_ONE` causes the fuzzer to exit after + processing the first queue entry; and `AFL_BENCH_UNTIL_CRASH` causes it to exit soon after the first crash is found. - - Setting AFL_DEBUG_CHILD_OUTPUT will not suppress the child output. + - Setting `AFL_DEBUG_CHILD_OUTPUT` will not suppress the child output. Not pretty but good for debugging purposes. - - Setting AFL_NO_CPU_RED will not display very high cpu usages in red color. + - Setting `AFL_NO_CPU_RED` will not display very high cpu usages in red color. - - Setting AFL_AUTORESUME will resume a fuzz run (same as providing `-i -`) + - Setting `AFL_AUTORESUME` will resume a fuzz run (same as providing `-i -`) for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. - - Setting AFL_NO_FORKSRV disables the forkserver optimization, reverting to + - Setting `AFL_NO_FORKSRV` disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when working with unruly libraries that create threads or do other crazy things when initializing (before the instrumentation has a chance to run). @@ -397,128 +398,128 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. - - Setting AFL_STATSD enable StatsD metrics collection. - By default AFL will send these metrics over UDP to 127.0.0.1:8125. - The host and port are configurable with AFL_STATSD_HOST and AFL_STATSD_PORT + - Setting `AFL_STATSD` enable StatsD metrics collection. + By default AFL++ will send these metrics over UDP to 127.0.0.1:8125. + The host and port are configurable with `AFL_STATSD_HOST` and `AFL_STATSD_PORT` respectively. - To get the most out of this, you should provide AFL_STATSD_TAGS_FLAVOR that + To get the most out of this, you should provide `AFL_STATSD_TAGS_FLAVOR` that matches your StatsD server. Available flavors are `dogstatsd`, `librato`, `signalfx` and `influxdb`. - - Outdated environment variables that are that not supported anymore: - AFL_DEFER_FORKSRV - AFL_PERSISTENT + - Outdated environment variables that are not supported anymore: + `AFL_DEFER_FORKSRV` + `AFL_PERSISTENT` ## 5) Settings for afl-qemu-trace The QEMU wrapper used to instrument binary-only code supports several settings: - - It is possible to set AFL_INST_RATIO to skip the instrumentation on some + - It is possible to set `AFL_INST_RATIO` to skip the instrumentation on some of the basic blocks, which can be useful when dealing with very complex binaries. - - Setting AFL_INST_LIBS causes the translator to also instrument the code + - Setting `AFL_INST_LIBS` causes the translator to also instrument the code inside any dynamically linked libraries (notably including glibc). - - Setting AFL_COMPCOV_LEVEL enables the CompareCoverage tracing of all cmp + - Setting `AFL_COMPCOV_LEVEL` enables the CompareCoverage tracing of all cmp and sub in x86 and x86_64 and memory comparions functions (e.g. strcmp, - memcmp, ...) when libcompcov is preloaded using AFL_PRELOAD. + memcmp, ...) when libcompcov is preloaded using `AFL_PRELOAD`. More info at qemu_mode/libcompcov/README.md. - There are two levels at the moment, AFL_COMPCOV_LEVEL=1 that instruments + There are two levels at the moment, `AFL_COMPCOV_LEVEL=1` that instruments only comparisons with immediate values / read-only memory and - AFL_COMPCOV_LEVEL=2 that instruments all the comparions. Level 2 is more + `AFL_COMPCOV_LEVEL=2` that instruments all the comparions. Level 2 is more accurate but may need a larger shared memory. - - Setting AFL_QEMU_COMPCOV enables the CompareCoverage tracing of all + - Setting `AFL_QEMU_COMPCOV` enables the CompareCoverage tracing of all cmp and sub in x86 and x86_64. - This is an alias of AFL_COMPCOV_LEVEL=1 when AFL_COMPCOV_LEVEL is + This is an alias of `AFL_COMPCOV_LEVEL=1` when `AFL_COMPCOV_LEVEL` is not specified. - The underlying QEMU binary will recognize any standard "user space - emulation" variables (e.g., QEMU_STACK_SIZE), but there should be no + emulation" variables (e.g., `QEMU_STACK_SIZE`), but there should be no reason to touch them. - - AFL_DEBUG will print the found entrypoint for the binary to stderr. + - `AFL_DEBUG` will print the found entrypoint for the binary to stderr. Use this if you are unsure if the entrypoint might be wrong - but - use it directly, e.g. afl-qemu-trace ./program + use it directly, e.g. `afl-qemu-trace ./program` - - AFL_ENTRYPOINT allows you to specify a specific entrypoint into the + - `AFL_ENTRYPOINT` allows you to specify a specific entrypoint into the binary (this can be very good for the performance!). - The entrypoint is specified as hex address, e.g. 0x4004110 + The entrypoint is specified as hex address, e.g. `0x4004110` Note that the address must be the address of a basic block. - When the target is i386/x86_64 you can specify the address of the function that has to be the body of the persistent loop using - AFL_QEMU_PERSISTENT_ADDR=`start addr`. + `AFL_QEMU_PERSISTENT_ADDR=start addr`. - Another modality to execute the persistent loop is to specify also the - AFL_QEMU_PERSISTENT_RET=`end addr` env variable. + `AFL_QEMU_PERSISTENT_RET=end addr` env variable. With this variable assigned, instead of patching the return address, the specified instruction is transformed to a jump towards `start addr`. - - AFL_QEMU_PERSISTENT_GPR=1 QEMU will save the original value of general + - `AFL_QEMU_PERSISTENT_GPR=1` QEMU will save the original value of general purpose registers and restore them in each persistent cycle. - - With AFL_QEMU_PERSISTENT_RETADDR_OFFSET you can specify the offset from the + - With `AFL_QEMU_PERSISTENT_RETADDR_OFFSET` you can specify the offset from the stack pointer in which QEMU can find the return address when `start addr` is - hitted. + hit. ## 6) Settings for afl-cmin The corpus minimization script offers very little customization: - - Setting AFL_PATH offers a way to specify the location of afl-showmap - and afl-qemu-trace (the latter only in -Q mode). + - Setting `AFL_PATH` offers a way to specify the location of afl-showmap + and afl-qemu-trace (the latter only in `-Q` mode). - - AFL_KEEP_TRACES makes the tool keep traces and other metadata used for + - `AFL_KEEP_TRACES` makes the tool keep traces and other metadata used for minimization and normally deleted at exit. The files can be found in the - /.traces/*. + `/.traces/` directory. - - AFL_ALLOW_TMP permits this and some other scripts to run in /tmp. This is + - `AFL_ALLOW_TMP` permits this and some other scripts to run in /tmp. This is a modest security risk on multi-user systems with rogue users, but should be safe on dedicated fuzzing boxes. # #6) Settings for afl-tmin -Virtually nothing to play with. Well, in QEMU mode (-Q), AFL_PATH will be -searched for afl-qemu-trace. In addition to this, TMPDIR may be used if a +Virtually nothing to play with. Well, in QEMU mode (`-Q`), `AFL_PATH` will be +searched for afl-qemu-trace. In addition to this, `TMPDIR` may be used if a temporary file can't be created in the current working directory. -You can specify AFL_TMIN_EXACT if you want afl-tmin to require execution paths +You can specify `AFL_TMIN_EXACT` if you want afl-tmin to require execution paths to match when minimizing crashes. This will make minimization less useful, but may prevent the tool from "jumping" from one crashing condition to another in -very buggy software. You probably want to combine it with the -e flag. +very buggy software. You probably want to combine it with the `-e` flag. ## 8) Settings for afl-analyze -You can set AFL_ANALYZE_HEX to get file offsets printed as hexadecimal instead +You can set `AFL_ANALYZE_HEX` to get file offsets printed as hexadecimal instead of decimal. ## 9) Settings for libdislocator The library honors these environmental variables: - - AFL_LD_LIMIT_MB caps the size of the maximum heap usage permitted by the + - `AFL_LD_LIMIT_MB` caps the size of the maximum heap usage permitted by the library, in megabytes. The default value is 1 GB. Once this is exceeded, allocations will return NULL. - - AFL_LD_HARD_FAIL alters the behavior by calling abort() on excessive - allocations, thus causing what AFL would perceive as a crash. Useful for + - `AFL_LD_HARD_FAIL` alters the behavior by calling `abort()` on excessive + allocations, thus causing what AFL++ would perceive as a crash. Useful for programs that are supposed to maintain a specific memory footprint. - - AFL_LD_VERBOSE causes the library to output some diagnostic messages + - `AFL_LD_VERBOSE` causes the library to output some diagnostic messages that may be useful for pinpointing the cause of any observed issues. - - AFL_LD_NO_CALLOC_OVER inhibits abort() on calloc() overflows. Most + - `AFL_LD_NO_CALLOC_OVER` inhibits `abort()` on `calloc()` overflows. Most of the common allocators check for that internally and return NULL, so it's a security risk only in more exotic setups. - - AFL_ALIGNED_ALLOC=1 will force the alignment of the allocation size to - max_align_t to be compliant with the C standard. + - `AFL_ALIGNED_ALLOC=1` will force the alignment of the allocation size to + `max_align_t` to be compliant with the C standard. ## 10) Settings for libtokencap -This library accepts AFL_TOKEN_FILE to indicate the location to which the +This library accepts `AFL_TOKEN_FILE` to indicate the location to which the discovered tokens should be written. ## 11) Third-party variables set by afl-fuzz & other tools @@ -526,33 +527,33 @@ discovered tokens should be written. Several variables are not directly interpreted by afl-fuzz, but are set to optimal values if not already present in the environment: - - By default, LD_BIND_NOW is set to speed up fuzzing by forcing the + - By default, `LD_BIND_NOW` is set to speed up fuzzing by forcing the linker to do all the work before the fork server kicks in. You can - override this by setting LD_BIND_LAZY beforehand, but it is almost + override this by setting `LD_BIND_LAZY` beforehand, but it is almost certainly pointless. - - By default, ASAN_OPTIONS are set to: - + - By default, `ASAN_OPTIONS` are set to: +``` abort_on_error=1 detect_leaks=0 malloc_context_size=0 symbolize=0 allocator_may_return_null=1 - - If you want to set your own options, be sure to include abort_on_error=1 - +``` + If you want to set your own options, be sure to include `abort_on_error=1` - otherwise, the fuzzer will not be able to detect crashes in the tested - app. Similarly, include symbolize=0, since without it, AFL may have + app. Similarly, include `symbolize=0`, since without it, AFL++ may have difficulty telling crashes and hangs apart. - - In the same vein, by default, MSAN_OPTIONS are set to: - + - In the same vein, by default, `MSAN_OPTIONS` are set to: +``` exit_code=86 (required for legacy reasons) abort_on_error=1 symbolize=0 msan_track_origins=0 allocator_may_return_null=1 - - Be sure to include the first one when customizing anything, since some - MSAN versions don't call abort() on error, and we need a way to detect +``` + Be sure to include the first one when customizing anything, since some + MSAN versions don't call `abort()` on error, and we need a way to detect faults. -- cgit 1.4.1 From f810639ab188bf3bbd27fe58fb0b0bb2fe4fbdf0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 31 Oct 2020 14:18:58 +0100 Subject: add our own inline trace-pc-guard --- GNUmakefile.llvm | 5 +- docs/Changelog.md | 3 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 1331 ++++++++++++++++++++++++ src/afl-cc.c | 15 +- 4 files changed, 1351 insertions(+), 3 deletions(-) create mode 100644 instrumentation/SanitizerCoveragePCGUARD.so.cc (limited to 'docs') diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 1f67ea7f..3605b425 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -297,7 +297,7 @@ ifeq "$(TEST_MMAP)" "1" endif PROGS_ALWAYS = ./afl-cc ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o -PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so +PROGS = $(PROGS_ALWAYS) ./afl-llvm-pass.so ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./libLLVMInsTrim.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./afl-llvm-lto-instrumentation.so ./SanitizerCoverageLTO.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -382,6 +382,9 @@ ifeq "$(LLVM_MIN_4_0_1)" "0" endif $(CXX) $(CLANG_CPPFL) -DLLVMInsTrim_EXPORTS -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o +./SanitizerCoveragePCGUARD.so: instrumentation/SanitizerCoveragePCGUARD.so.cc instrumentation/afl-llvm-common.o | test_deps + $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o + ./afl-llvm-lto-instrumentlist.so: instrumentation/afl-llvm-lto-instrumentlist.so.cc instrumentation/afl-llvm-common.o ifeq "$(LLVM_LTO)" "1" $(CXX) $(CLANG_CPPFL) -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o diff --git a/docs/Changelog.md b/docs/Changelog.md index f8f15fc8..798a056f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -40,6 +40,9 @@ sending a mail to . - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore + - we now have our own trace-pc-guard implementation. It is the same as + -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline + and b) works from llvm 10+ on :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc new file mode 100644 index 00000000..97e8d32b --- /dev/null +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -0,0 +1,1331 @@ +//===-- SanitizerCoverage.cpp - coverage instrumentation for sanitizers ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Coverage instrumentation done on LLVM IR level, works with Sanitizers. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +#include "config.h" +#include "debug.h" +#include "afl-llvm-common.h" + +namespace llvm { + +/// This is the ModuleSanitizerCoverage pass used in the new pass manager. The +/// pass instruments functions for coverage, adds initialization calls to the +/// module for trace PC guards and 8bit counters if they are requested, and +/// appends globals to llvm.compiler.used. +class ModuleSanitizerCoveragePass + : public PassInfoMixin { + + public: + explicit ModuleSanitizerCoveragePass( + SanitizerCoverageOptions Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + + } + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + static bool isRequired() { + + return true; + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +// Insert SanitizerCoverage instrumentation. +ModulePass *createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = std::vector(), + const std::vector &BlocklistFiles = + std::vector()); + +} // namespace llvm + +using namespace llvm; + +#define DEBUG_TYPE "sancov" + +static const char *const SanCovTracePCIndirName = + "__sanitizer_cov_trace_pc_indir"; +static const char *const SanCovTracePCName = "__sanitizer_cov_trace_pc"; +static const char *const SanCovTraceCmp1 = "__sanitizer_cov_trace_cmp1"; +static const char *const SanCovTraceCmp2 = "__sanitizer_cov_trace_cmp2"; +static const char *const SanCovTraceCmp4 = "__sanitizer_cov_trace_cmp4"; +static const char *const SanCovTraceCmp8 = "__sanitizer_cov_trace_cmp8"; +static const char *const SanCovTraceConstCmp1 = + "__sanitizer_cov_trace_const_cmp1"; +static const char *const SanCovTraceConstCmp2 = + "__sanitizer_cov_trace_const_cmp2"; +static const char *const SanCovTraceConstCmp4 = + "__sanitizer_cov_trace_const_cmp4"; +static const char *const SanCovTraceConstCmp8 = + "__sanitizer_cov_trace_const_cmp8"; +static const char *const SanCovTraceDiv4 = "__sanitizer_cov_trace_div4"; +static const char *const SanCovTraceDiv8 = "__sanitizer_cov_trace_div8"; +static const char *const SanCovTraceGep = "__sanitizer_cov_trace_gep"; +static const char *const SanCovTraceSwitchName = "__sanitizer_cov_trace_switch"; +static const char *const SanCovModuleCtorTracePcGuardName = + "sancov.module_ctor_trace_pc_guard"; +static const char *const SanCovModuleCtor8bitCountersName = + "sancov.module_ctor_8bit_counters"; +static const char *const SanCovModuleCtorBoolFlagName = + "sancov.module_ctor_bool_flag"; +static const uint64_t SanCtorAndDtorPriority = 2; + +static const char *const SanCovTracePCGuardName = + "__sanitizer_cov_trace_pc_guard"; +static const char *const SanCovTracePCGuardInitName = + "__sanitizer_cov_trace_pc_guard_init"; +static const char *const SanCov8bitCountersInitName = + "__sanitizer_cov_8bit_counters_init"; +static const char *const SanCovBoolFlagInitName = + "__sanitizer_cov_bool_flag_init"; +static const char *const SanCovPCsInitName = "__sanitizer_cov_pcs_init"; + +static const char *const SanCovGuardsSectionName = "sancov_guards"; +static const char *const SanCovCountersSectionName = "sancov_cntrs"; +static const char *const SanCovBoolFlagSectionName = "sancov_bools"; +static const char *const SanCovPCsSectionName = "sancov_pcs"; + +static const char *const SanCovLowestStackName = "__sancov_lowest_stack"; + +static char *skip_nozero; + +/* +static cl::opt ClCoverageLevel( + "sanitizer-coverage-level", + cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " + "3: all blocks and critical edges"), + cl::Hidden, cl::init(3)); + +static cl::opt ClTracePC("sanitizer-coverage-trace-pc", + cl::desc("Experimental pc tracing"), cl::Hidden, + cl::init(false)); + +static cl::opt ClTracePCGuard("sanitizer-coverage-trace-pc-guard", + cl::desc("pc tracing with a guard"), + cl::Hidden, cl::init(true)); + +// If true, we create a global variable that contains PCs of all instrumented +// BBs, put this global into a named section, and pass this section's bounds +// to __sanitizer_cov_pcs_init. +// This way the coverage instrumentation does not need to acquire the PCs +// at run-time. Works with trace-pc-guard, inline-8bit-counters, and +// inline-bool-flag. +static cl::opt ClCreatePCTable("sanitizer-coverage-pc-table", + cl::desc("create a static PC table"), + cl::Hidden, cl::init(false)); + +static cl::opt ClInline8bitCounters( + "sanitizer-coverage-inline-8bit-counters", + cl::desc("increments 8-bit counter for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClInlineBoolFlag( + "sanitizer-coverage-inline-bool-flag", + cl::desc("sets a boolean flag for every edge"), cl::Hidden, + cl::init(false)); + +static cl::opt ClCMPTracing( + "sanitizer-coverage-trace-compares", + cl::desc("Tracing of CMP and similar instructions"), cl::Hidden, + cl::init(false)); + +static cl::opt ClDIVTracing("sanitizer-coverage-trace-divs", + cl::desc("Tracing of DIV instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClGEPTracing("sanitizer-coverage-trace-geps", + cl::desc("Tracing of GEP instructions"), + cl::Hidden, cl::init(false)); + +static cl::opt ClPruneBlocks( + "sanitizer-coverage-prune-blocks", + cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, + cl::init(true)); + +static cl::opt ClStackDepth("sanitizer-coverage-stack-depth", + cl::desc("max stack depth tracing"), + cl::Hidden, cl::init(false)); +*/ +namespace { + +/* +SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { + + SanitizerCoverageOptions Res; + switch (LegacyCoverageLevel) { + + case 0: + Res.CoverageType = SanitizerCoverageOptions::SCK_None; + break; + case 1: + Res.CoverageType = SanitizerCoverageOptions::SCK_Function; + break; + case 2: + Res.CoverageType = SanitizerCoverageOptions::SCK_BB; + break; + case 3: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + break; + case 4: + Res.CoverageType = SanitizerCoverageOptions::SCK_Edge; + Res.IndirectCalls = true; + break; + + } + + return Res; + +} + +*/ + +SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { + + // Sets CoverageType and IndirectCalls. + // SanitizerCoverageOptions CLOpts = getOptions(ClCoverageLevel); + Options.CoverageType = + SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, + // CLOpts.CoverageType); + Options.IndirectCalls = true; // CLOpts.IndirectCalls; + Options.TraceCmp = false; //|= ClCMPTracing; + Options.TraceDiv = false; //|= ClDIVTracing; + Options.TraceGep = false; //|= ClGEPTracing; + Options.TracePC = false; //|= ClTracePC; + Options.TracePCGuard = true; // |= ClTracePCGuard; + Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; + // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; + Options.PCTable = false; //|= ClCreatePCTable; + Options.NoPrune = false; //|= !ClPruneBlocks; + Options.StackDepth = false; //|= ClStackDepth; + if (!Options.TracePCGuard && !Options.TracePC && + !Options.Inline8bitCounters && !Options.StackDepth /*&& + !Options.InlineBoolFlag*/) + Options.TracePCGuard = true; // TracePCGuard is default. + + return Options; + +} + +using DomTreeCallback = function_ref; +using PostDomTreeCallback = + function_ref; + +class ModuleSanitizerCoverage { + + public: + ModuleSanitizerCoverage( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const SpecialCaseList * Allowlist = nullptr, + const SpecialCaseList * Blocklist = nullptr) + : Options(OverrideFromCL(Options)), + Allowlist(Allowlist), + Blocklist(Blocklist) { + + } + + bool instrumentModule(Module &M, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + + private: + void instrumentFunction(Function &F, DomTreeCallback DTCallback, + PostDomTreeCallback PDTCallback); + void InjectCoverageForIndirectCalls(Function & F, + ArrayRef IndirCalls); + void InjectTraceForCmp(Function &F, ArrayRef CmpTraceTargets); + void InjectTraceForDiv(Function & F, + ArrayRef DivTraceTargets); + void InjectTraceForGep(Function & F, + ArrayRef GepTraceTargets); + void InjectTraceForSwitch(Function & F, + ArrayRef SwitchTraceTargets); + bool InjectCoverage(Function &F, ArrayRef AllBlocks, + bool IsLeafFunc = true); + GlobalVariable *CreateFunctionLocalArrayInSection(size_t NumElements, + Function &F, Type *Ty, + const char *Section); + GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, + bool IsLeafFunc = true); + Function *CreateInitCallsForSections(Module &M, const char *CtorName, + const char *InitFunctionName, Type *Ty, + const char *Section); + std::pair CreateSecStartEnd(Module &M, const char *Section, + Type *Ty); + + void SetNoSanitizeMetadata(Instruction *I) { + + I->setMetadata(I->getModule()->getMDKindID("nosanitize"), + MDNode::get(*C, None)); + + } + + std::string getSectionName(const std::string &Section) const; + std::string getSectionStart(const std::string &Section) const; + std::string getSectionEnd(const std::string &Section) const; + FunctionCallee SanCovTracePCIndir; + FunctionCallee SanCovTracePC, SanCovTracePCGuard; + FunctionCallee SanCovTraceCmpFunction[4]; + FunctionCallee SanCovTraceConstCmpFunction[4]; + FunctionCallee SanCovTraceDivFunction[2]; + FunctionCallee SanCovTraceGepFunction; + FunctionCallee SanCovTraceSwitchFunction; + GlobalVariable *SanCovLowestStack; + Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, + *Int16Ty, *Int8Ty, *Int8PtrTy, *Int1Ty, *Int1PtrTy; + Module * CurModule; + std::string CurModuleUniqueId; + Triple TargetTriple; + LLVMContext * C; + const DataLayout *DL; + + GlobalVariable *FunctionGuardArray; // for trace-pc-guard. + GlobalVariable *Function8bitCounterArray; // for inline-8bit-counters. + GlobalVariable *FunctionBoolArray; // for inline-bool-flag. + GlobalVariable *FunctionPCsArray; // for pc-table. + SmallVector GlobalsToAppendToUsed; + SmallVector GlobalsToAppendToCompilerUsed; + + SanitizerCoverageOptions Options; + + const SpecialCaseList *Allowlist; + const SpecialCaseList *Blocklist; + + uint32_t instr = 0; + GlobalVariable *AFLMapPtr = NULL; + ConstantInt * One = NULL; + ConstantInt * Zero = NULL; + +}; + +class ModuleSanitizerCoverageLegacyPass : public ModulePass { + + public: + ModuleSanitizerCoverageLegacyPass( + const SanitizerCoverageOptions &Options = SanitizerCoverageOptions(), + const std::vector &AllowlistFiles = + std::vector(), + const std::vector &BlocklistFiles = + std::vector()) + : ModulePass(ID), Options(Options) { + + if (AllowlistFiles.size() > 0) + Allowlist = SpecialCaseList::createOrDie(AllowlistFiles, + *vfs::getRealFileSystem()); + if (BlocklistFiles.size() > 0) + Blocklist = SpecialCaseList::createOrDie(BlocklistFiles, + *vfs::getRealFileSystem()); + initializeModuleSanitizerCoverageLegacyPassPass( + *PassRegistry::getPassRegistry()); + + } + + bool runOnModule(Module &M) override { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto DTCallback = [this](Function &F) -> const DominatorTree * { + + return &this->getAnalysis(F).getDomTree(); + + }; + + auto PDTCallback = [this](Function &F) -> const PostDominatorTree * { + + return &this->getAnalysis(F) + .getPostDomTree(); + + }; + + return ModuleSancov.instrumentModule(M, DTCallback, PDTCallback); + + } + + static char ID; // Pass identification, replacement for typeid + StringRef getPassName() const override { + + return "ModuleSanitizerCoverage"; + + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + + AU.addRequired(); + AU.addRequired(); + + } + + private: + SanitizerCoverageOptions Options; + + std::unique_ptr Allowlist; + std::unique_ptr Blocklist; + +}; + +} // namespace + +PreservedAnalyses ModuleSanitizerCoveragePass::run(Module & M, + ModuleAnalysisManager &MAM) { + + ModuleSanitizerCoverage ModuleSancov(Options, Allowlist.get(), + Blocklist.get()); + auto &FAM = MAM.getResult(M).getManager(); + auto DTCallback = [&FAM](Function &F) -> const DominatorTree * { + + return &FAM.getResult(F); + + }; + + auto PDTCallback = [&FAM](Function &F) -> const PostDominatorTree * { + + return &FAM.getResult(F); + + }; + + if (ModuleSancov.instrumentModule(M, DTCallback, PDTCallback)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); + +} + +std::pair ModuleSanitizerCoverage::CreateSecStartEnd( + Module &M, const char *Section, Type *Ty) { + + GlobalVariable *SecStart = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionStart(Section)); + SecStart->setVisibility(GlobalValue::HiddenVisibility); + GlobalVariable *SecEnd = new GlobalVariable( + M, Ty->getPointerElementType(), false, GlobalVariable::ExternalLinkage, + nullptr, getSectionEnd(Section)); + SecEnd->setVisibility(GlobalValue::HiddenVisibility); + IRBuilder<> IRB(M.getContext()); + if (!TargetTriple.isOSBinFormatCOFF()) + return std::make_pair(SecStart, SecEnd); + + // Account for the fact that on windows-msvc __start_* symbols actually + // point to a uint64_t before the start of the array. + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); + auto GEP = IRB.CreateGEP(Int8Ty, SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + return std::make_pair(IRB.CreatePointerCast(GEP, Ty), SecEnd); + +} + +Function *ModuleSanitizerCoverage::CreateInitCallsForSections( + Module &M, const char *CtorName, const char *InitFunctionName, Type *Ty, + const char *Section) { + + auto SecStartEnd = CreateSecStartEnd(M, Section, Ty); + auto SecStart = SecStartEnd.first; + auto SecEnd = SecStartEnd.second; + Function *CtorFunc; + std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( + M, CtorName, InitFunctionName, {Ty, Ty}, {SecStart, SecEnd}); + assert(CtorFunc->getName() == CtorName); + + if (TargetTriple.supportsCOMDAT()) { + + // Use comdat to dedup CtorFunc. + CtorFunc->setComdat(M.getOrInsertComdat(CtorName)); + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority, CtorFunc); + + } else { + + appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority); + + } + + if (TargetTriple.isOSBinFormatCOFF()) { + + // In COFF files, if the contructors are set as COMDAT (they are because + // COFF supports COMDAT) and the linker flag /OPT:REF (strip unreferenced + // functions and data) is used, the constructors get stripped. To prevent + // this, give the constructors weak ODR linkage and ensure the linker knows + // to include the sancov constructor. This way the linker can deduplicate + // the constructors but always leave one copy. + CtorFunc->setLinkage(GlobalValue::WeakODRLinkage); + appendToUsed(M, CtorFunc); + + } + + return CtorFunc; + +} + +bool ModuleSanitizerCoverage::instrumentModule( + Module &M, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + setvbuf(stdout, NULL, _IONBF, 0); + if (getenv("AFL_DEBUG")) debug = 1; + + if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { + + SAYF(cCYA "SanitizerCoveragePCGUARD" VERSION cRST "\n"); + + } else + + be_quiet = 1; + + skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); + // scanForDangerousFunctions(&M); + + if (debug) { + + fprintf(stderr, + "SANCOV: covtype:%u indirect:%d stack:%d noprune:%d " + "createtable:%d tracepcguard:%d tracepc:%d\n", + Options.CoverageType, Options.IndirectCalls == true ? 1 : 0, + Options.StackDepth == true ? 1 : 0, Options.NoPrune == true ? 1 : 0, + // Options.InlineBoolFlag == true ? 1 : 0, + Options.PCTable == true ? 1 : 0, + Options.TracePCGuard == true ? 1 : 0, + Options.TracePC == true ? 1 : 0); + + } + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_None) return false; + if (Allowlist && + !Allowlist->inSection("coverage", "src", M.getSourceFileName())) + return false; + if (Blocklist && + Blocklist->inSection("coverage", "src", M.getSourceFileName())) + return false; + C = &(M.getContext()); + DL = &M.getDataLayout(); + CurModule = &M; + CurModuleUniqueId = getUniqueModuleId(CurModule); + TargetTriple = Triple(M.getTargetTriple()); + FunctionGuardArray = nullptr; + Function8bitCounterArray = nullptr; + FunctionBoolArray = nullptr; + FunctionPCsArray = nullptr; + IntptrTy = Type::getIntNTy(*C, DL->getPointerSizeInBits()); + IntptrPtrTy = PointerType::getUnqual(IntptrTy); + Type * VoidTy = Type::getVoidTy(*C); + IRBuilder<> IRB(*C); + Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty()); + Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); + Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty()); + Int1PtrTy = PointerType::getUnqual(IRB.getInt1Ty()); + Int64Ty = IRB.getInt64Ty(); + Int32Ty = IRB.getInt32Ty(); + Int16Ty = IRB.getInt16Ty(); + Int8Ty = IRB.getInt8Ty(); + Int1Ty = IRB.getInt1Ty(); + LLVMContext &Ctx = M.getContext(); + + AFLMapPtr = + new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, + GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); + One = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 1); + Zero = ConstantInt::get(IntegerType::getInt8Ty(Ctx), 0); + + SanCovTracePCIndir = + M.getOrInsertFunction(SanCovTracePCIndirName, VoidTy, IntptrTy); + // Make sure smaller parameters are zero-extended to i64 if required by the + // target ABI. + AttributeList SanCovTraceCmpZeroExtAL; + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceCmpZeroExtAL = + SanCovTraceCmpZeroExtAL.addParamAttribute(*C, 1, Attribute::ZExt); + + SanCovTraceCmpFunction[0] = + M.getOrInsertFunction(SanCovTraceCmp1, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt8Ty(), IRB.getInt8Ty()); + SanCovTraceCmpFunction[1] = + M.getOrInsertFunction(SanCovTraceCmp2, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt16Ty(), IRB.getInt16Ty()); + SanCovTraceCmpFunction[2] = + M.getOrInsertFunction(SanCovTraceCmp4, SanCovTraceCmpZeroExtAL, VoidTy, + IRB.getInt32Ty(), IRB.getInt32Ty()); + SanCovTraceCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceCmp8, VoidTy, Int64Ty, Int64Ty); + + SanCovTraceConstCmpFunction[0] = M.getOrInsertFunction( + SanCovTraceConstCmp1, SanCovTraceCmpZeroExtAL, VoidTy, Int8Ty, Int8Ty); + SanCovTraceConstCmpFunction[1] = M.getOrInsertFunction( + SanCovTraceConstCmp2, SanCovTraceCmpZeroExtAL, VoidTy, Int16Ty, Int16Ty); + SanCovTraceConstCmpFunction[2] = M.getOrInsertFunction( + SanCovTraceConstCmp4, SanCovTraceCmpZeroExtAL, VoidTy, Int32Ty, Int32Ty); + SanCovTraceConstCmpFunction[3] = + M.getOrInsertFunction(SanCovTraceConstCmp8, VoidTy, Int64Ty, Int64Ty); + + { + + AttributeList AL; + AL = AL.addParamAttribute(*C, 0, Attribute::ZExt); + SanCovTraceDivFunction[0] = + M.getOrInsertFunction(SanCovTraceDiv4, AL, VoidTy, IRB.getInt32Ty()); + + } + + SanCovTraceDivFunction[1] = + M.getOrInsertFunction(SanCovTraceDiv8, VoidTy, Int64Ty); + SanCovTraceGepFunction = + M.getOrInsertFunction(SanCovTraceGep, VoidTy, IntptrTy); + SanCovTraceSwitchFunction = + M.getOrInsertFunction(SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy); + + Constant *SanCovLowestStackConstant = + M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); + SanCovLowestStack = dyn_cast(SanCovLowestStackConstant); + if (!SanCovLowestStack) { + + C->emitError(StringRef("'") + SanCovLowestStackName + + "' should not be declared by the user"); + return true; + + } + + SanCovLowestStack->setThreadLocalMode( + GlobalValue::ThreadLocalMode::InitialExecTLSModel); + if (Options.StackDepth && !SanCovLowestStack->isDeclaration()) + SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy)); + + SanCovTracePC = M.getOrInsertFunction(SanCovTracePCName, VoidTy); + SanCovTracePCGuard = + M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, Int32PtrTy); + + for (auto &F : M) + instrumentFunction(F, DTCallback, PDTCallback); + + Function *Ctor = nullptr; + + if (FunctionGuardArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorTracePcGuardName, + SanCovTracePCGuardInitName, Int32PtrTy, + SanCovGuardsSectionName); + if (Function8bitCounterArray) + Ctor = CreateInitCallsForSections(M, SanCovModuleCtor8bitCountersName, + SanCov8bitCountersInitName, Int8PtrTy, + SanCovCountersSectionName); + if (FunctionBoolArray) { + + Ctor = CreateInitCallsForSections(M, SanCovModuleCtorBoolFlagName, + SanCovBoolFlagInitName, Int1PtrTy, + SanCovBoolFlagSectionName); + + } + + if (Ctor && Options.PCTable) { + + auto SecStartEnd = CreateSecStartEnd(M, SanCovPCsSectionName, IntptrPtrTy); + FunctionCallee InitFunction = declareSanitizerInitFunction( + M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy}); + IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); + IRBCtor.CreateCall(InitFunction, {SecStartEnd.first, SecStartEnd.second}); + + } + + // We don't reference these arrays directly in any of our runtime functions, + // so we need to prevent them from being dead stripped. + if (TargetTriple.isOSBinFormatMachO()) appendToUsed(M, GlobalsToAppendToUsed); + appendToCompilerUsed(M, GlobalsToAppendToCompilerUsed); + + if (!be_quiet) { + + if (!instr) + WARNF("No instrumentation targets found."); + else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); + OKF("Instrumented %u locations with no collisions (%s mode).", instr, + modeline); + + } + + } + + return true; + +} + +// True if block has successors and it dominates all of them. +static bool isFullDominator(const BasicBlock *BB, const DominatorTree *DT) { + + if (succ_begin(BB) == succ_end(BB)) return false; + + for (const BasicBlock *SUCC : make_range(succ_begin(BB), succ_end(BB))) { + + if (!DT->dominates(BB, SUCC)) return false; + + } + + return true; + +} + +// True if block has predecessors and it postdominates all of them. +static bool isFullPostDominator(const BasicBlock * BB, + const PostDominatorTree *PDT) { + + if (pred_begin(BB) == pred_end(BB)) return false; + + for (const BasicBlock *PRED : make_range(pred_begin(BB), pred_end(BB))) { + + if (!PDT->dominates(BB, PRED)) return false; + + } + + return true; + +} + +static bool shouldInstrumentBlock(const Function &F, const BasicBlock *BB, + const DominatorTree * DT, + const PostDominatorTree * PDT, + const SanitizerCoverageOptions &Options) { + + // Don't insert coverage for blocks containing nothing but unreachable: we + // will never call __sanitizer_cov() for them, so counting them in + // NumberOfInstrumentedBlocks() might complicate calculation of code coverage + // percentage. Also, unreachable instructions frequently have no debug + // locations. + if (isa(BB->getFirstNonPHIOrDbgOrLifetime())) return false; + + // Don't insert coverage into blocks without a valid insertion point + // (catchswitch blocks). + if (BB->getFirstInsertionPt() == BB->end()) return false; + + if (Options.NoPrune || &F.getEntryBlock() == BB) return true; + + if (Options.CoverageType == SanitizerCoverageOptions::SCK_Function && + &F.getEntryBlock() != BB) + return false; + + // Do not instrument full dominators, or full post-dominators with multiple + // predecessors. + return !isFullDominator(BB, DT) && + !(isFullPostDominator(BB, PDT) && !BB->getSinglePredecessor()); + +} + +// Returns true iff From->To is a backedge. +// A twist here is that we treat From->To as a backedge if +// * To dominates From or +// * To->UniqueSuccessor dominates From +static bool IsBackEdge(BasicBlock *From, BasicBlock *To, + const DominatorTree *DT) { + + if (DT->dominates(To, From)) return true; + if (auto Next = To->getUniqueSuccessor()) + if (DT->dominates(Next, From)) return true; + return false; + +} + +// Prunes uninteresting Cmp instrumentation: +// * CMP instructions that feed into loop backedge branch. +// +// Note that Cmp pruning is controlled by the same flag as the +// BB pruning. +static bool IsInterestingCmp(ICmpInst *CMP, const DominatorTree *DT, + const SanitizerCoverageOptions &Options) { + + if (!Options.NoPrune) + if (CMP->hasOneUse()) + if (auto BR = dyn_cast(CMP->user_back())) + for (BasicBlock *B : BR->successors()) + if (IsBackEdge(BR->getParent(), B, DT)) return false; + return true; + +} + +void ModuleSanitizerCoverage::instrumentFunction( + Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { + + if (F.empty()) return; + if (F.getName().find(".module_ctor") != std::string::npos) + return; // Should not instrument sanitizer init functions. + if (F.getName().startswith("__sanitizer_")) + return; // Don't instrument __sanitizer_* callbacks. + // Don't touch available_externally functions, their actual body is elewhere. + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return; + // Don't instrument MSVC CRT configuration helpers. They may run before normal + // initialization. + if (F.getName() == "__local_stdio_printf_options" || + F.getName() == "__local_stdio_scanf_options") + return; + if (isa(F.getEntryBlock().getTerminator())) return; + // Don't instrument functions using SEH for now. Splitting basic blocks like + // we do for coverage breaks WinEHPrepare. + // FIXME: Remove this when SEH no longer uses landingpad pattern matching. + if (F.hasPersonalityFn() && + isAsynchronousEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + return; + if (Allowlist && !Allowlist->inSection("coverage", "fun", F.getName())) + return; + if (Blocklist && Blocklist->inSection("coverage", "fun", F.getName())) return; + if (Options.CoverageType >= SanitizerCoverageOptions::SCK_Edge) + SplitAllCriticalEdges( + F, CriticalEdgeSplittingOptions().setIgnoreUnreachableDests()); + SmallVector IndirCalls; + SmallVector BlocksToInstrument; + SmallVector CmpTraceTargets; + SmallVector SwitchTraceTargets; + SmallVector DivTraceTargets; + SmallVector GepTraceTargets; + + const DominatorTree * DT = DTCallback(F); + const PostDominatorTree *PDT = PDTCallback(F); + bool IsLeafFunc = true; + + for (auto &BB : F) { + + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) + BlocksToInstrument.push_back(&BB); + for (auto &Inst : BB) { + + if (Options.IndirectCalls) { + + CallBase *CB = dyn_cast(&Inst); + if (CB && !CB->getCalledFunction()) IndirCalls.push_back(&Inst); + + } + + if (Options.TraceCmp) { + + if (ICmpInst *CMP = dyn_cast(&Inst)) + if (IsInterestingCmp(CMP, DT, Options)) + CmpTraceTargets.push_back(&Inst); + if (isa(&Inst)) SwitchTraceTargets.push_back(&Inst); + + } + + if (Options.TraceDiv) + if (BinaryOperator *BO = dyn_cast(&Inst)) + if (BO->getOpcode() == Instruction::SDiv || + BO->getOpcode() == Instruction::UDiv) + DivTraceTargets.push_back(BO); + if (Options.TraceGep) + if (GetElementPtrInst *GEP = dyn_cast(&Inst)) + GepTraceTargets.push_back(GEP); + if (Options.StackDepth) + if (isa(Inst) || + (isa(Inst) && !isa(Inst))) + IsLeafFunc = false; + + } + + } + + InjectCoverage(F, BlocksToInstrument, IsLeafFunc); + InjectCoverageForIndirectCalls(F, IndirCalls); + InjectTraceForCmp(F, CmpTraceTargets); + InjectTraceForSwitch(F, SwitchTraceTargets); + InjectTraceForDiv(F, DivTraceTargets); + InjectTraceForGep(F, GepTraceTargets); + +} + +GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection( + size_t NumElements, Function &F, Type *Ty, const char *Section) { + + ArrayType *ArrayTy = ArrayType::get(Ty, NumElements); + auto Array = new GlobalVariable( + *CurModule, ArrayTy, false, GlobalVariable::PrivateLinkage, + Constant::getNullValue(ArrayTy), "__sancov_gen_"); + + if (TargetTriple.supportsCOMDAT() && !F.isInterposable()) + if (auto Comdat = + GetOrCreateFunctionComdat(F, TargetTriple, CurModuleUniqueId)) + Array->setComdat(Comdat); + Array->setSection(getSectionName(Section)); + Array->setAlignment(Align(DL->getTypeStoreSize(Ty).getFixedSize())); + GlobalsToAppendToUsed.push_back(Array); + GlobalsToAppendToCompilerUsed.push_back(Array); + MDNode *MD = MDNode::get(F.getContext(), ValueAsMetadata::get(&F)); + Array->addMetadata(LLVMContext::MD_associated, *MD); + + return Array; + +} + +GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( + Function &F, ArrayRef AllBlocks) { + + size_t N = AllBlocks.size(); + assert(N); + SmallVector PCs; + IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt()); + for (size_t i = 0; i < N; i++) { + + if (&F.getEntryBlock() == AllBlocks[i]) { + + PCs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 1), IntptrPtrTy)); + + } else { + + PCs.push_back((Constant *)IRB.CreatePointerCast( + BlockAddress::get(AllBlocks[i]), IntptrPtrTy)); + PCs.push_back((Constant *)IRB.CreateIntToPtr( + ConstantInt::get(IntptrTy, 0), IntptrPtrTy)); + + } + + } + + auto *PCArray = CreateFunctionLocalArrayInSection(N * 2, F, IntptrPtrTy, + SanCovPCsSectionName); + PCArray->setInitializer( + ConstantArray::get(ArrayType::get(IntptrPtrTy, N * 2), PCs)); + PCArray->setConstant(true); + + return PCArray; + +} + +void ModuleSanitizerCoverage::CreateFunctionLocalArrays( + Function &F, ArrayRef AllBlocks) { + + if (Options.TracePCGuard) + FunctionGuardArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + + if (Options.Inline8bitCounters) + Function8bitCounterArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int8Ty, SanCovCountersSectionName); + /* + if (Options.InlineBoolFlag) + FunctionBoolArray = CreateFunctionLocalArrayInSection( + AllBlocks.size(), F, Int1Ty, SanCovBoolFlagSectionName); + */ + if (Options.PCTable) FunctionPCsArray = CreatePCArray(F, AllBlocks); + +} + +bool ModuleSanitizerCoverage::InjectCoverage(Function & F, + ArrayRef AllBlocks, + bool IsLeafFunc) { + + if (AllBlocks.empty()) return false; + CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) + InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + return true; + +} + +// On every indirect call we call a run-time function +// __sanitizer_cov_indir_call* with two parameters: +// - callee address, +// - global cache array that contains CacheSize pointers (zero-initialized). +// The cache is used to speed up recording the caller-callee pairs. +// The address of the caller is passed implicitly via caller PC. +// CacheSize is encoded in the name of the run-time function. +void ModuleSanitizerCoverage::InjectCoverageForIndirectCalls( + Function &F, ArrayRef IndirCalls) { + + if (IndirCalls.empty()) return; + assert(Options.TracePC || Options.TracePCGuard || + Options.Inline8bitCounters /*|| Options.InlineBoolFlag*/); + for (auto I : IndirCalls) { + + IRBuilder<> IRB(I); + CallBase & CB = cast(*I); + Value * Callee = CB.getCalledOperand(); + if (isa(Callee)) continue; + IRB.CreateCall(SanCovTracePCIndir, IRB.CreatePointerCast(Callee, IntptrTy)); + + } + +} + +// For every switch statement we insert a call: +// __sanitizer_cov_trace_switch(CondValue, +// {NumCases, ValueSizeInBits, Case0Value, Case1Value, Case2Value, ... }) + +void ModuleSanitizerCoverage::InjectTraceForSwitch( + Function &, ArrayRef SwitchTraceTargets) { + + for (auto I : SwitchTraceTargets) { + + if (SwitchInst *SI = dyn_cast(I)) { + + IRBuilder<> IRB(I); + SmallVector Initializers; + Value * Cond = SI->getCondition(); + if (Cond->getType()->getScalarSizeInBits() > + Int64Ty->getScalarSizeInBits()) + continue; + Initializers.push_back(ConstantInt::get(Int64Ty, SI->getNumCases())); + Initializers.push_back( + ConstantInt::get(Int64Ty, Cond->getType()->getScalarSizeInBits())); + if (Cond->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + Cond = IRB.CreateIntCast(Cond, Int64Ty, false); + for (auto It : SI->cases()) { + + Constant *C = It.getCaseValue(); + if (C->getType()->getScalarSizeInBits() < + Int64Ty->getScalarSizeInBits()) + C = ConstantExpr::getCast(CastInst::ZExt, It.getCaseValue(), Int64Ty); + Initializers.push_back(C); + + } + + llvm::sort(Initializers.begin() + 2, Initializers.end(), + [](const Constant *A, const Constant *B) { + + return cast(A)->getLimitedValue() < + cast(B)->getLimitedValue(); + + }); + + ArrayType *ArrayOfInt64Ty = ArrayType::get(Int64Ty, Initializers.size()); + GlobalVariable *GV = new GlobalVariable( + *CurModule, ArrayOfInt64Ty, false, GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayOfInt64Ty, Initializers), + "__sancov_gen_cov_switch_values"); + IRB.CreateCall(SanCovTraceSwitchFunction, + {Cond, IRB.CreatePointerCast(GV, Int64PtrTy)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForDiv( + Function &, ArrayRef DivTraceTargets) { + + for (auto BO : DivTraceTargets) { + + IRBuilder<> IRB(BO); + Value * A1 = BO->getOperand(1); + if (isa(A1)) continue; + if (!A1->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A1->getType()); + int CallbackIdx = TypeSize == 32 ? 0 : TypeSize == 64 ? 1 : -1; + if (CallbackIdx < 0) continue; + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(SanCovTraceDivFunction[CallbackIdx], + {IRB.CreateIntCast(A1, Ty, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForGep( + Function &, ArrayRef GepTraceTargets) { + + for (auto GEP : GepTraceTargets) { + + IRBuilder<> IRB(GEP); + for (auto I = GEP->idx_begin(); I != GEP->idx_end(); ++I) + if (!isa(*I) && (*I)->getType()->isIntegerTy()) + IRB.CreateCall(SanCovTraceGepFunction, + {IRB.CreateIntCast(*I, IntptrTy, true)}); + + } + +} + +void ModuleSanitizerCoverage::InjectTraceForCmp( + Function &, ArrayRef CmpTraceTargets) { + + for (auto I : CmpTraceTargets) { + + if (ICmpInst *ICMP = dyn_cast(I)) { + + IRBuilder<> IRB(ICMP); + Value * A0 = ICMP->getOperand(0); + Value * A1 = ICMP->getOperand(1); + if (!A0->getType()->isIntegerTy()) continue; + uint64_t TypeSize = DL->getTypeStoreSizeInBits(A0->getType()); + int CallbackIdx = + TypeSize == 8 + ? 0 + : TypeSize == 16 ? 1 + : TypeSize == 32 ? 2 : TypeSize == 64 ? 3 : -1; + if (CallbackIdx < 0) continue; + // __sanitizer_cov_trace_cmp((type_size << 32) | predicate, A0, A1); + auto CallbackFunc = SanCovTraceCmpFunction[CallbackIdx]; + bool FirstIsConst = isa(A0); + bool SecondIsConst = isa(A1); + // If both are const, then we don't need such a comparison. + if (FirstIsConst && SecondIsConst) continue; + // If only one is const, then make it the first callback argument. + if (FirstIsConst || SecondIsConst) { + + CallbackFunc = SanCovTraceConstCmpFunction[CallbackIdx]; + if (SecondIsConst) std::swap(A0, A1); + + } + + auto Ty = Type::getIntNTy(*C, TypeSize); + IRB.CreateCall(CallbackFunc, {IRB.CreateIntCast(A0, Ty, true), + IRB.CreateIntCast(A1, Ty, true)}); + + } + + } + +} + +void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB, + size_t Idx, + bool IsLeafFunc) { + + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + bool IsEntryBB = &BB == &F.getEntryBlock(); + DebugLoc EntryLoc; + if (IsEntryBB) { + + if (auto SP = F.getSubprogram()) + EntryLoc = DebugLoc::get(SP->getScopeLine(), 0, SP); + // Keep static allocas and llvm.localescape calls in the entry block. Even + // if we aren't splitting the block, it's nice for allocas to be before + // calls. + IP = PrepareToSplitEntryBlock(BB, IP); + + } else { + + EntryLoc = IP->getDebugLoc(); + + } + + IRBuilder<> IRB(&*IP); + IRB.SetCurrentDebugLocation(EntryLoc); + if (Options.TracePC) { + + IRB.CreateCall(SanCovTracePC); + // ->setCannotMerge(); // gets the PC using GET_CALLER_PC. + + } + + if (Options.TracePCGuard) { + + /* Get CurLoc */ + + Value *GuardPtr = IRB.CreateIntToPtr( + IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy), + ConstantInt::get(IntptrTy, Idx * 4)), + Int32PtrTy); + + LoadInst *CurLoc = IRB.CreateLoad(GuardPtr); + + /* Load SHM pointer */ + + LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); + + /* Load counter for CurLoc */ + + Value * MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); + LoadInst *Counter = IRB.CreateLoad(MapPtrIdx); + + /* Update bitmap */ + + Value *Incr = IRB.CreateAdd(Counter, One); + + if (skip_nozero == NULL) { + + auto cf = IRB.CreateICmpEQ(Incr, Zero); + auto carry = IRB.CreateZExt(cf, Int8Ty); + Incr = IRB.CreateAdd(Incr, carry); + + } + + IRB.CreateStore(Incr, MapPtrIdx); + + // done :) + + // IRB.CreateCall(SanCovTracePCGuard, Offset)->setCannotMerge(); + // IRB.CreateCall(SanCovTracePCGuard, GuardPtr)->setCannotMerge(); + ++instr; + + } + + if (Options.Inline8bitCounters) { + + auto CounterPtr = IRB.CreateGEP( + Function8bitCounterArray->getValueType(), Function8bitCounterArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int8Ty, CounterPtr); + auto Inc = IRB.CreateAdd(Load, ConstantInt::get(Int8Ty, 1)); + auto Store = IRB.CreateStore(Inc, CounterPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + /* + if (Options.InlineBoolFlag) { + + auto FlagPtr = IRB.CreateGEP( + FunctionBoolArray->getValueType(), FunctionBoolArray, + {ConstantInt::get(IntptrTy, 0), ConstantInt::get(IntptrTy, Idx)}); + auto Load = IRB.CreateLoad(Int1Ty, FlagPtr); + auto ThenTerm = + SplitBlockAndInsertIfThen(IRB.CreateIsNull(Load), &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(ConstantInt::getTrue(Int1Ty), FlagPtr); + SetNoSanitizeMetadata(Load); + SetNoSanitizeMetadata(Store); + + } + + */ + + if (Options.StackDepth && IsEntryBB && !IsLeafFunc) { + + // Check stack depth. If it's the deepest so far, record it. + Module * M = F.getParent(); + Function *GetFrameAddr = Intrinsic::getDeclaration( + M, Intrinsic::frameaddress, + IRB.getInt8PtrTy(M->getDataLayout().getAllocaAddrSpace())); + auto FrameAddrPtr = + IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)}); + auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy); + auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack); + auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack); + auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack); + SetNoSanitizeMetadata(LowestStack); + SetNoSanitizeMetadata(Store); + + } + +} + +std::string ModuleSanitizerCoverage::getSectionName( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatCOFF()) { + + if (Section == SanCovCountersSectionName) return ".SCOV$CM"; + if (Section == SanCovBoolFlagSectionName) return ".SCOV$BM"; + if (Section == SanCovPCsSectionName) return ".SCOVP$M"; + return ".SCOV$GM"; // For SanCovGuardsSectionName. + + } + + if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; + return "__" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionStart( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$start$__DATA$__" + Section; + return "__start___" + Section; + +} + +std::string ModuleSanitizerCoverage::getSectionEnd( + const std::string &Section) const { + + if (TargetTriple.isOSBinFormatMachO()) + return "\1section$end$__DATA$__" + Section; + return "__stop___" + Section; + +} + +char ModuleSanitizerCoverageLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) +INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", + "Pass for instrumenting coverage on functions", false, + false) +ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( + const SanitizerCoverageOptions &Options, + const std::vector &AllowlistFiles, + const std::vector &BlocklistFiles) { + + return new ModuleSanitizerCoverageLegacyPass(Options, AllowlistFiles, + BlocklistFiles); + +} + +static void registerPCGUARDPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + + auto p = new ModuleSanitizerCoverageLegacyPass(); + PM.add(p); + +} + +static RegisterStandardPasses RegisterCompTransPass( + PassManagerBuilder::EP_OptimizerLast, registerPCGUARDPass); + +static RegisterStandardPasses RegisterCompTransPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerPCGUARDPass); + diff --git a/src/afl-cc.c b/src/afl-cc.c index c516dc4c..1a7a837f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -501,11 +501,22 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { -#if LLVM_MAJOR >= 4 +#if LLVM_MAJOR >= 10 + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = - "-fsanitize-coverage=trace-pc-guard"; // edge coverage by default + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); #else + #if LLVM_MAJOR >= 4 + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to llvm 10+ for " + "enhanced version.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + #else FATAL("pcguard instrumentation requires llvm 4.0.1+"); + #endif #endif } else { -- cgit 1.4.1 From 5ecc4ae0ab8af9def83e10c7b5df4cbe1dff997c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 31 Oct 2020 18:53:35 +0100 Subject: add helper script repo --- README.md | 3 ++- docs/sister_projects.md | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/README.md b/README.md index e9d0f826..7c3b6ecf 100644 --- a/README.md +++ b/README.md @@ -700,14 +700,15 @@ Distributed execution: * [afl-in-the-cloud](https://github.com/abhisek/afl-in-the-cloud) - another script for running AFL in AWS. Deployment, management, monitoring, reporting + * [afl-utils](https://gitlab.com/rc0r/afl-utils) - a set of utilities for automatic processing/analysis of crashes and reducing the number of test cases. * [afl-other-arch](https://github.com/shellphish/afl-other-arch) - is a set of patches and scripts for easily adding support for various non-x86 architectures for AFL. * [afl-trivia](https://github.com/bnagy/afl-trivia) - a few small scripts to simplify the management of AFL. * [afl-monitor](https://github.com/reflare/afl-monitor) - a script for monitoring AFL. * [afl-manager](https://github.com/zx1340/afl-manager) - a web server on Python for managing multi-afl. * [afl-remote](https://github.com/block8437/afl-remote) - a web server for the remote management of AFL instances. + * [afl-extras](https://github.com/fekir/afl-extras) - shell scripts to parallelize afl-tmin, startup, and data collection. Crash processing - * [afl-utils](https://gitlab.com/rc0r/afl-utils) - a set of utilities for automatic processing/analysis of crashes and reducing the number of test cases. * [afl-crash-analyzer](https://github.com/floyd-fuh/afl-crash-analyzer) - another crash analyzer for AFL. * [fuzzer-utils](https://github.com/ThePatrickStar/fuzzer-utils) - a set of scripts for the analysis of results. * [atriage](https://github.com/Ayrx/atriage) - a simple triage tool. diff --git a/docs/sister_projects.md b/docs/sister_projects.md index 640e59f7..5cb3a102 100644 --- a/docs/sister_projects.md +++ b/docs/sister_projects.md @@ -119,10 +119,18 @@ Simplifies the triage of discovered crashes, start parallel instances, etc. https://github.com/rc0r/afl-utils +### AFL crash analyzer (floyd) + Another crash triage tool: https://github.com/floyd-fuh/afl-crash-analyzer +### afl-extras (fekir) + +Collect data, parallel afl-tmin, startup scripts. + +https://github.com/fekir/afl-extras + ### afl-fuzzing-scripts (Tobias Ospelt) Simplifies starting up multiple parallel AFL jobs. -- cgit 1.4.1 From 0fd98ae8b070b05a72b2c47a76f4ea145f9d51c2 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 1 Nov 2020 21:34:08 +0100 Subject: added mutation introspection make target --- GNUmakefile | 6 + README.md | 1 + docs/Changelog.md | 2 + include/afl-fuzz.h | 6 + include/alloc-inl.h | 36 ++ instrumentation/SanitizerCoveragePCGUARD.so.cc | 14 +- src/afl-fuzz-bitmap.c | 11 + src/afl-fuzz-extras.c | 16 +- src/afl-fuzz-one.c | 503 ++++++++++++++++++++++++- src/afl-fuzz.c | 17 + 10 files changed, 592 insertions(+), 20 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index c8d155e4..764c9baa 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -110,6 +110,11 @@ ifdef PROFILING LDFLAGS += -pg endif +ifdef INTROSPECTION + $(info Compiling with introspection documentation) + CFLAGS_OPT += -DINTROSPECTION=1 +endif + ifneq "$(shell uname -m)" "x86_64" ifneq "$(patsubst i%86,i386,$(shell uname -m))" "i386" ifneq "$(shell uname -m)" "amd64" @@ -348,6 +353,7 @@ help: @echo ASAN_BUILD - compiles with memory sanitizer for debug purposes @echo DEBUG - no optimization, -ggdb3, all warnings and -Werror @echo PROFILING - compile afl-fuzz with profiling information + @echo INTROSPECTION - compile afl-fuzz with mutation introspection @echo NO_PYTHON - disable python support @echo NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing @echo AFL_NO_X86 - if compiling on non-intel/amd platforms diff --git a/README.md b/README.md index 7c3b6ecf..d954a236 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ These build options exist: * ASAN_BUILD - compiles with memory sanitizer for debug purposes * DEBUG - no optimization, -ggdb3, all warnings and -Werror * PROFILING - compile with profiling information (gprof) +* INTROSPECTION - compile afl-fuzz with mutation introspection * NO_PYTHON - disable python support * NO_SPLICING - disables splicing mutation in afl-fuzz, not recommended for normal fuzzing * AFL_NO_X86 - if compiling on non-intel/amd platforms diff --git a/docs/Changelog.md b/docs/Changelog.md index 798a056f..f11a1178 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,8 @@ sending a mail to . skipped. They are used for splicing though. - set the default power schedule to the superiour "seek" schedule - added NO_SPLICING compile option and makefile define + - added INTROSPECTION make target that writes all mutations to + out/NAME/introspection.txt - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 85b31795..5ff7672b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -717,6 +717,12 @@ typedef struct afl_state { * is too large) */ struct queue_entry **q_testcase_cache; +#ifdef INTROSPECTION + char mutation[8072]; + char m_tmp[4096]; + FILE *introspection_file; +#endif + } afl_state_t; struct custom_mutator { diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 36e47810..d7aa51a7 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -708,6 +708,42 @@ static inline void *afl_realloc(void **buf, size_t size_needed) { } +/* afl_realloc_exact uses afl alloc buffers but sets it to a specific size */ + +static inline void *afl_realloc_exact(void **buf, size_t size_needed) { + + struct afl_alloc_buf *new_buf = NULL; + + size_t current_size = 0; + + if (likely(*buf)) { + + /* the size is always stored at buf - 1*size_t */ + new_buf = (struct afl_alloc_buf *)afl_alloc_bufptr(*buf); + current_size = new_buf->complete_size; + + } + + size_needed += AFL_ALLOC_SIZE_OFFSET; + + /* No need to realloc */ + if (unlikely(current_size == size_needed)) { return *buf; } + + /* alloc */ + new_buf = (struct afl_alloc_buf *)realloc(new_buf, size_needed); + if (unlikely(!new_buf)) { + + *buf = NULL; + return NULL; + + } + + new_buf->complete_size = size_needed; + *buf = (void *)(new_buf->buf); + return *buf; + +} + static inline void afl_free(void *buf) { if (buf) { free(afl_alloc_bufptr(buf)); } diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 97e8d32b..2f87e4f9 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -247,13 +247,13 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { Options.CoverageType = SanitizerCoverageOptions::SCK_Edge; // std::max(Options.CoverageType, // CLOpts.CoverageType); - Options.IndirectCalls = true; // CLOpts.IndirectCalls; - Options.TraceCmp = false; //|= ClCMPTracing; - Options.TraceDiv = false; //|= ClDIVTracing; - Options.TraceGep = false; //|= ClGEPTracing; - Options.TracePC = false; //|= ClTracePC; - Options.TracePCGuard = true; // |= ClTracePCGuard; - Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; + Options.IndirectCalls = true; // CLOpts.IndirectCalls; + Options.TraceCmp = false; //|= ClCMPTracing; + Options.TraceDiv = false; //|= ClDIVTracing; + Options.TraceGep = false; //|= ClGEPTracing; + Options.TracePC = false; //|= ClTracePC; + Options.TracePCGuard = true; // |= ClTracePCGuard; + Options.Inline8bitCounters = 0; //|= ClInline8bitCounters; // Options.InlineBoolFlag = 0; //|= ClInlineBoolFlag; Options.PCTable = false; //|= ClCreatePCTable; Options.NoPrune = false; //|= !ClPruneBlocks; diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 2653b9fd..735420c3 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -587,6 +587,11 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { add_to_queue(afl, queue_fn, len, 0); +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); +#endif + if (hnb == 2) { afl->queue_top->has_new_cov = 1; @@ -659,6 +664,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { } ++afl->unique_tmouts; +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); +#endif /* Before saving, we make sure that it's a genuine hang by re-running the target with a more generous timeout (unless the default timeout @@ -742,6 +750,9 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif /* ^!SIMPLE_FILES */ ++afl->unique_crashes; +#ifdef INTROSPECTION + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); +#endif if (unlikely(afl->infoexec)) { // if the user wants to be informed on new crashes - do that diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index adec986e..171cce96 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -423,8 +423,8 @@ void dedup_extras(afl_state_t *afl) { } if (afl->extras_cnt != orig_cnt) - afl->extras = ck_realloc((void **)&afl->extras, - afl->extras_cnt * sizeof(struct extra_data)); + afl->extras = afl_realloc_exact( + (void **)&afl->extras, afl->extras_cnt * sizeof(struct extra_data)); } @@ -462,16 +462,8 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - if (afl->extras) { - - afl->extras = ck_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); - - } else { - - afl->extras = ck_alloc((afl->extras_cnt + 1) * sizeof(struct extra_data)); - - } + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); if (unlikely(!afl->extras)) { PFATAL("alloc"); } diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0f3393d2..5337b7f8 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -609,6 +609,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT1 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -718,6 +723,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT2 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -747,6 +757,11 @@ u8 fuzz_one_original(afl_state_t *afl) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT4 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -802,6 +817,11 @@ u8 fuzz_one_original(afl_state_t *afl) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT8 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -889,6 +909,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT16 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -927,6 +952,11 @@ u8 fuzz_one_original(afl_state_t *afl) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s FLIP_BIT32 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -985,6 +1015,11 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1001,6 +1036,11 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH8- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1065,6 +1105,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1079,6 +1124,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1097,6 +1147,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16+BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1111,6 +1166,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH16-BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1174,6 +1234,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+ %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1188,6 +1253,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32- %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1206,6 +1276,11 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32+BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1220,6 +1295,11 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s ARITH32-BE %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1287,6 +1367,11 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING8 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -1342,6 +1427,11 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING16 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1358,6 +1448,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); +#endif + *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1421,6 +1516,11 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s INTERESTING32 %u %u", + afl->queue_cur->fname, i, j); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1437,6 +1537,11 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); +#endif + *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1510,6 +1615,12 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1557,6 +1668,12 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -1614,6 +1731,12 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->a_extras[j].data); +#endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -1675,7 +1798,7 @@ custom_mutator_stage: for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { - struct queue_entry *target; + struct queue_entry *target = NULL; u32 tid; u8 * new_buf = NULL; u32 target_len = 0; @@ -1717,6 +1840,12 @@ custom_mutator_stage: if (mutated_size > 0) { +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s CUSTOM %s", + afl->queue_cur->fname, + target != NULL ? (char *)target->fname : "none"); +#endif + if (common_fuzz_stuff(afl, mutated_buf, (u32)mutated_size)) { goto abandon_entry; @@ -1866,6 +1995,11 @@ havoc_stage: afl->stage_cur_val = use_stacking; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s HAVOC %u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { if (afl->custom_mutators_count) { @@ -1909,6 +2043,10 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); break; @@ -1916,6 +2054,10 @@ havoc_stage: /* Set byte to interesting value. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1928,11 +2070,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); @@ -1948,11 +2098,19 @@ havoc_stage: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); @@ -1964,6 +2122,10 @@ havoc_stage: /* Randomly subtract from byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8-"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); break; @@ -1971,6 +2133,10 @@ havoc_stage: /* Randomly add to byte. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH8+"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); break; @@ -1984,6 +2150,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -1991,6 +2161,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -2008,6 +2183,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2015,6 +2194,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); @@ -2032,6 +2216,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { @@ -2039,6 +2227,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -2056,6 +2249,10 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+_%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { @@ -2063,6 +2260,11 @@ havoc_stage: u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+BE_%u_%u", pos, + num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); @@ -2076,6 +2278,10 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); break; @@ -2095,6 +2301,11 @@ havoc_stage: del_from = rand_below(afl, temp_len - del_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL_%u_%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2128,6 +2339,12 @@ havoc_stage: clone_to = rand_below(afl, temp_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + actually_clone ? "clone" : "insert", clone_from, clone_to, + clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); if (unlikely(!new_buf)) { PFATAL("alloc"); } @@ -2181,12 +2398,23 @@ havoc_stage: if (copy_from != copy_to) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -2222,6 +2450,12 @@ havoc_stage: if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2236,6 +2470,12 @@ havoc_stage: if ((s32)extra_len > temp_len) { break; } insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2258,12 +2498,23 @@ havoc_stage: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, + ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } @@ -2324,6 +2575,12 @@ havoc_stage: copy_from = rand_below(afl, new_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, new_buf + copy_from, copy_len); } else { @@ -2340,6 +2597,12 @@ havoc_stage: temp_len + clone_len + 1); if (unlikely(!temp_buf)) { PFATAL("alloc"); } +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif /* Head */ memcpy(temp_buf, out_buf, clone_to); @@ -2755,6 +3018,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT1 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2864,6 +3131,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur); FLIP_BIT(out_buf, afl->stage_cur + 1); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT2 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2893,6 +3164,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { FLIP_BIT(out_buf, afl->stage_cur + 2); FLIP_BIT(out_buf, afl->stage_cur + 3); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT4 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } FLIP_BIT(out_buf, afl->stage_cur); @@ -2948,6 +3223,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { out_buf[afl->stage_cur] ^= 0xFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT8 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } /* We also use this stage to pull off a simple trick: we identify @@ -3035,6 +3314,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u16 *)(out_buf + i) ^= 0xFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT16 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3073,6 +3356,10 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { *(u32 *)(out_buf + i) ^= 0xFFFFFFFF; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_FLIP_BIT32 %u", + afl->queue_cur->fname, afl->stage_cur); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3131,6 +3418,10 @@ skip_bitflip: afl->stage_cur_val = j; out_buf[i] = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3147,6 +3438,10 @@ skip_bitflip: afl->stage_cur_val = -j; out_buf[i] = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH8- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3211,6 +3506,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3225,6 +3524,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH16- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3243,6 +3546,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16+BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3257,6 +3564,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u16 *)(out_buf + i) = SWAP16(SWAP16(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH16-BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3320,6 +3631,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = orig + j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32+ %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3334,6 +3649,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = orig - j; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_ARITH32- %u %u", + afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3352,6 +3671,10 @@ skip_bitflip: afl->stage_cur_val = j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) + j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32+BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3366,6 +3689,10 @@ skip_bitflip: afl->stage_cur_val = -j; *(u32 *)(out_buf + i) = SWAP32(SWAP32(orig) - j); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_ARITH32-BE %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3433,6 +3760,10 @@ skip_arith: afl->stage_cur_val = interesting_8[j]; out_buf[i] = interesting_8[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING8 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } out_buf[i] = orig; @@ -3488,6 +3819,10 @@ skip_arith: *(u16 *)(out_buf + i) = interesting_16[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3504,6 +3839,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING16BE %u %u", afl->queue_cur->fname, i, j); +#endif *(u16 *)(out_buf + i) = SWAP16(interesting_16[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3567,6 +3906,10 @@ skip_arith: *(u32 *)(out_buf + i) = interesting_32[j]; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32 %u %u", afl->queue_cur->fname, i, j); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3583,6 +3926,10 @@ skip_arith: afl->stage_val_type = STAGE_VAL_BE; +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_INTERESTING32BE %u %u", afl->queue_cur->fname, i, j); +#endif *(u32 *)(out_buf + i) = SWAP32(interesting_32[j]); if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3656,6 +4003,11 @@ skip_interest: last_len = afl->extras[j].len; memcpy(out_buf + i, afl->extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3703,6 +4055,11 @@ skip_interest: /* Copy tail */ memcpy(ex_tmp + i + afl->extras[j].len, out_buf + i, len - i); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, + afl->extras[j].data); +#endif if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -3759,6 +4116,11 @@ skip_user_extras: last_len = afl->a_extras[j].len; memcpy(out_buf + i, afl->a_extras[j].data, last_len); +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), + "%s MOPT_AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, + i, j, afl->a_extras[j].data); +#endif if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -3885,6 +4247,11 @@ pacemaker_fuzzing: } +#ifdef INTROSPECTION + snprintf(afl->mutation, sizeof(afl->mutation), "%s MOPT_HAVOC %u", + afl->queue_cur->fname, use_stacking); +#endif + for (i = 0; i < use_stacking; ++i) { switch (select_algorithm(afl)) { @@ -3893,6 +4260,10 @@ pacemaker_fuzzing: /* Flip a single bit somewhere. Spooky! */ FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); MOpt_globals.cycles_v2[STAGE_FLIP1] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 1: @@ -3901,6 +4272,10 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet); FLIP_BIT(out_buf, temp_len_puppet + 1); MOpt_globals.cycles_v2[STAGE_FLIP2] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT2"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 2: @@ -3911,24 +4286,40 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet + 2); FLIP_BIT(out_buf, temp_len_puppet + 3); MOpt_globals.cycles_v2[STAGE_FLIP4] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT4"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 3: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] ^= 0xFF; MOpt_globals.cycles_v2[STAGE_FLIP8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 4: if (temp_len < 8) { break; } *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) ^= 0xFFFF; MOpt_globals.cycles_v2[STAGE_FLIP16] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT16"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 5: if (temp_len < 8) { break; } *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) ^= 0xFFFFFFFF; MOpt_globals.cycles_v2[STAGE_FLIP32] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT32"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 6: @@ -3937,6 +4328,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); MOpt_globals.cycles_v2[STAGE_ARITH8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH+-"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 7: @@ -3946,11 +4341,20 @@ pacemaker_fuzzing: u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) - num); @@ -3960,12 +4364,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16+%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH16BE+%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + pos) = SWAP16(SWAP16(*(u16 *)(out_buf + pos)) + num); @@ -3980,12 +4393,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32-%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE-%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) - num); @@ -3996,12 +4418,21 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { u32 pos = rand_below(afl, temp_len - 3); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32+%u", pos); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH32BE+%u-%u", + pos, num); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + pos) = SWAP32(SWAP32(*(u32 *)(out_buf + pos)) + num); @@ -4016,6 +4447,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; MOpt_globals.cycles_v2[STAGE_INTEREST8] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 10: @@ -4023,12 +4458,20 @@ pacemaker_fuzzing: if (temp_len < 8) { break; } if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING16BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16(interesting_16[rand_below( afl, sizeof(interesting_16) >> 1)]); @@ -4045,12 +4488,20 @@ pacemaker_fuzzing: if (rand_below(afl, 2)) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING32BE"); + strcat(afl->mutation, afl->m_tmp); +#endif *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32(interesting_32[rand_below( afl, sizeof(interesting_32) >> 2)]); @@ -4068,6 +4519,10 @@ pacemaker_fuzzing: out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); MOpt_globals.cycles_v2[STAGE_RANDOMBYTE] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); + strcat(afl->mutation, afl->m_tmp); +#endif break; case 13: { @@ -4091,6 +4546,11 @@ pacemaker_fuzzing: temp_len -= del_len; MOpt_globals.cycles_v2[STAGE_DELETEBYTE] += 1; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u%u", del_from, + del_len); + strcat(afl->mutation, afl->m_tmp); +#endif break; } @@ -4120,6 +4580,12 @@ pacemaker_fuzzing: clone_to = rand_below(afl, temp_len); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), " CLONE_%s_%u_%u_%u", + actually_clone ? "clone" : "insert", clone_from, + clone_to, clone_len); + strcat(afl->mutation, afl->m_tmp); +#endif new_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), temp_len + clone_len); if (unlikely(!new_buf)) { PFATAL("alloc"); } @@ -4175,12 +4641,24 @@ pacemaker_fuzzing: if (copy_from != copy_to) { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_COPY_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memmove(out_buf + copy_to, out_buf + copy_from, copy_len); } } else { +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " OVERWRITE_FIXED_%u_%u_%u", copy_from, copy_to, + copy_len); + strcat(afl->mutation, afl->m_tmp); +#endif memset(out_buf + copy_to, rand_below(afl, 2) ? rand_below(afl, 256) : out_buf[rand_below(afl, temp_len)], @@ -4212,6 +4690,12 @@ pacemaker_fuzzing: if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -4225,6 +4709,12 @@ pacemaker_fuzzing: if (extra_len > (u32)temp_len) break; u32 insert_at = rand_below(afl, temp_len - extra_len + 1); +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, + afl->a_extras[use_extra].data); + strcat(afl->mutation, afl->m_tmp); +#endif memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -4254,12 +4744,23 @@ pacemaker_fuzzing: use_extra = rand_below(afl, afl->a_extras_cnt); extra_len = afl->a_extras[use_extra].len; ptr = afl->a_extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " AUTO_EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, + ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } else { use_extra = rand_below(afl, afl->extras_cnt); extra_len = afl->extras[use_extra].len; ptr = afl->extras[use_extra].data; +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " EXTRA_INSERT_%u_%u_%s", insert_at, extra_len, ptr); + strcat(afl->mutation, afl->m_tmp); +#endif } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index cad26841..575e6b74 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -236,6 +236,10 @@ static void usage(u8 *argv0, int more_help) { SAYF("Compiled with PROFILING\n\n"); #endif +#ifdef INTROSPECTION + SAYF("Compiled with INTROSPECTION\n\n"); +#endif + #ifdef _DEBUG SAYF("Compiled with _DEBUG\n\n"); #endif @@ -1462,6 +1466,19 @@ int main(int argc, char **argv_orig, char **envp) { u32 prev_queued_paths = 0; u8 skipped_fuzz; + #ifdef INTROSPECTION + char ifn[4096]; + snprintf(ifn, sizeof(ifn), "%s/introspection.txt", afl->out_dir); + if ((afl->introspection_file = fopen(ifn, "w")) == NULL) { + + PFATAL("could not create '%s'", ifn); + + } + + setvbuf(afl->introspection_file, NULL, _IONBF, 0); + OKF("Writing mutation introspection to '%s'", ifn); + #endif + while (likely(!afl->stop_soon)) { cull_queue(afl); -- cgit 1.4.1 From 416e01d3c6277988ed39e67f9404bd7a6034820b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 2 Nov 2020 11:04:35 +0100 Subject: match mopt to havoc --- docs/Changelog.md | 6 +- include/afl-fuzz.h | 3 +- src/afl-fuzz-one.c | 209 ++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 164 insertions(+), 54 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f11a1178..50c1d48a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -21,11 +21,11 @@ sending a mail to . a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - statsd support by Edznux, thanks a lot! + - rpc.statsd support by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - - allow up to 4 -x command line options + - allow up to 4 times the -x command line option - loaded extras now have a duplicate protection - If test cases are too large we do a partial read on the maximum supported size @@ -33,7 +33,7 @@ sending a mail to . for fuzzing but still be used for splicing - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - - set the default power schedule to the superiour "seek" schedule + - update MOpt for expanded havoc modes - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 5ff7672b..e59d5f90 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -231,7 +231,7 @@ enum { }; -#define operator_num 18 +#define operator_num 19 #define swarm_num 5 #define period_core 500000 @@ -247,6 +247,7 @@ enum { #define STAGE_OverWrite75 15 #define STAGE_OverWriteExtra 16 #define STAGE_InsertExtra 17 +#define STAGE_Splice 18 #define period_pilot 50000 enum { diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 5337b7f8..e4016773 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -29,11 +29,9 @@ /* MOpt */ -static int select_algorithm(afl_state_t *afl) { +static int select_algorithm(afl_state_t *afl, u32 max_algorithm) { - int i_puppet, j_puppet = 0, operator_number = operator_num; - - if (!afl->extras_cnt && !afl->a_extras_cnt) operator_number -= 2; + int i_puppet, j_puppet = 0, operator_number = max_algorithm; double range_sele = (double)afl->probability_now[afl->swarm_now][operator_number - 1]; @@ -502,6 +500,8 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && !afl->disable_trim)) { + u32 old_len = afl->queue_cur->len; + u8 res = trim_case(afl, afl->queue_cur, in_buf); orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); @@ -524,6 +524,9 @@ u8 fuzz_one_original(afl_state_t *afl) { len = afl->queue_cur->len; + /* maybe current entry is not ready for splicing anymore */ + if (unlikely(len <= 4 && old_len > 4)) afl->ready_for_splicing_count--; + } memcpy(out_buf, in_buf, len); @@ -537,7 +540,7 @@ u8 fuzz_one_original(afl_state_t *afl) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); - if (unlikely(perf_score == 0)) { goto abandon_entry; } + if (unlikely(perf_score <= 0)) { goto abandon_entry; } if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { @@ -1976,7 +1979,7 @@ havoc_stage: /* add expensive havoc cases here, they are activated after a full cycle without finds happened */ - r_max += 1; + r_max++; } @@ -1985,7 +1988,7 @@ havoc_stage: /* add expensive havoc cases here if there is no findings in the last 5s */ - r_max += 1; + r_max++; } @@ -2396,7 +2399,7 @@ havoc_stage: if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), @@ -2445,11 +2448,10 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->a_extras_cnt); u32 extra_len = afl->a_extras[use_extra].len; - u32 insert_at; if ((s32)extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " AUTO_EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, @@ -2465,11 +2467,10 @@ havoc_stage: u32 use_extra = rand_below(afl, afl->extras_cnt); u32 extra_len = afl->extras[use_extra].len; - u32 insert_at; if ((s32)extra_len > temp_len) { break; } - insert_at = rand_below(afl, temp_len - extra_len + 1); + u32 insert_at = rand_below(afl, temp_len - extra_len + 1); #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " EXTRA_OVERWRITE_%u_%u_%s", insert_at, extra_len, @@ -2816,7 +2817,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { possibly skip to them at the expense of already-fuzzed or non-favored cases. */ - if ((afl->queue_cur->was_fuzzed || !afl->queue_cur->favored) && + if (((afl->queue_cur->was_fuzzed > 0 || afl->queue_cur->fuzz_level > 0) || + !afl->queue_cur->favored) && rand_below(afl, 100) < SKIP_TO_NEW_PROB) { return 1; @@ -2831,13 +2833,14 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { The odds of skipping stuff are higher for already-fuzzed inputs and lower for never-fuzzed entries. */ - if (afl->queue_cycle > 1 && !afl->queue_cur->was_fuzzed) { + if (afl->queue_cycle > 1 && + (afl->queue_cur->fuzz_level == 0 || afl->queue_cur->was_fuzzed)) { - if (rand_below(afl, 100) < SKIP_NFAV_NEW_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_NEW_PROB)) { return 1; } } else { - if (rand_below(afl, 100) < SKIP_NFAV_OLD_PROB) { return 1; } + if (likely(rand_below(afl, 100) < SKIP_NFAV_OLD_PROB)) { return 1; } } @@ -2856,16 +2859,19 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Map the test case into memory. */ orig_in = in_buf = queue_testcase_get(afl, afl->queue_cur); len = afl->queue_cur->len; + out_buf = afl_realloc(AFL_BUF_PARAM(out), len); if (unlikely(!out_buf)) { PFATAL("alloc"); } + afl->subseq_tmouts = 0; + afl->cur_depth = afl->queue_cur->depth; /******************************************* * CALIBRATION (only if failed earlier on) * *******************************************/ - if (afl->queue_cur->cal_failed) { + if (unlikely(afl->queue_cur->cal_failed)) { u8 res = FSRV_RUN_TMOUT; @@ -2897,7 +2903,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { * TRIMMING * ************/ - if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done) { + if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done && + !afl->disable_trim)) { u32 old_len = afl->queue_cur->len; @@ -2939,6 +2946,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { else orig_perf = perf_score = calculate_score(afl, afl->queue_cur); + if (unlikely(perf_score <= 0)) { goto abandon_entry; } + if (unlikely(afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized)) { if (input_to_state_stage(afl, in_buf, out_buf, len, @@ -2968,8 +2977,8 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { this entry ourselves (was_fuzzed), or if it has gone through deterministic testing in earlier, resumed runs (passed_det). */ - if (afl->skip_deterministic || afl->queue_cur->was_fuzzed || - afl->queue_cur->passed_det) { + if (likely(afl->skip_deterministic || afl->queue_cur->was_fuzzed || + afl->queue_cur->passed_det)) { goto havoc_stage; @@ -2978,8 +2987,9 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { /* Skip deterministic fuzzing if exec path checksum puts this out of scope for this main instance. */ - if (afl->main_node_max && (afl->queue_cur->exec_cksum % afl->main_node_max) != - afl->main_node_id - 1) { + if (unlikely(afl->main_node_max && + (afl->queue_cur->exec_cksum % afl->main_node_max) != + afl->main_node_id - 1)) { goto havoc_stage; @@ -4008,6 +4018,7 @@ skip_interest: "%s MOPT_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, afl->extras[j].data); #endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -4060,6 +4071,7 @@ skip_interest: "%s MOPT_EXTRAS insert %u %u:%s", afl->queue_cur->fname, i, j, afl->extras[j].data); #endif + if (common_fuzz_stuff(afl, ex_tmp, len + afl->extras[j].len)) { goto abandon_entry; @@ -4099,7 +4111,8 @@ skip_user_extras: afl->stage_cur_byte = i; - for (j = 0; j < MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); ++j) { + u32 min_extra_len = MIN(afl->a_extras_cnt, (u32)USE_AUTO_EXTRAS); + for (j = 0; j < min_extra_len; ++j) { /* See the comment in the earlier code; extras are sorted by size. */ @@ -4121,6 +4134,7 @@ skip_user_extras: "%s MOPT_AUTO_EXTRAS overwrite %u %u:%s", afl->queue_cur->fname, i, j, afl->a_extras[j].data); #endif + if (common_fuzz_stuff(afl, out_buf, len)) { goto abandon_entry; } ++afl->stage_cur; @@ -4234,6 +4248,19 @@ pacemaker_fuzzing: havoc_queued = afl->queued_paths; + u32 r_max; + + r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0); + + if (unlikely(afl->expand_havoc && afl->ready_for_splicing_count > 1)) { + + /* add expensive havoc cases here, they are activated after a full + cycle without finds happened */ + + ++r_max; + + } + for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) { @@ -4254,12 +4281,12 @@ pacemaker_fuzzing: for (i = 0; i < use_stacking; ++i) { - switch (select_algorithm(afl)) { + switch (select_algorithm(afl, r_max)) { case 0: /* Flip a single bit somewhere. Spooky! */ FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); - MOpt_globals.cycles_v2[STAGE_FLIP1] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP1]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT1"); strcat(afl->mutation, afl->m_tmp); @@ -4271,7 +4298,7 @@ pacemaker_fuzzing: temp_len_puppet = rand_below(afl, (temp_len << 3) - 1); FLIP_BIT(out_buf, temp_len_puppet); FLIP_BIT(out_buf, temp_len_puppet + 1); - MOpt_globals.cycles_v2[STAGE_FLIP2] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP2]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT2"); strcat(afl->mutation, afl->m_tmp); @@ -4285,7 +4312,7 @@ pacemaker_fuzzing: FLIP_BIT(out_buf, temp_len_puppet + 1); FLIP_BIT(out_buf, temp_len_puppet + 2); FLIP_BIT(out_buf, temp_len_puppet + 3); - MOpt_globals.cycles_v2[STAGE_FLIP4] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP4]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT4"); strcat(afl->mutation, afl->m_tmp); @@ -4295,7 +4322,7 @@ pacemaker_fuzzing: case 3: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] ^= 0xFF; - MOpt_globals.cycles_v2[STAGE_FLIP8] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT8"); strcat(afl->mutation, afl->m_tmp); @@ -4305,7 +4332,7 @@ pacemaker_fuzzing: case 4: if (temp_len < 8) { break; } *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) ^= 0xFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP16] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP16]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT16"); strcat(afl->mutation, afl->m_tmp); @@ -4315,7 +4342,7 @@ pacemaker_fuzzing: case 5: if (temp_len < 8) { break; } *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) ^= 0xFFFFFFFF; - MOpt_globals.cycles_v2[STAGE_FLIP32] += 1; + MOpt_globals.cycles_v2[STAGE_FLIP32]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " FLIP_BIT32"); strcat(afl->mutation, afl->m_tmp); @@ -4327,7 +4354,7 @@ pacemaker_fuzzing: 1 + rand_below(afl, ARITH_MAX); out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); - MOpt_globals.cycles_v2[STAGE_ARITH8] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " ARITH+-"); strcat(afl->mutation, afl->m_tmp); @@ -4384,7 +4411,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_ARITH16] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH16]++; break; case 8: @@ -4438,7 +4465,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_ARITH32] += 1; + MOpt_globals.cycles_v2[STAGE_ARITH32]++; break; case 9: @@ -4446,7 +4473,7 @@ pacemaker_fuzzing: if (temp_len < 4) { break; } out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; - MOpt_globals.cycles_v2[STAGE_INTEREST8] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST8]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " INTERESTING8"); strcat(afl->mutation, afl->m_tmp); @@ -4478,7 +4505,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_INTEREST16] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST16]++; break; case 11: @@ -4508,7 +4535,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_INTEREST32] += 1; + MOpt_globals.cycles_v2[STAGE_INTEREST32]++; break; case 12: @@ -4518,7 +4545,7 @@ pacemaker_fuzzing: possibility of a no-op. */ out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); - MOpt_globals.cycles_v2[STAGE_RANDOMBYTE] += 1; + MOpt_globals.cycles_v2[STAGE_RANDOMBYTE]++; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " RAND8"); strcat(afl->mutation, afl->m_tmp); @@ -4541,16 +4568,16 @@ pacemaker_fuzzing: del_from = rand_below(afl, temp_len - del_len + 1); - memmove(out_buf + del_from, out_buf + del_from + del_len, - temp_len - del_from - del_len); - - temp_len -= del_len; - MOpt_globals.cycles_v2[STAGE_DELETEBYTE] += 1; #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), " DEL-%u%u", del_from, del_len); strcat(afl->mutation, afl->m_tmp); #endif + memmove(out_buf + del_from, out_buf + del_from + del_len, + temp_len - del_from - del_len); + + temp_len -= del_len; + MOpt_globals.cycles_v2[STAGE_DELETEBYTE]++; break; } @@ -4566,7 +4593,7 @@ pacemaker_fuzzing: u32 clone_from, clone_to, clone_len; u8 *new_buf; - if (actually_clone) { + if (likely(actually_clone)) { clone_len = choose_block_len(afl, temp_len); clone_from = rand_below(afl, temp_len - clone_len + 1); @@ -4617,7 +4644,7 @@ pacemaker_fuzzing: out_buf = new_buf; afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); temp_len += clone_len; - MOpt_globals.cycles_v2[STAGE_Clone75] += 1; + MOpt_globals.cycles_v2[STAGE_Clone75]++; } @@ -4637,9 +4664,9 @@ pacemaker_fuzzing: copy_from = rand_below(afl, temp_len - copy_len + 1); copy_to = rand_below(afl, temp_len - copy_len + 1); - if (rand_below(afl, 4)) { + if (likely(rand_below(afl, 4))) { - if (copy_from != copy_to) { + if (likely(copy_from != copy_to)) { #ifdef INTROSPECTION snprintf(afl->m_tmp, sizeof(afl->m_tmp), @@ -4666,7 +4693,7 @@ pacemaker_fuzzing: } - MOpt_globals.cycles_v2[STAGE_OverWrite75] += 1; + MOpt_globals.cycles_v2[STAGE_OverWrite75]++; break; } /* case 15 */ @@ -4721,7 +4748,7 @@ pacemaker_fuzzing: } afl->stage_cycles_puppet_v2[afl->swarm_now] - [STAGE_OverWriteExtra] += 1; + [STAGE_OverWriteExtra]++; break; @@ -4783,11 +4810,93 @@ pacemaker_fuzzing: } + default: { + + if (unlikely(afl->ready_for_splicing_count < 2)) break; + + u32 tid; + do { + + tid = rand_below(afl, afl->queued_paths); + + } while (tid == afl->current_entry || + + afl->queue_buf[tid]->len < 4); + + /* Get the testcase for splicing. */ + struct queue_entry *target = afl->queue_buf[tid]; + u32 new_len = target->len; + u8 * new_buf = queue_testcase_get(afl, target); + + if ((temp_len >= 2 && rand_below(afl, 2)) || + temp_len + HAVOC_BLK_XL >= MAX_FILE) { + + /* overwrite mode */ + + u32 copy_from, copy_to, copy_len; + + copy_len = choose_block_len(afl, new_len - 1); + if ((s32)copy_len > temp_len) copy_len = temp_len; + + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_OVERWRITE_%u_%u_%u_%s", copy_from, copy_to, + copy_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + memmove(out_buf + copy_to, new_buf + copy_from, copy_len); + + } else { + + /* insert mode */ + + u32 clone_from, clone_to, clone_len; + + clone_len = choose_block_len(afl, new_len); + clone_from = rand_below(afl, new_len - clone_len + 1); + clone_to = rand_below(afl, temp_len + 1); + + u8 *temp_buf = afl_realloc(AFL_BUF_PARAM(out_scratch), + temp_len + clone_len + 1); + if (unlikely(!temp_buf)) { PFATAL("alloc"); } + +#ifdef INTROSPECTION + snprintf(afl->m_tmp, sizeof(afl->m_tmp), + " SPLICE_INSERT_%u_%u_%u_%s", clone_from, clone_to, + clone_len, target->fname); + strcat(afl->mutation, afl->m_tmp); +#endif + /* Head */ + + memcpy(temp_buf, out_buf, clone_to); + + /* Inserted part */ + + memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len); + + /* Tail */ + memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to, + temp_len - clone_to); + + out_buf = temp_buf; + afl_swap_bufs(AFL_BUF_PARAM(out), AFL_BUF_PARAM(out_scratch)); + temp_len += clone_len; + + } + + afl->stage_cycles_puppet_v2[afl->swarm_now][STAGE_Splice]++; + break; + + } // end of default: + } /* switch select_algorithm() */ } /* for i=0; i < use_stacking */ - *MOpt_globals.pTime += 1; + ++*MOpt_globals.pTime; u64 temp_total_found = afl->queued_paths + afl->unique_crashes; @@ -5138,7 +5247,7 @@ u8 pilot_fuzzing(afl_state_t *afl) { void pso_updating(afl_state_t *afl) { - afl->g_now += 1; + afl->g_now++; if (afl->g_now > afl->g_max) { afl->g_now = 0; } afl->w_now = (afl->w_init - afl->w_end) * (afl->g_max - afl->g_now) / (afl->g_max) + -- cgit 1.4.1 From 8e1047f5efaece663bba9b8ef86d181198db5101 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 10 Nov 2020 14:08:21 +0100 Subject: support custom mutator introspection --- docs/Changelog.md | 1 + docs/custom_mutators.md | 10 ++++++ include/afl-fuzz.h | 33 ++++++++++++++------ src/afl-fuzz-bitmap.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++--- src/afl-fuzz-mutators.c | 7 +++++ src/afl-fuzz-one.c | 8 +++++ src/afl-fuzz-python.c | 33 ++++++++++++++++++++ 7 files changed, 159 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 50c1d48a..a69f2ff4 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,7 @@ sending a mail to . - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt + - added INTROSPECTION support for custom modules - print special compile time options used in help output - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 81ee9de4..2516e511 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -42,6 +42,7 @@ size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size unsigned char afl_custom_havoc_mutation_probability(void *data); unsigned char afl_custom_queue_get(void *data, const unsigned char *filename); void afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue); +const char* afl_custom_introspection(my_mutator_t *data); void afl_custom_deinit(void *data); ``` @@ -81,6 +82,9 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): pass ``` +def introspection(): + return string + ### Custom Mutation - `init`: @@ -130,6 +134,12 @@ def queue_new_entry(filename_new_queue, filename_orig_queue): This methods is called after adding a new test case to the queue. +- `introspection` (optional): + + This method is called after a new queue entry, crash or timeout is + discovered if compiled with INTROSPECTION. The custom mutator can then + return a string (const char *) that reports the exact mutations used. + - `deinit`: The last method to be called, deinitializing the state. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e59d5f90..c355263b 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -310,6 +310,7 @@ enum { /* 09 */ PY_FUNC_HAVOC_MUTATION_PROBABILITY, /* 10 */ PY_FUNC_QUEUE_GET, /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, + /* 12 */ PY_FUNC_INTROSPECTION, PY_FUNC_COUNT }; @@ -684,6 +685,8 @@ typedef struct afl_state { u32 custom_mutators_count; + struct custom_mutator *current_custom_fuzz; + list_t custom_mutator_list; /* this is a fixed buffer of size map_size that can be used by any function if @@ -747,6 +750,15 @@ struct custom_mutator { */ void *(*afl_custom_init)(afl_state_t *afl, unsigned int seed); + /** + * When afl-fuzz was compiled with INTROSPECTION=1 then custom mutators can + * also give introspection information back with this function. + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @return pointer to a text string (const char*) + */ + const char *(*afl_custom_introspection)(void *data); + /** * This method is called just before fuzzing a queue entry with the custom * mutator, and receives the initial buffer. It should return the number of @@ -953,16 +965,17 @@ u8 trim_case_custom(afl_state_t *, struct queue_entry *q, u8 *in_buf, struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); void finalize_py_module(void *); -u32 fuzz_count_py(void *, const u8 *, size_t); -size_t post_process_py(void *, u8 *, size_t, u8 **); -s32 init_trim_py(void *, u8 *, size_t); -s32 post_trim_py(void *, u8); -size_t trim_py(void *, u8 **); -size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t); -u8 havoc_mutation_probability_py(void *); -u8 queue_get_py(void *, const u8 *); -void queue_new_entry_py(void *, const u8 *, const u8 *); -void deinit_py(void *); +u32 fuzz_count_py(void *, const u8 *, size_t); +size_t post_process_py(void *, u8 *, size_t, u8 **); +s32 init_trim_py(void *, u8 *, size_t); +s32 post_trim_py(void *, u8); +size_t trim_py(void *, u8 **); +size_t havoc_mutation_py(void *, u8 *, size_t, u8 **, size_t); +u8 havoc_mutation_probability_py(void *); +u8 queue_get_py(void *, const u8 *); +const char *introspection_py(void *); +void queue_new_entry_py(void *, const u8 *, const u8 *); +void deinit_py(void *); #endif diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 735420c3..132499d6 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -588,8 +588,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { add_to_queue(afl, queue_fn, len, 0); #ifdef INTROSPECTION - fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, - afl->queue_top->fname); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "QUEUE %s = %s\n", afl->mutation, + afl->queue_top->fname); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "QUEUE CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } + #endif if (hnb == 2) { @@ -665,7 +689,32 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_tmouts; #ifdef INTROSPECTION - fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_TIMEOUT %s\n", afl->mutation); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, + "UNIQUE_TIMEOUT CUSTOM %s = %s\n", ptr, + afl->queue_top->fname); + + } + + } + + }); + + } + #endif /* Before saving, we make sure that it's a genuine hang by re-running @@ -751,7 +800,31 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { ++afl->unique_crashes; #ifdef INTROSPECTION - fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + if (afl->mutation[0] != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH %s\n", afl->mutation); + + } else if (afl->custom_mutators_count && afl->current_custom_fuzz) { + + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { + + if (afl->current_custom_fuzz == el && el->afl_custom_introspection) { + + const char *ptr = el->afl_custom_introspection(el->data); + + if (ptr != NULL && *ptr != 0) { + + fprintf(afl->introspection_file, "UNIQUE_CRASH CUSTOM %s = %s\n", + ptr, afl->queue_top->fname); + + } + + } + + }); + + } + #endif if (unlikely(afl->infoexec)) { diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index c4d7233c..1d14f657 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -166,6 +166,13 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { } + /* "afl_custom_introspection", optional */ +#ifdef INTROSPECTION + mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); + if (!mutator->afl_custom_introspection) + ACTF("optional symbol 'afl_custom_introspection' not found."); +#endif + /* "afl_custom_fuzz_count", optional */ mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); if (!mutator->afl_custom_fuzz_count) diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 91bbced6..64365ebb 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1780,10 +1780,16 @@ custom_mutator_stage: orig_hit_cnt = afl->queued_paths + afl->unique_crashes; +#ifdef INTROSPECTION + afl->mutation[0] = 0; +#endif + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { if (el->afl_custom_fuzz) { + afl->current_custom_fuzz = el; + if (el->afl_custom_fuzz_count) afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); else @@ -1889,6 +1895,8 @@ custom_mutator_stage: }); + afl->current_custom_fuzz = NULL; + if (!has_custom_fuzz) goto havoc_stage; new_hit_cnt = afl->queued_paths + afl->unique_crashes; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index adb92649..fe16bc46 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -163,6 +163,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject_GetAttrString(py_module, "queue_get"); py_functions[PY_FUNC_QUEUE_NEW_ENTRY] = PyObject_GetAttrString(py_module, "queue_new_entry"); + py_functions[PY_FUNC_INTROSPECTION] = + PyObject_GetAttrString(py_module, "introspection"); py_functions[PY_FUNC_DEINIT] = PyObject_GetAttrString(py_module, "deinit"); if (!py_functions[PY_FUNC_DEINIT]) FATAL("deinit function not found in python module"); @@ -381,6 +383,15 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, } + #ifdef INTROSPECTION + if (py_functions[PY_FUNC_INTROSPECTION]) { + + mutator->afl_custom_introspection = introspection_py; + + } + + #endif + OKF("Python mutator '%s' installed successfully.", module_name); /* Initialize the custom mutator */ @@ -679,6 +690,28 @@ u8 havoc_mutation_probability_py(void *py_mutator) { } +const char *introspection_py(void *py_mutator) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(0); + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_INTROSPECTION], + py_args); + Py_DECREF(py_args); + + if (py_value == NULL) { + + return NULL; + + } else { + + return PyByteArray_AsString(py_value); + + } + +} + u8 queue_get_py(void *py_mutator, const u8 *filename) { PyObject *py_args, *py_value; -- cgit 1.4.1 From 1cc637a0a05a043a223f69fb9661ecc3d5597d23 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 16 Nov 2020 10:59:09 +0100 Subject: support AFL_LLVM_INSTRUMENT env for our own PCGUARD --- docs/Changelog.md | 13 +-- instrumentation/SanitizerCoveragePCGUARD.so.cc | 8 +- src/afl-cc.c | 113 ++++++++++++++----------- src/afl-fuzz-init.c | 3 +- src/afl-fuzz.c | 42 +++++++-- 5 files changed, 116 insertions(+), 63 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index a69f2ff4..baa2667b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,24 +37,27 @@ sending a mail to . - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt - - added INTROSPECTION support for custom modules - print special compile time options used in help output + - somewhere we broke -n dumb fuzzing, fixed - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! - not overriding -Ox or -fno-unroll-loops anymore - we now have our own trace-pc-guard implementation. It is the same as -fsanitize-coverage=trace-pc-guard from llvm 12, but: it is a) inline - and b) works from llvm 10+ on :) + and b) works from llvm 10.0.1 + onwards :) - new llvm pass: dict2file via AFL_LLVM_DICT2FILE, create afl-fuzz -x dictionary of string comparisons found during compilation - LTO autodict now also collects interesting cmp comparisons, std::string compare + find + ==, bcmp - fix crash in dict2file for integers > 64 bit + - custom mutators + - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ + - added a new custom mutator: libfuzzer that integrates libfuzzer mutations + - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ + - added INTROSPECTION support for custom modules + - python fuzz function was not optional, fixed - unicornafl synced with upstream (arm64 fix, better rust bindings) - - added a new custom mutator: symcc -> https://github.com/eurecom-s3/symcc/ - - added a new custom mutator: libfuzzer that integrates libfuzzer mutations - - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ ### Version ++2.68c (release) diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index b3c55108..e85f9cd3 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -544,7 +544,9 @@ bool ModuleSanitizerCoverage::instrumentModule( be_quiet = 1; skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - // scanForDangerousFunctions(&M); + + initInstrumentList(); + scanForDangerousFunctions(&M); if (debug) { @@ -819,6 +821,8 @@ void ModuleSanitizerCoverage::instrumentFunction( Function &F, DomTreeCallback DTCallback, PostDomTreeCallback PDTCallback) { if (F.empty()) return; + if (!isInInstrumentList(&F)) return; + if (F.getName().find(".module_ctor") != std::string::npos) return; // Should not instrument sanitizer init functions. if (F.getName().startswith("__sanitizer_")) @@ -1315,6 +1319,7 @@ std::string ModuleSanitizerCoverage::getSectionEnd( } char ModuleSanitizerCoverageLegacyPass::ID = 0; + INITIALIZE_PASS_BEGIN(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) @@ -1323,6 +1328,7 @@ INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) INITIALIZE_PASS_END(ModuleSanitizerCoverageLegacyPass, "sancov", "Pass for instrumenting coverage on functions", false, false) + ModulePass *llvm::createModuleSanitizerCoverageLegacyPassPass( const SanitizerCoverageOptions &Options, const std::vector &AllowlistFiles, diff --git a/src/afl-cc.c b/src/afl-cc.c index 771a58f5..5d8d33a5 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -49,14 +49,14 @@ static u8 * obj_path; /* Path to runtime libraries */ static u8 **cc_params; /* Parameters passed to the real CC */ static u32 cc_par_cnt = 1; /* Param count, including argv0 */ static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode, - compiler_mode, plusplus_mode; -static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto; -static u8 *lto_flag = AFL_CLANG_FLTO, *argvnull; -static u8 debug; -static u8 cwd[4096]; -static u8 cmplog_mode; -u8 use_stdin; /* dummy */ +static u8 instrument_mode, instrument_opt_mode, ngram_size, lto_mode; +static u8 compiler_mode, plusplus_mode, have_instr_env = 0; +static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; +static u8 * lto_flag = AFL_CLANG_FLTO, *argvnull; +static u8 debug; +static u8 cwd[4096]; +static u8 cmplog_mode; +u8 use_stdin; /* dummy */ // static u8 *march_opt = CFLAGS_OPT; enum { @@ -354,19 +354,13 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && plusplus_mode) cc_params[cc_par_cnt++] = "-lc++"; // needed by fuzzbench, early - if (lto_mode) { - - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + if (lto_mode && have_instr_env) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); - - } + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); } @@ -508,11 +502,25 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (instrument_mode == INSTRUMENT_PCGUARD) { #if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); + if (have_instr_list) { + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n"); + cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; + + } else { + + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = "-load"; + cc_params[cc_par_cnt++] = "-Xclang"; + cc_params[cc_par_cnt++] = + alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); + + } + #else #if LLVM_MAJOR >= 4 if (!be_quiet) @@ -590,6 +598,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; if (!strcmp(cur, "-m64")) bit_mode = 64; + if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) + have_instr_list = 1; + if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) asan_set = 1; @@ -856,6 +867,14 @@ int main(int argc, char **argv, char **envp) { be_quiet = 1; + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { + + have_instr_env = 1; + + } + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1015,14 +1034,14 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && - getenv("AFL_DONT_OPTIMIZE")) + if (have_instr_env && getenv("AFL_DONT_OPTIMIZE")) { + WARNF( "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " "for file matching, only function matching!"); + } + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -1426,22 +1445,20 @@ int main(int argc, char **argv, char **envp) { #if LLVM_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (have_instr_env) { instrument_mode = INSTRUMENT_AFL; - WARNF( - "switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " - "SanitizerCoverage.html#partially-disabling-instrumentation"); + if (!be_quiet) + WARNF( + "Switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); } else + #endif instrument_mode = INSTRUMENT_PCGUARD; + #endif } @@ -1487,18 +1504,16 @@ int main(int argc, char **argv, char **envp) { "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " "together"); - if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || - getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) { + FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_ALLOWLIST/DENYLIST! Use " - "-fsanitize-coverage-allowlist=allowlist.txt or " - "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " - "12+), see " - "https://clang.llvm.org/docs/" - "SanitizerCoverage.html#partially-disabling-instrumentation"); + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif u8 *ptr2; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 8b9b0a6f..6884bb1d 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2497,7 +2497,8 @@ void check_binary(afl_state_t *afl, u8 *fname) { } - if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || afl->non_instrumented_mode) { + if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + afl->non_instrumented_mode) { return; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index c1ddd413..cedfdf8f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -435,11 +435,23 @@ int main(int argc, char **argv_orig, char **envp) { u8 *c; - if (afl->non_instrumented_mode) { FATAL("-M is not supported in non-instrumented mode"); } + if (afl->non_instrumented_mode) { + + FATAL("-M is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } - /* sanity check for argument: should not begin with '-' (possible option) */ - if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing @@ -469,11 +481,23 @@ int main(int argc, char **argv_orig, char **envp) { case 'S': /* secondary sync id */ - if (afl->non_instrumented_mode) { FATAL("-S is not supported in non-instrumented mode"); } + if (afl->non_instrumented_mode) { + + FATAL("-S is not supported in non-instrumented mode"); + + } + if (afl->sync_id) { FATAL("Multiple -S or -M options not supported"); } - /* sanity check for argument: should not begin with '-' (possible option) */ - if (optarg && *optarg == '-') { FATAL("argument for -M started with a dash '-', which is used for options"); } + /* sanity check for argument: should not begin with '-' (possible + * option) */ + if (optarg && *optarg == '-') { + + FATAL( + "argument for -M started with a dash '-', which is used for " + "options"); + + } afl->sync_id = ck_strdup(optarg); afl->is_secondary_node = 1; @@ -1354,7 +1378,11 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { check_binary(afl, afl->cmplog_binary); } + if (!afl->fsrv.qemu_mode && !afl->non_instrumented_mode) { + + check_binary(afl, afl->cmplog_binary); + + } } -- cgit 1.4.1 From 57f8aec3814e1959d36210815a0369d7bc149ac7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 02:41:35 +0100 Subject: brought back missing env vars --- docs/env_variables.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index a36f2b4e..469fc957 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -306,6 +306,14 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL++ to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. + - Setting `AFL_FORKSRV_INIT_TMOUT` allows yout to specify a different timeout + to wait for the forkserver to spin up. The default is the `-t` value times + `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the + default would wait `1000` milis. Setting a different time here is useful + if the target has a very slow startup time, for example when doing + full-system fuzzing or emulation, but you don't want the actual runs + to wait too long for timeouts. + - `AFL_NO_ARITH` causes AFL++ to skip most of the deterministic arithmetics. This can be useful to speed up the fuzzing of text-based file formats. @@ -389,6 +397,13 @@ checks or alter some of the more exotic semantics of the tool: for an existing out folder, even if a different `-i` was provided. Without this setting, afl-fuzz will refuse execution for a long-fuzzed out dir. + - Setting `AFL_MAX_DET_EXRAS` will change the threshold at what number of elements + in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will + kick off. In probabilistic mode, not all dictionary entires will be used all + of the times for fuzzing mutations to not make fuzzing slower by it. + The default count is `200` element. So for the 200 + 1st element, there is a + 1 in 201 chance, that one of the dictionary entry will not be used directly. + - Setting `AFL_NO_FORKSRV` disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when working with unruly libraries that create threads or do other crazy -- cgit 1.4.1 From f80f62f14bb5222344925a7ec51c81aa2f95d86e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 03:02:13 +0100 Subject: renamed env var to AFL_DEBUG_CHILD --- docs/Changelog.md | 1 + docs/env_variables.md | 2 +- examples/afl_network_proxy/afl-network-server.c | 7 +++++-- include/afl-fuzz.h | 4 ++-- include/envs.h | 3 ++- instrumentation/afl-compiler-rt.o.c | 2 +- src/afl-fuzz-run.c | 2 +- src/afl-fuzz-state.c | 6 ++++-- src/afl-fuzz.c | 4 ++-- src/afl-showmap.c | 6 +++++- src/afl-tmin.c | 7 +++++-- test/test-unicorn-mode.sh | 6 +++--- 12 files changed, 32 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index baa2667b..9426ed54 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -58,6 +58,7 @@ sending a mail to . - added INTROSPECTION support for custom modules - python fuzz function was not optional, fixed - unicornafl synced with upstream (arm64 fix, better rust bindings) + - renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD ### Version ++2.68c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index 469fc957..1ce6f206 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -388,7 +388,7 @@ checks or alter some of the more exotic semantics of the tool: processing the first queue entry; and `AFL_BENCH_UNTIL_CRASH` causes it to exit soon after the first crash is found. - - Setting `AFL_DEBUG_CHILD_OUTPUT` will not suppress the child output. + - Setting `AFL_DEBUG_CHILD` will not suppress the child output. Not pretty but good for debugging purposes. - Setting `AFL_NO_CPU_RED` will not display very high cpu usages in red color. diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c index 3831f985..513dc8f2 100644 --- a/examples/afl_network_proxy/afl-network-server.c +++ b/examples/afl_network_proxy/afl-network-server.c @@ -636,8 +636,11 @@ int main(int argc, char **argv_orig, char **envp) { if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } - afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); #ifdef USE_DEFLATE compressor = libdeflate_alloc_compressor(1); diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index c355263b..b484b93e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -362,8 +362,8 @@ typedef struct afl_env_vars { u8 afl_skip_cpufreq, afl_exit_when_done, afl_no_affinity, afl_skip_bin_check, afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui, afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one, - afl_bench_until_crash, afl_debug_child_output, afl_autoresume, - afl_cal_fast, afl_cycle_schedules, afl_expand_havoc, afl_statsd; + afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, + afl_cycle_schedules, afl_expand_havoc, afl_statsd; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, diff --git a/include/envs.h b/include/envs.h index b753d5f8..8255cf4f 100644 --- a/include/envs.h +++ b/include/envs.h @@ -6,6 +6,7 @@ static char *afl_environment_deprecated[] = { "AFL_LLVM_WHITELIST", "AFL_GCC_WHITELIST", + "AFL_DEBUG_CHILD_OUTPUT", "AFL_DEFER_FORKSRV", "AFL_POST_LIBRARY", "AFL_PERSISTENT", @@ -36,7 +37,7 @@ static char *afl_environment_variables[] = { "AFL_CXX", "AFL_CYCLE_SCHEDULES", "AFL_DEBUG", - "AFL_DEBUG_CHILD_OUTPUT", + "AFL_DEBUG_CHILD", "AFL_DEBUG_GDB", "AFL_DISABLE_TRIM", "AFL_DONT_OPTIMIZE", diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 18501b65..485f500c 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -992,7 +992,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { // For stability analysis, if you want to know to which function unstable // edge IDs belong - uncomment, recompile+install llvm_mode, recompile // the target. libunwind and libbacktrace are better solutions. - // Set AFL_DEBUG_CHILD_OUTPUT=1 and run afl-fuzz with 2>file to capture + // Set AFL_DEBUG_CHILD=1 and run afl-fuzz with 2>file to capture // the backtrace output /* uint32_t unstable[] = { ... unstable edge IDs }; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b96fe71a..95b3ee8a 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -332,7 +332,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + afl->afl_env.afl_debug_child); if (afl->fsrv.support_shmem_fuzz && !afl->fsrv.use_shmem_fuzz) { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 61bd06b7..489d4e53 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -268,11 +268,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_bench_until_crash = get_afl_env(afl_environment_variables[i]) ? 1 : 0; - } else if (!strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", + } else if (!strncmp(env, "AFL_DEBUG_CHILD", + afl_environment_variable_len) || + !strncmp(env, "AFL_DEBUG_CHILD_OUTPUT", afl_environment_variable_len)) { - afl->afl_env.afl_debug_child_output = + afl->afl_env.afl_debug_child = get_afl_env(afl_environment_variables[i]) ? 1 : 0; } else if (!strncmp(env, "AFL_AUTORESUME", diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index ac77bb1f..e04ae649 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -166,7 +166,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" "AFL_DEBUG: extra debugging output for Python mode trimming\n" - "AFL_DEBUG_CHILD_OUTPUT: do not suppress stdout/stderr from target\n" + "AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n" "AFL_DISABLE_TRIM: disable the trimming of test cases\n" "AFL_DUMB_FORKSRV: use fork server without feedback from target\n" "AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n" @@ -1426,7 +1426,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; afl_fsrv_start(&afl->cmplog_fsrv, afl->argv, &afl->stop_soon, - afl->afl_env.afl_debug_child_output); + afl->afl_env.afl_debug_child); OKF("Cmplog forkserver successfully started"); } diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 4b357254..69527007 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1091,7 +1091,11 @@ int main(int argc, char **argv_orig, char **envp) { } afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + map_size = fsrv->map_size; if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 06037d61..e4fb068d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1141,8 +1141,11 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - afl_fsrv_start(fsrv, use_argv, &stop_soon, - get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0); + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); diff --git a/test/test-unicorn-mode.sh b/test/test-unicorn-mode.sh index 7ac4cdd2..b4c6eb3e 100755 --- a/test/test-unicorn-mode.sh +++ b/test/test-unicorn-mode.sh @@ -7,7 +7,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel test -e ../unicorn_mode/samples/simple/simple_target.bin -a -e ../unicorn_mode/samples/compcov_x64/compcov_target.bin && { { # We want to see python errors etc. in logs, in case something doesn't work - export AFL_DEBUG_CHILD_OUTPUT=1 + export AFL_DEBUG_CHILD=1 # some python version should be available now PYTHONS="`command -v python3` `command -v python` `command -v python2`" @@ -34,7 +34,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel cd ../unicorn_mode/samples/persistent make >>errors 2>&1 $ECHO "$GREY[*] running afl-fuzz for unicorn_mode (persistent), this will take approx 25 seconds" - AFL_DEBUG_CHILD_OUTPUT=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 + AFL_DEBUG_CHILD=1 ../../../afl-fuzz -m none -V25 -U -i sample_inputs -o out -d -- ./harness @@ >>errors 2>&1 test -n "$( ls out/default/queue/id:000002* 2>/dev/null )" && { $ECHO "$GREEN[+] afl-fuzz is working correctly with unicorn_mode (persistent)" } || { @@ -96,7 +96,7 @@ test -d ../unicorn_mode/unicornafl -a -e ../unicorn_mode/unicornafl/samples/shel } fi - unset AFL_DEBUG_CHILD_OUTPUT + unset AFL_DEBUG_CHILD } } || { -- cgit 1.4.1 From 68e18dbb7a261b0343bc3580f26cf59531c18152 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 18 Nov 2020 03:09:50 +0100 Subject: more info about child_debug --- docs/env_variables.md | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 1ce6f206..e6f9c275 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -389,7 +389,11 @@ checks or alter some of the more exotic semantics of the tool: exit soon after the first crash is found. - Setting `AFL_DEBUG_CHILD` will not suppress the child output. + This lets you see all output of the child, making setup issues obvious. + For example, in an unicornafl harness, you might see python stacktraces. + You may also see other logs that way, indicating why the forkserver won't start. Not pretty but good for debugging purposes. + Note that `AFL_DEBUG_CHILD_OUTPUT` is deprecated. - Setting `AFL_NO_CPU_RED` will not display very high cpu usages in red color. -- cgit 1.4.1 From 211a6eb411a6e57cd22a24bb50ae095676d8b0c5 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Wed, 18 Nov 2020 08:40:12 +0100 Subject: typos and wording --- docs/env_variables.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index e6f9c275..04ba032a 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -306,10 +306,10 @@ checks or alter some of the more exotic semantics of the tool: don't want AFL++ to spend too much time classifying that stuff and just rapidly put all timeouts in that bin. - - Setting `AFL_FORKSRV_INIT_TMOUT` allows yout to specify a different timeout + - Setting `AFL_FORKSRV_INIT_TMOUT` allows you to specify a different timeout to wait for the forkserver to spin up. The default is the `-t` value times `FORK_WAIT_MULT` from `config.h` (usually 10), so for a `-t 100`, the - default would wait `1000` milis. Setting a different time here is useful + default would wait for `1000` milliseconds. Setting a different time here is useful if the target has a very slow startup time, for example when doing full-system fuzzing or emulation, but you don't want the actual runs to wait too long for timeouts. @@ -403,10 +403,10 @@ checks or alter some of the more exotic semantics of the tool: - Setting `AFL_MAX_DET_EXRAS` will change the threshold at what number of elements in the `-x` dictionary and LTO autodict (combined) the probabilistic mode will - kick off. In probabilistic mode, not all dictionary entires will be used all - of the times for fuzzing mutations to not make fuzzing slower by it. - The default count is `200` element. So for the 200 + 1st element, there is a - 1 in 201 chance, that one of the dictionary entry will not be used directly. + kick off. In probabilistic mode not all dictionary entires will be used all + of the times for fuzzing mutations to not slow down fuzzing. + The default count is `200` elements. So for the 200 + 1st element, there is a + 1 in 201 chance, that one of the dictionary entries will not be used directly. - Setting `AFL_NO_FORKSRV` disables the forkserver optimization, reverting to fork + execve() call for every tested input. This is useful mostly when -- cgit 1.4.1 From cd0a25be5e9b05a2ab6a11592cd95e7f653bf42d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 18 Nov 2020 14:29:17 -0500 Subject: Use buffer protocol to retrieve result from python post_process (#605) Saves an extra copy, gives post processing functions more flexibility --- docs/custom_mutators.md | 3 +++ include/afl-fuzz.h | 3 +-- src/afl-fuzz-python.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 28 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 2516e511..53f783fe 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -130,6 +130,9 @@ def introspection(): `post_process` function. This function is then transforming the data into the format expected by the API before executing the target. + This can return any python object that implements the buffer protocol and + supports PyBUF_SIMPLE. These include bytes, bytearray, etc. + - `queue_new_entry` (optional): This methods is called after adding a new test case to the queue. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 423230f1..933af65d 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -326,8 +326,7 @@ typedef struct py_mutator { u8 * fuzz_buf; size_t fuzz_size; - u8 * post_process_buf; - size_t post_process_size; + Py_buffer post_process_buf; u8 * trim_buf; size_t trim_size; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 80532774..9ac4403b 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -134,6 +134,18 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { PyObject * py_module = py->py_module; PyObject **py_functions = py->py_functions; + // initialize the post process buffer; ensures it's always valid + PyObject *unused_bytes = PyByteArray_FromStringAndSize("OHAI", 4); + if (!unused_bytes) { FATAL("allocation failed!"); } + if (PyObject_GetBuffer(unused_bytes, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { + + FATAL("buffer initialization failed"); + + } + + Py_DECREF(unused_bytes); + if (py_module != NULL) { u8 py_notrim = 0, py_idx; @@ -313,7 +325,6 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, struct custom_mutator *mutator; mutator = ck_alloc(sizeof(struct custom_mutator)); - mutator->post_process_buf = NULL; mutator->name = module_name; ACTF("Loading Python mutator library from '%s'...", module_name); @@ -403,10 +414,13 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf) { - size_t py_out_buf_size; PyObject * py_args, *py_value; py_mutator_t *py = (py_mutator_t *)py_mutator; + // buffer returned previously must be released; initialized during init + // so we don't need to do comparisons + PyBuffer_Release(&py->post_process_buf); + py_args = PyTuple_New(1); py_value = PyByteArray_FromStringAndSize(buf, buf_size); if (!py_value) { @@ -426,20 +440,20 @@ size_t post_process_py(void *py_mutator, u8 *buf, size_t buf_size, if (py_value != NULL) { - py_out_buf_size = PyByteArray_Size(py_value); + if (PyObject_GetBuffer(py_value, &py->post_process_buf, PyBUF_SIMPLE) == + -1) { - if (unlikely(!afl_realloc(BUF_PARAMS(post_process), py_out_buf_size))) { - - PFATAL("alloc"); + PyErr_Print(); + FATAL( + "Python custom mutator: post_process call return value not a " + "bytes-like object"); } - memcpy(py->post_process_buf, PyByteArray_AsString(py_value), - py_out_buf_size); Py_DECREF(py_value); - *out_buf = py->post_process_buf; - return py_out_buf_size; + *out_buf = (u8 *)py->post_process_buf.buf; + return py->post_process_buf.len; } else { -- cgit 1.4.1 From e32b7eeb83c0571a2bdaadfd5b7b769fec1405cc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 19 Nov 2020 16:14:19 +0100 Subject: fixed child not killed with -c --- docs/Changelog.md | 1 + instrumentation/afl-compiler-rt.o.c | 22 ++++++++++++++++++++-- src/afl-fuzz.c | 9 ++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 9426ed54..3c20f8bd 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -38,6 +38,7 @@ sending a mail to . - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt - print special compile time options used in help output + - when using -c cmplog, one of the childs was not killed, fixed - somewhere we broke -n dumb fuzzing, fixed - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 485f500c..b07aeb83 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -101,6 +101,11 @@ int __afl_sharedmem_fuzzing __attribute__((weak)); struct cmp_map *__afl_cmp_map; +/* Child pid? */ + +static s32 child_pid; +static void (*old_sigterm_handler)(int) = 0; + /* Running in persistent mode? */ static u8 is_persistent; @@ -109,6 +114,14 @@ static u8 is_persistent; static u8 _is_sancov; +/* ensure we kill the child on termination */ + +void at_exit(int signal) { + + if (child_pid > 0) { kill(child_pid, SIGKILL); } + +} + /* Uninspired gcc plugin instrumentation */ void __afl_trace(const u32 x) { @@ -432,7 +445,6 @@ static void __afl_map_shm(void) { static void __afl_start_snapshots(void) { static u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; u32 status = 0; u32 already_read_first = 0; u32 was_killed; @@ -579,6 +591,7 @@ static void __afl_start_snapshots(void) { //(void)nice(-20); // does not seem to improve signal(SIGCHLD, old_sigchld_handler); + signal(SIGTERM, old_sigterm_handler); close(FORKSRV_FD); close(FORKSRV_FD + 1); @@ -633,6 +646,11 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { + struct sigaction orig_action; + sigaction(SIGTERM, NULL, &orig_action); + old_sigterm_handler = orig_action.sa_handler; + signal(SIGTERM, at_exit); + #ifdef __linux__ if (/*!is_persistent &&*/ !__afl_cmp_map && !getenv("AFL_NO_SNAPSHOT") && afl_snapshot_init() >= 0) { @@ -645,7 +663,6 @@ static void __afl_start_forkserver(void) { #endif u8 tmp[4] = {0, 0, 0, 0}; - s32 child_pid; u32 status = 0; u32 already_read_first = 0; u32 was_killed; @@ -793,6 +810,7 @@ static void __afl_start_forkserver(void) { //(void)nice(-20); signal(SIGCHLD, old_sigchld_handler); + signal(SIGTERM, old_sigterm_handler); close(FORKSRV_FD); close(FORKSRV_FD + 1); diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 1008f28c..b60908da 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -40,7 +40,7 @@ extern u64 time_spent_working; static void at_exit() { - int i; + s32 i, pid1 = 0, pid2 = 0; char *list[4] = {SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, CMPLOG_SHM_ENV_VAR, NULL}; char *ptr; @@ -48,10 +48,10 @@ static void at_exit() { if (ptr && *ptr) unlink(ptr); ptr = getenv("__AFL_TARGET_PID1"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid1 = atoi(ptr)) > 0) kill(pid1, SIGTERM); ptr = getenv("__AFL_TARGET_PID2"); - if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL); + if (ptr && *ptr && (pid2 = atoi(ptr)) > 0) kill(pid2, SIGTERM); i = 0; while (list[i] != NULL) { @@ -75,6 +75,9 @@ static void at_exit() { } + if (pid1 > 0) { kill(pid1, SIGKILL); } + if (pid2 > 0) { kill(pid2, SIGKILL); } + } /* Display usage hints. */ -- cgit 1.4.1 From 8584f9d2b5de9687c518c672e471f4f8cd9166fa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 1 Dec 2020 13:13:11 +0100 Subject: added AFL_NO_AUTODICT --- docs/Changelog.md | 1 + docs/env_variables.md | 3 + include/envs.h | 1 + instrumentation/README.lto.md | 33 +++++++---- src/afl-forkserver.c | 125 ++++++++++++++++++++++-------------------- src/afl-fuzz.c | 1 + 6 files changed, 95 insertions(+), 69 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 3c20f8bd..7fa7ff53 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -34,6 +34,7 @@ sending a mail to . - crashing seeds are now not prohibiting a run anymore but are skipped. They are used for splicing though. - update MOpt for expanded havoc modes + - setting the env var AFL_NO_AUTODICT will not load an LTO autodictionary - added NO_SPLICING compile option and makefile define - added INTROSPECTION make target that writes all mutations to out/NAME/introspection.txt diff --git a/docs/env_variables.md b/docs/env_variables.md index 04ba032a..f7b4c994 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -294,6 +294,9 @@ checks or alter some of the more exotic semantics of the tool: on Linux systems. This slows things down, but lets you run more instances of afl-fuzz than would be prudent (if you really want to). + - Setting `AFL_NO_AUTODICT` will not load an LTO generated auto dictionary + that is compiled into the target. + - `AFL_SKIP_CRASHES` causes AFL++ to tolerate crashing files in the input queue. This can help with rare situations where a program crashes only intermittently, but it's not really recommended under normal operating diff --git a/include/envs.h b/include/envs.h index 8255cf4f..3aa05cb5 100644 --- a/include/envs.h +++ b/include/envs.h @@ -100,6 +100,7 @@ static char *afl_environment_variables[] = { "AFL_LLVM_LTO_STARTID", "AFL_LLVM_LTO_DONTWRITEID", "AFL_NO_ARITH", + "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index abdbd2ac..62e98902 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -60,7 +60,12 @@ AUTODICTIONARY: 11 strings found ## Getting llvm 11+ -### Installing llvm from the llvm repository (version 11) +### Installing llvm version 11 + +llvm 11 should be available in all current Linux repository. +If you use an outdated Linux distribution read the next section. + +### Installing llvm from the llvm repository (version 12) Installing the llvm snapshot builds is easy and mostly painless: @@ -73,11 +78,11 @@ then add the pgp key of llvm and install the packages: ``` wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - apt-get update && apt-get upgrade -y -apt-get install -y clang-11 clang-tools-11 libc++1-11 libc++-11-dev \ - libc++abi1-11 libc++abi-11-dev libclang1-11 libclang-11-dev \ - libclang-common-11-dev libclang-cpp11 libclang-cpp11-dev liblld-11 \ - liblld-11-dev liblldb-11 liblldb-11-dev libllvm11 libomp-11-dev \ - libomp5-11 lld-11 lldb-11 llvm-11 llvm-11-dev llvm-11-runtime llvm-11-tools +apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ + libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \ + libclang-common-12-dev libclang-cpp11 libclang-cpp11-dev liblld-12 \ + liblld-12-dev liblldb-12 liblldb-12-dev libllvm11 libomp-12-dev \ + libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools ``` ### Building llvm yourself (version 12) @@ -120,16 +125,22 @@ While compiling, a dictionary based on string comparisons is automatically generated and put into the target binary. This dictionary is transfered to afl-fuzz on start. This improves coverage statistically by 5-10% :) +Note that if for any reason you do not want to use the autodictionary feature +then just set the environment variable `AFL_NO_AUTODICT` when starting afl-fuzz. + ## Fixed memory map -To speed up fuzzing, it is possible to set a fixed shared memory map. +To speed up fuzzing a little bit more, it is possible to set a fixed shared +memory map. Recommended is the value 0x10000. + In most cases this will work without any problems. However if a target uses early constructors, ifuncs or a deferred forkserver this can crash the target. -On unusual operating systems/processors/kernels or weird libraries this might -fail so to change the fixed address at compile time set -AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address -to be dynamic - the original afl way, which is slower). + +Also on unusual operating systems/processors/kernels or weird libraries the +recommended 0x10000 address might not work, so then change the fixed address. + +To enable this feature set AFL_LLVM_MAP_ADDR with the address. ## Document edge IDs diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 3814a77e..01ef1d9e 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -348,9 +348,10 @@ static void report_error_and_exit(int error) { void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output) { - int st_pipe[2], ctl_pipe[2]; - s32 status; - s32 rlen; + int st_pipe[2], ctl_pipe[2]; + s32 status; + s32 rlen; + char *ignore_autodict = getenv("AFL_NO_AUTODICT"); if (!be_quiet) { ACTF("Spinning up the fork server..."); } @@ -607,7 +608,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, fsrv->use_shmem_fuzz = 1; if (!be_quiet) { ACTF("Using SHARED MEMORY FUZZING feature."); } - if ((status & FS_OPT_AUTODICT) == 0) { + if ((status & FS_OPT_AUTODICT) == 0 || ignore_autodict) { u32 send_status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); if (write(fsrv->fsrv_ctl_fd, &send_status, 4) != 4) { @@ -660,101 +661,109 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { + if (ignore_autodict) { - // this is not afl-fuzz - or it is cmplog - we deny and return - if (fsrv->use_shmem_fuzz) { + if (!be_quiet) { WARNF("Ignoring offered AUTODICT feature."); } - status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); + } else { - } else { + if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { - status = (FS_OPT_ENABLED); + // this is not afl-fuzz - or it is cmplog - we deny and return + if (fsrv->use_shmem_fuzz) { - } + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + } else { - FATAL("Writing to forkserver failed."); + status = (FS_OPT_ENABLED); - } + } - return; + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { - } + FATAL("Writing to forkserver failed."); - if (!be_quiet) { ACTF("Using AUTODICT feature."); } + } - if (fsrv->use_shmem_fuzz) { + return; - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); + } - } else { + if (!be_quiet) { ACTF("Using AUTODICT feature."); } - status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); + if (fsrv->use_shmem_fuzz) { - } + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT | FS_OPT_SHDMEM_FUZZ); - if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { + } else { - FATAL("Writing to forkserver failed."); + status = (FS_OPT_ENABLED | FS_OPT_AUTODICT); - } + } - if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { + if (write(fsrv->fsrv_ctl_fd, &status, 4) != 4) { - FATAL("Reading from forkserver failed."); + FATAL("Writing to forkserver failed."); - } + } - if (status < 2 || (u32)status > 0xffffff) { + if (read(fsrv->fsrv_st_fd, &status, 4) != 4) { - FATAL("Dictionary has an illegal size: %d", status); + FATAL("Reading from forkserver failed."); - } + } - u32 offset = 0, count = 0; - u32 len = status; - u8 *dict = ck_alloc(len); - if (dict == NULL) { + if (status < 2 || (u32)status > 0xffffff) { - FATAL("Could not allocate %u bytes of autodictionary memory", len); + FATAL("Dictionary has an illegal size: %d", status); - } + } - while (len != 0) { + u32 offset = 0, count = 0; + u32 len = status; + u8 *dict = ck_alloc(len); + if (dict == NULL) { - rlen = read(fsrv->fsrv_st_fd, dict + offset, len); - if (rlen > 0) { + FATAL("Could not allocate %u bytes of autodictionary memory", len); - len -= rlen; - offset += rlen; + } - } else { + while (len != 0) { - FATAL( - "Reading autodictionary fail at position %u with %u bytes " - "left.", - offset, len); + rlen = read(fsrv->fsrv_st_fd, dict + offset, len); + if (rlen > 0) { + + len -= rlen; + offset += rlen; + + } else { + + FATAL( + "Reading autodictionary fail at position %u with %u bytes " + "left.", + offset, len); + + } } - } + offset = 0; + while (offset < (u32)status && + (u8)dict[offset] + offset < (u32)status) { + + fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, + (u8)dict[offset]); + offset += (1 + dict[offset]); + count++; - offset = 0; - while (offset < (u32)status && - (u8)dict[offset] + offset < (u32)status) { + } - fsrv->add_extra_func(fsrv->afl_ptr, dict + offset + 1, - (u8)dict[offset]); - offset += (1 + dict[offset]); - count++; + if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } + ck_free(dict); } - if (!be_quiet) { ACTF("Loaded %u autodictionary entries", count); } - ck_free(dict); - } } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b60908da..b91d862d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -187,6 +187,7 @@ static void usage(u8 *argv0, int more_help) { " used. Defaults to 200.\n" "AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n" "AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n" + "AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target\n" "AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n" "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n" -- cgit 1.4.1 From c05e4efbe9b4e7d1ff078b7a392621f2ca7572e6 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 1 Dec 2020 14:40:30 +0100 Subject: renamed examples/ to utils/ --- GNUmakefile | 36 +- README.md | 7 +- docs/Changelog.md | 1 + docs/FAQ.md | 2 +- docs/binaryonly_fuzzing.md | 6 +- docs/custom_mutators.md | 4 +- docs/env_variables.md | 2 +- docs/life_pro_tips.md | 4 +- docs/notes_for_asan.md | 4 +- docs/parallel_fuzzing.md | 2 +- examples/README.md | 54 -- examples/afl_frida/GNUmakefile | 23 - examples/afl_frida/Makefile | 2 - examples/afl_frida/README.md | 34 - examples/afl_frida/afl-frida.c | 542 --------------- examples/afl_frida/afl-frida.h | 53 -- examples/afl_frida/libtestinstr.c | 35 - examples/afl_network_proxy/GNUmakefile | 43 -- examples/afl_network_proxy/Makefile | 2 - examples/afl_network_proxy/README.md | 61 -- examples/afl_network_proxy/afl-network-client.c | 415 ------------ examples/afl_network_proxy/afl-network-server.c | 720 -------------------- examples/afl_proxy/Makefile | 7 - examples/afl_proxy/README.md | 9 - examples/afl_proxy/afl-proxy.c | 238 ------- examples/afl_untracer/Makefile | 16 - examples/afl_untracer/README.md | 60 -- examples/afl_untracer/TODO | 2 - examples/afl_untracer/afl-untracer.c | 768 ---------------------- examples/afl_untracer/ghidra_get_patchpoints.java | 84 --- examples/afl_untracer/ida_get_patchpoints.py | 62 -- examples/afl_untracer/libtestinstr.c | 35 - examples/afl_untracer/libtestinstr.so | Bin 17152 -> 0 bytes examples/afl_untracer/patches.txt | 34 - examples/aflpp_driver/GNUmakefile | 46 -- examples/aflpp_driver/Makefile | 2 - examples/aflpp_driver/aflpp_driver.c | 326 --------- examples/aflpp_driver/aflpp_driver_test.c | 32 - examples/aflpp_driver/aflpp_qemu_driver.c | 38 -- examples/aflpp_driver/aflpp_qemu_driver_hook.c | 22 - examples/analysis_scripts/queue2csv.sh | 122 ---- examples/argv_fuzzing/Makefile | 58 -- examples/argv_fuzzing/README.md | 16 - examples/argv_fuzzing/argv-fuzz-inl.h | 90 --- examples/argv_fuzzing/argvfuzz.c | 49 -- examples/asan_cgroups/limit_memory.sh | 157 ----- examples/bash_shellshock/shellshock-fuzz.diff | 59 -- examples/canvas_harness/canvas_harness.html | 170 ----- examples/clang_asm_normalize/as | 75 --- examples/crash_triage/triage_crashes.sh | 115 ---- examples/custom_mutators/Makefile | 7 - examples/custom_mutators/README.md | 35 - examples/custom_mutators/XmlMutatorMin.py | 332 ---------- examples/custom_mutators/common.py | 40 -- examples/custom_mutators/custom_mutator_helpers.h | 342 ---------- examples/custom_mutators/example.c | 376 ----------- examples/custom_mutators/example.py | 186 ------ examples/custom_mutators/post_library_gif.so.c | 165 ----- examples/custom_mutators/post_library_png.so.c | 163 ----- examples/custom_mutators/simple-chunk-replace.py | 64 -- examples/custom_mutators/simple_example.c | 74 --- examples/custom_mutators/wrapper_afl_min.py | 118 ---- examples/defork/Makefile | 64 -- examples/defork/README.md | 11 - examples/defork/defork.c | 50 -- examples/defork/forking_target.c | 49 -- examples/distributed_fuzzing/sync_script.sh | 97 --- examples/libpng_no_checksum/libpng-nocrc.patch | 15 - examples/persistent_mode/Makefile | 10 - examples/persistent_mode/persistent_demo.c | 112 ---- examples/persistent_mode/persistent_demo_new.c | 117 ---- examples/persistent_mode/test-instr.c | 69 -- examples/qemu_persistent_hook/Makefile | 6 - examples/qemu_persistent_hook/README.md | 19 - examples/qemu_persistent_hook/read_into_rdi.c | 34 - examples/qemu_persistent_hook/test.c | 35 - examples/socket_fuzzing/Makefile | 61 -- examples/socket_fuzzing/README.md | 11 - examples/socket_fuzzing/socketfuzz.c | 110 ---- instrumentation/README.gcc_plugin.md | 2 +- instrumentation/README.persistent_mode.md | 4 +- qemu_mode/README.md | 2 +- qemu_mode/README.persistent.md | 2 +- src/afl-as.c | 2 +- test/test-custom-mutators.sh | 8 +- test/test-gcc-plugin.sh | 2 +- test/test-llvm-lto.sh | 2 +- test/test-llvm.sh | 2 +- utils/README.md | 54 ++ utils/afl_frida/GNUmakefile | 23 + utils/afl_frida/Makefile | 2 + utils/afl_frida/README.md | 34 + utils/afl_frida/afl-frida.c | 542 +++++++++++++++ utils/afl_frida/afl-frida.h | 53 ++ utils/afl_frida/libtestinstr.c | 35 + utils/afl_network_proxy/GNUmakefile | 43 ++ utils/afl_network_proxy/Makefile | 2 + utils/afl_network_proxy/README.md | 61 ++ utils/afl_network_proxy/afl-network-client.c | 415 ++++++++++++ utils/afl_network_proxy/afl-network-server.c | 720 ++++++++++++++++++++ utils/afl_proxy/Makefile | 7 + utils/afl_proxy/README.md | 9 + utils/afl_proxy/afl-proxy.c | 238 +++++++ utils/afl_untracer/Makefile | 16 + utils/afl_untracer/README.md | 60 ++ utils/afl_untracer/TODO | 2 + utils/afl_untracer/afl-untracer.c | 768 ++++++++++++++++++++++ utils/afl_untracer/ghidra_get_patchpoints.java | 84 +++ utils/afl_untracer/ida_get_patchpoints.py | 62 ++ utils/afl_untracer/libtestinstr.c | 35 + utils/afl_untracer/patches.txt | 34 + utils/aflpp_driver/GNUmakefile | 46 ++ utils/aflpp_driver/Makefile | 2 + utils/aflpp_driver/aflpp_driver.c | 326 +++++++++ utils/aflpp_driver/aflpp_driver_test.c | 32 + utils/aflpp_driver/aflpp_qemu_driver.c | 38 ++ utils/aflpp_driver/aflpp_qemu_driver_hook.c | 22 + utils/analysis_scripts/queue2csv.sh | 122 ++++ utils/argv_fuzzing/Makefile | 58 ++ utils/argv_fuzzing/README.md | 16 + utils/argv_fuzzing/argv-fuzz-inl.h | 90 +++ utils/argv_fuzzing/argvfuzz.c | 49 ++ utils/asan_cgroups/limit_memory.sh | 157 +++++ utils/bash_shellshock/shellshock-fuzz.diff | 59 ++ utils/canvas_harness/canvas_harness.html | 170 +++++ utils/clang_asm_normalize/as | 75 +++ utils/crash_triage/triage_crashes.sh | 115 ++++ utils/custom_mutators/Makefile | 7 + utils/custom_mutators/README.md | 35 + utils/custom_mutators/XmlMutatorMin.py | 332 ++++++++++ utils/custom_mutators/common.py | 40 ++ utils/custom_mutators/custom_mutator_helpers.h | 342 ++++++++++ utils/custom_mutators/example.c | 376 +++++++++++ utils/custom_mutators/example.py | 186 ++++++ utils/custom_mutators/post_library_gif.so.c | 165 +++++ utils/custom_mutators/post_library_png.so.c | 163 +++++ utils/custom_mutators/simple-chunk-replace.py | 64 ++ utils/custom_mutators/simple_example.c | 74 +++ utils/custom_mutators/wrapper_afl_min.py | 118 ++++ utils/defork/Makefile | 64 ++ utils/defork/README.md | 11 + utils/defork/defork.c | 50 ++ utils/defork/forking_target.c | 49 ++ utils/distributed_fuzzing/sync_script.sh | 97 +++ utils/libpng_no_checksum/libpng-nocrc.patch | 15 + utils/persistent_mode/Makefile | 10 + utils/persistent_mode/persistent_demo.c | 112 ++++ utils/persistent_mode/persistent_demo_new.c | 117 ++++ utils/persistent_mode/test-instr.c | 69 ++ utils/qemu_persistent_hook/Makefile | 6 + utils/qemu_persistent_hook/README.md | 19 + utils/qemu_persistent_hook/read_into_rdi.c | 34 + utils/qemu_persistent_hook/test.c | 35 + utils/socket_fuzzing/Makefile | 61 ++ utils/socket_fuzzing/README.md | 11 + utils/socket_fuzzing/socketfuzz.c | 110 ++++ 156 files changed, 7496 insertions(+), 7494 deletions(-) delete mode 100644 examples/README.md delete mode 100644 examples/afl_frida/GNUmakefile delete mode 100644 examples/afl_frida/Makefile delete mode 100644 examples/afl_frida/README.md delete mode 100644 examples/afl_frida/afl-frida.c delete mode 100644 examples/afl_frida/afl-frida.h delete mode 100644 examples/afl_frida/libtestinstr.c delete mode 100644 examples/afl_network_proxy/GNUmakefile delete mode 100644 examples/afl_network_proxy/Makefile delete mode 100644 examples/afl_network_proxy/README.md delete mode 100644 examples/afl_network_proxy/afl-network-client.c delete mode 100644 examples/afl_network_proxy/afl-network-server.c delete mode 100644 examples/afl_proxy/Makefile delete mode 100644 examples/afl_proxy/README.md delete mode 100644 examples/afl_proxy/afl-proxy.c delete mode 100644 examples/afl_untracer/Makefile delete mode 100644 examples/afl_untracer/README.md delete mode 100644 examples/afl_untracer/TODO delete mode 100644 examples/afl_untracer/afl-untracer.c delete mode 100644 examples/afl_untracer/ghidra_get_patchpoints.java delete mode 100644 examples/afl_untracer/ida_get_patchpoints.py delete mode 100644 examples/afl_untracer/libtestinstr.c delete mode 100755 examples/afl_untracer/libtestinstr.so delete mode 100644 examples/afl_untracer/patches.txt delete mode 100644 examples/aflpp_driver/GNUmakefile delete mode 100644 examples/aflpp_driver/Makefile delete mode 100644 examples/aflpp_driver/aflpp_driver.c delete mode 100644 examples/aflpp_driver/aflpp_driver_test.c delete mode 100644 examples/aflpp_driver/aflpp_qemu_driver.c delete mode 100644 examples/aflpp_driver/aflpp_qemu_driver_hook.c delete mode 100755 examples/analysis_scripts/queue2csv.sh delete mode 100644 examples/argv_fuzzing/Makefile delete mode 100644 examples/argv_fuzzing/README.md delete mode 100644 examples/argv_fuzzing/argv-fuzz-inl.h delete mode 100644 examples/argv_fuzzing/argvfuzz.c delete mode 100755 examples/asan_cgroups/limit_memory.sh delete mode 100644 examples/bash_shellshock/shellshock-fuzz.diff delete mode 100644 examples/canvas_harness/canvas_harness.html delete mode 100755 examples/clang_asm_normalize/as delete mode 100755 examples/crash_triage/triage_crashes.sh delete mode 100644 examples/custom_mutators/Makefile delete mode 100644 examples/custom_mutators/README.md delete mode 100644 examples/custom_mutators/XmlMutatorMin.py delete mode 100644 examples/custom_mutators/common.py delete mode 100644 examples/custom_mutators/custom_mutator_helpers.h delete mode 100644 examples/custom_mutators/example.c delete mode 100644 examples/custom_mutators/example.py delete mode 100644 examples/custom_mutators/post_library_gif.so.c delete mode 100644 examples/custom_mutators/post_library_png.so.c delete mode 100644 examples/custom_mutators/simple-chunk-replace.py delete mode 100644 examples/custom_mutators/simple_example.c delete mode 100644 examples/custom_mutators/wrapper_afl_min.py delete mode 100644 examples/defork/Makefile delete mode 100644 examples/defork/README.md delete mode 100644 examples/defork/defork.c delete mode 100644 examples/defork/forking_target.c delete mode 100755 examples/distributed_fuzzing/sync_script.sh delete mode 100644 examples/libpng_no_checksum/libpng-nocrc.patch delete mode 100644 examples/persistent_mode/Makefile delete mode 100644 examples/persistent_mode/persistent_demo.c delete mode 100644 examples/persistent_mode/persistent_demo_new.c delete mode 100644 examples/persistent_mode/test-instr.c delete mode 100644 examples/qemu_persistent_hook/Makefile delete mode 100644 examples/qemu_persistent_hook/README.md delete mode 100644 examples/qemu_persistent_hook/read_into_rdi.c delete mode 100644 examples/qemu_persistent_hook/test.c delete mode 100644 examples/socket_fuzzing/Makefile delete mode 100644 examples/socket_fuzzing/README.md delete mode 100644 examples/socket_fuzzing/socketfuzz.c create mode 100644 utils/README.md create mode 100644 utils/afl_frida/GNUmakefile create mode 100644 utils/afl_frida/Makefile create mode 100644 utils/afl_frida/README.md create mode 100644 utils/afl_frida/afl-frida.c create mode 100644 utils/afl_frida/afl-frida.h create mode 100644 utils/afl_frida/libtestinstr.c create mode 100644 utils/afl_network_proxy/GNUmakefile create mode 100644 utils/afl_network_proxy/Makefile create mode 100644 utils/afl_network_proxy/README.md create mode 100644 utils/afl_network_proxy/afl-network-client.c create mode 100644 utils/afl_network_proxy/afl-network-server.c create mode 100644 utils/afl_proxy/Makefile create mode 100644 utils/afl_proxy/README.md create mode 100644 utils/afl_proxy/afl-proxy.c create mode 100644 utils/afl_untracer/Makefile create mode 100644 utils/afl_untracer/README.md create mode 100644 utils/afl_untracer/TODO create mode 100644 utils/afl_untracer/afl-untracer.c create mode 100644 utils/afl_untracer/ghidra_get_patchpoints.java create mode 100644 utils/afl_untracer/ida_get_patchpoints.py create mode 100644 utils/afl_untracer/libtestinstr.c create mode 100644 utils/afl_untracer/patches.txt create mode 100644 utils/aflpp_driver/GNUmakefile create mode 100644 utils/aflpp_driver/Makefile create mode 100644 utils/aflpp_driver/aflpp_driver.c create mode 100644 utils/aflpp_driver/aflpp_driver_test.c create mode 100644 utils/aflpp_driver/aflpp_qemu_driver.c create mode 100644 utils/aflpp_driver/aflpp_qemu_driver_hook.c create mode 100755 utils/analysis_scripts/queue2csv.sh create mode 100644 utils/argv_fuzzing/Makefile create mode 100644 utils/argv_fuzzing/README.md create mode 100644 utils/argv_fuzzing/argv-fuzz-inl.h create mode 100644 utils/argv_fuzzing/argvfuzz.c create mode 100755 utils/asan_cgroups/limit_memory.sh create mode 100644 utils/bash_shellshock/shellshock-fuzz.diff create mode 100644 utils/canvas_harness/canvas_harness.html create mode 100755 utils/clang_asm_normalize/as create mode 100755 utils/crash_triage/triage_crashes.sh create mode 100644 utils/custom_mutators/Makefile create mode 100644 utils/custom_mutators/README.md create mode 100644 utils/custom_mutators/XmlMutatorMin.py create mode 100644 utils/custom_mutators/common.py create mode 100644 utils/custom_mutators/custom_mutator_helpers.h create mode 100644 utils/custom_mutators/example.c create mode 100644 utils/custom_mutators/example.py create mode 100644 utils/custom_mutators/post_library_gif.so.c create mode 100644 utils/custom_mutators/post_library_png.so.c create mode 100644 utils/custom_mutators/simple-chunk-replace.py create mode 100644 utils/custom_mutators/simple_example.c create mode 100644 utils/custom_mutators/wrapper_afl_min.py create mode 100644 utils/defork/Makefile create mode 100644 utils/defork/README.md create mode 100644 utils/defork/defork.c create mode 100644 utils/defork/forking_target.c create mode 100755 utils/distributed_fuzzing/sync_script.sh create mode 100644 utils/libpng_no_checksum/libpng-nocrc.patch create mode 100644 utils/persistent_mode/Makefile create mode 100644 utils/persistent_mode/persistent_demo.c create mode 100644 utils/persistent_mode/persistent_demo_new.c create mode 100644 utils/persistent_mode/test-instr.c create mode 100644 utils/qemu_persistent_hook/Makefile create mode 100644 utils/qemu_persistent_hook/README.md create mode 100644 utils/qemu_persistent_hook/read_into_rdi.c create mode 100644 utils/qemu_persistent_hook/test.c create mode 100644 utils/socket_fuzzing/Makefile create mode 100644 utils/socket_fuzzing/README.md create mode 100644 utils/socket_fuzzing/socketfuzz.c (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index 521ab683..309a7d4c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -500,8 +500,8 @@ code-format: ./.custom-format.py -i instrumentation/*.c @#./.custom-format.py -i custom_mutators/*/*.c* # destroys libfuzzer :-( @#./.custom-format.py -i custom_mutators/*/*.h # destroys honggfuzz :-( - ./.custom-format.py -i examples/*/*.c* - ./.custom-format.py -i examples/*/*.h + ./.custom-format.py -i utils/*/*.c* + ./.custom-format.py -i utils/*/*.h ./.custom-format.py -i test/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.c ./.custom-format.py -i qemu_mode/libcompcov/*.cc @@ -547,9 +547,9 @@ clean: -$(MAKE) -f GNUmakefile.gcc_plugin clean $(MAKE) -C libdislocator clean $(MAKE) -C libtokencap clean - $(MAKE) -C examples/afl_network_proxy clean - $(MAKE) -C examples/socket_fuzzing clean - $(MAKE) -C examples/argv_fuzzing clean + $(MAKE) -C utils/afl_network_proxy clean + $(MAKE) -C utils/socket_fuzzing clean + $(MAKE) -C utils/argv_fuzzing clean $(MAKE) -C qemu_mode/unsigaction clean $(MAKE) -C qemu_mode/libcompcov clean ifeq "$(IN_REPO)" "1" @@ -572,10 +572,10 @@ distrib: all -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/aflpp_driver - $(MAKE) -C examples/afl_network_proxy - $(MAKE) -C examples/socket_fuzzing - $(MAKE) -C examples/argv_fuzzing + $(MAKE) -C utils/aflpp_driver + $(MAKE) -C utils/afl_network_proxy + $(MAKE) -C utils/socket_fuzzing + $(MAKE) -C utils/argv_fuzzing -cd qemu_mode && sh ./build_qemu_support.sh -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh @@ -583,9 +583,9 @@ distrib: all binary-only: all $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/afl_network_proxy - $(MAKE) -C examples/socket_fuzzing - $(MAKE) -C examples/argv_fuzzing + $(MAKE) -C utils/afl_network_proxy + $(MAKE) -C utils/socket_fuzzing + $(MAKE) -C utils/argv_fuzzing -cd qemu_mode && sh ./build_qemu_support.sh -cd unicorn_mode && unset CFLAGS && sh ./build_unicorn_support.sh @@ -595,7 +595,7 @@ source-only: all -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - $(MAKE) -C examples/aflpp_driver + $(MAKE) -C utils/aflpp_driver %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ @@ -628,11 +628,11 @@ install: all $(MANPAGES) @if [ -f libtokencap.so ]; then set -e; install -m 755 libtokencap.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f libcompcov.so ]; then set -e; install -m 755 libcompcov.so $${DESTDIR}$(HELPER_PATH); fi @if [ -f afl-fuzz-document ]; then set -e; install -m 755 afl-fuzz-document $${DESTDIR}$(BIN_PATH); fi - @if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C examples/socket_fuzzing install; fi - @if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C examples/argv_fuzzing install; fi - @if [ -f examples/afl_network_proxy/afl-network-server ]; then $(MAKE) -C examples/afl_network_proxy install; fi - @if [ -f examples/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi - @if [ -f examples/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 examples/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi + @if [ -f socketfuzz32.so -o -f socketfuzz64.so ]; then $(MAKE) -C utils/socket_fuzzing install; fi + @if [ -f argvfuzz32.so -o -f argvfuzz64.so ]; then $(MAKE) -C utils/argv_fuzzing install; fi + @if [ -f utils/afl_network_proxy/afl-network-server ]; then $(MAKE) -C utils/afl_network_proxy install; fi + @if [ -f utils/aflpp_driver/libAFLDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLDriver.a $${DESTDIR}$(HELPER_PATH); fi + @if [ -f utils/aflpp_driver/libAFLQemuDriver.a ]; then set -e; install -m 644 utils/aflpp_driver/libAFLQemuDriver.a $${DESTDIR}$(HELPER_PATH); fi -$(MAKE) -f GNUmakefile.llvm install -$(MAKE) -f GNUmakefile.gcc_plugin install ln -sf afl-cc $${DESTDIR}$(BIN_PATH)/afl-gcc diff --git a/README.md b/README.md index d7cad092..b00e5d00 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). + * utils/ got renamed to utils/ ## Contents @@ -760,10 +761,10 @@ cd unicorn_mode If the goal is to fuzz a dynamic library then there are two options available. For both you need to write a small hardness that loads and calls the library. -Faster is the frida solution: [examples/afl_frida/README.md](examples/afl_frida/README.md) +Faster is the frida solution: [utils/afl_frida/README.md](utils/afl_frida/README.md) Another, less precise and slower option is using ptrace with debugger interrupt -instrumentation: [examples/afl_untracer/README.md](examples/afl_untracer/README.md) +instrumentation: [utils/afl_untracer/README.md](utils/afl_untracer/README.md) ### More @@ -1037,7 +1038,7 @@ Here are some of the most important caveats for AFL: wholly wrap the actual data format to be tested. To work around this, you can comment out the relevant checks (see - examples/libpng_no_checksum/ for inspiration); if this is not possible, + utils/libpng_no_checksum/ for inspiration); if this is not possible, you can also write a postprocessor, one of the hooks of custom mutators. See [docs/custom_mutators.md](docs/custom_mutators.md) on how to use `AFL_CUSTOM_MUTATOR_LIBRARY` diff --git a/docs/Changelog.md b/docs/Changelog.md index 7fa7ff53..fd30c7b0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,7 @@ sending a mail to . ### Version ++3.00a (develop) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ + - examples/ renamed to utils/ - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz diff --git a/docs/FAQ.md b/docs/FAQ.md index 064638f4..714d50eb 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -63,7 +63,7 @@ x10 - that is a x100 difference!). If modifying the source is not an option (e.g. because you only have a binary and perform binary fuzzing) you can also use a shared library with AFL_PRELOAD to emulate the network. This is also much faster than the real network would be. -See [examples/socket_fuzzing/](../examples/socket_fuzzing/). +See [utils/socket_fuzzing/](../utils/socket_fuzzing/). There is an outdated afl++ branch that implements networking if you are desperate though: [https://github.com/AFLplusplus/AFLplusplus/tree/networking](https://github.com/AFLplusplus/AFLplusplus/tree/networking) - diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index cb1288ef..66734452 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -15,7 +15,7 @@ high enough. Otherwise try retrowrite, afl-dyninst and if these fail too then try standard qemu_mode with AFL_ENTRYPOINT to where you need it. - If your target is a library use examples/afl_frida/. + If your target is a library use utils/afl_frida/. If your target is non-linux then use unicorn_mode/. @@ -65,14 +65,14 @@ ## AFL FRIDA If you want to fuzz a binary-only shared library then you can fuzz it with - frida-gum via examples/afl_frida/, you will have to write a harness to + frida-gum via utils/afl_frida/, you will have to write a harness to call the target function in the library, use afl-frida.c as a template. ## AFL UNTRACER If you want to fuzz a binary-only shared library then you can fuzz it with - examples/afl_untracer/, use afl-untracer.c as a template. + utils/afl_untracer/, use afl-untracer.c as a template. It is slower than AFL FRIDA (see above). diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 53f783fe..6e16ba0f 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -268,8 +268,8 @@ afl-fuzz /path/to/program ## 4) Example -Please see [example.c](../examples/custom_mutators/example.c) and -[example.py](../examples/custom_mutators/example.py) +Please see [example.c](../utils/custom_mutators/example.c) and +[example.py](../utils/custom_mutators/example.py) ## 5) Other Resources diff --git a/docs/env_variables.md b/docs/env_variables.md index f7b4c994..ada89257 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -55,7 +55,7 @@ make fairly broad use of environmental variables instead: in your `$PATH`. - `AFL_PATH` can be used to point afl-gcc to an alternate location of afl-as. - One possible use of this is examples/clang_asm_normalize/, which lets + One possible use of this is utils/clang_asm_normalize/, which lets you instrument hand-written assembly when compiling clang code by plugging a normalizer into the chain. (There is no equivalent feature for GCC.) diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index 323f16f1..77845c63 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -78,10 +78,10 @@ Be sure to check out docs/sister_projects.md before writing your own. ## Need to fuzz the command-line arguments of a particular program? -You can find a simple solution in examples/argv_fuzzing. +You can find a simple solution in utils/argv_fuzzing. ## Attacking a format that uses checksums? Remove the checksum-checking code or use a postprocessor! -See examples/custom_mutators/ for more. +See utils/custom_mutators/ for more. diff --git a/docs/notes_for_asan.md b/docs/notes_for_asan.md index 2e18c15f..f08ae3fb 100644 --- a/docs/notes_for_asan.md +++ b/docs/notes_for_asan.md @@ -20,7 +20,7 @@ Because of this, fuzzing with ASAN is recommended only in four scenarios: - Precisely gauge memory needs using http://jwilk.net/software/recidivm . - Limit the memory available to process using cgroups on Linux (see - examples/asan_cgroups). + utils/asan_cgroups). To compile with ASAN, set AFL_USE_ASAN=1 before calling 'make clean all'. The afl-gcc / afl-clang wrappers will pick that up and add the appropriate flags. @@ -74,7 +74,7 @@ There are also cgroups, but they are Linux-specific, not universally available even on Linux systems, and they require root permissions to set up; I'm a bit hesitant to make afl-fuzz require root permissions just for that. That said, if you are on Linux and want to use cgroups, check out the contributed script -that ships in examples/asan_cgroups/. +that ships in utils/asan_cgroups/. In settings where cgroups aren't available, we have no nice, portable way to avoid counting the ASAN allocation toward the limit. On 32-bit systems, or for diff --git a/docs/parallel_fuzzing.md b/docs/parallel_fuzzing.md index bf57ace8..8f2afe1b 100644 --- a/docs/parallel_fuzzing.md +++ b/docs/parallel_fuzzing.md @@ -152,7 +152,7 @@ write a simple script that performs two actions: done ``` -There is an example of such a script in examples/distributed_fuzzing/. +There is an example of such a script in utils/distributed_fuzzing/. There are other (older) more featured, experimental tools: * https://github.com/richo/roving diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 7dd70d6a..00000000 --- a/examples/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# AFL++ Examples - -Here's a quick overview of the stuff you can find in this directory: - - - afl_network_proxy - fuzz a target over the network: afl-fuzz on - a host, target on an embedded system. - - - afl_proxy - skeleton file example to show how to fuzz - something where you gather coverage data via - different means, e.g. hw debugger - - - afl_untracer - fuzz binary-only libraries much faster but with - less coverage than qemu_mode - - - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed - (e.g., to test setuid programs). - - - asan_cgroups - a contributed script to simplify fuzzing ASAN - binaries with robust memory limits on Linux. - - - bash_shellshock - a simple hack used to find a bunch of - post-Shellshock bugs in bash. - - - canvas_harness - a test harness used to find browser bugs with a - corpus generated using simple image parsing - binaries & afl-fuzz. - - - clang_asm_normalize - a script that makes it easy to instrument - hand-written assembly, provided that you have clang. - - - crash_triage - a very rudimentary example of how to annotate crashes - with additional gdb metadata. - - - custom_mutators - examples for the afl++ custom mutator interface in - C and Python - - - distributed_fuzzing - a sample script for synchronizing fuzzer instances - across multiple machines (see parallel_fuzzing.md). - - - libpng_no_checksum - a sample patch for removing CRC checks in libpng. - - - persistent_mode - an example of how to use the LLVM persistent process - mode to speed up certain fuzzing jobs. - - - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin - for fuzzing access with afl++ - -Note that the minimize_corpus.sh tool has graduated from the examples/ -directory and is now available as ../afl-cmin. The LLVM mode has likewise -graduated to ../instrumentation/*. - -Most of the tools in this directory are meant chiefly as examples that need to -be tweaked for your specific needs. They come with some basic documentation, -but are not necessarily production-grade. diff --git a/examples/afl_frida/GNUmakefile b/examples/afl_frida/GNUmakefile deleted file mode 100644 index c154f3a4..00000000 --- a/examples/afl_frida/GNUmakefile +++ /dev/null @@ -1,23 +0,0 @@ -ifdef DEBUG - OPT=-O0 -D_DEBUG=\"1\" -else - OPT=-O3 -funroll-loops -endif - -all: afl-frida libtestinstr.so - -libfrida-gum.a: - @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest - @exit 1 - -afl-frida: afl-frida.c libfrida-gum.a - $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-frida *~ core *.o libtestinstr.so - -deepclean: clean - rm -f libfrida-gum.a frida-gum* diff --git a/examples/afl_frida/Makefile b/examples/afl_frida/Makefile deleted file mode 100644 index 0b306dde..00000000 --- a/examples/afl_frida/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @echo please use GNU make, thanks! diff --git a/examples/afl_frida/README.md b/examples/afl_frida/README.md deleted file mode 100644 index 7743479b..00000000 --- a/examples/afl_frida/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# afl-frida - faster fuzzing of binary-only libraries - -## Introduction - -afl-frida is an example skeleton file which can easily be used to fuzz -a closed source library. - -It requires less memory and is x5-10 faster than qemu_mode but does not -provide interesting features like compcov or cmplog. - -## How-to - -### Modify afl-frida.c - -Read and modify afl-frida.c then `make`. -To adapt afl-frida.c to your needs, read the header of the file and then -search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. - -### Fuzzing - -Example (after modifying afl-frida.c to your needs and compile it): -``` -LD_LIBRARY_PATH=/path/to/the/target/library afl-fuzz -i in -o out -- ./afl-frida -``` -(or even remote via afl-network-proxy). - -# Speed and stability - -The speed is very good, about x12 of fork() qemu_mode. -However the stability is low. Reason is currently unknown. - -# Background - -This code is copied for a larger part from https://github.com/meme/hotwax diff --git a/examples/afl_frida/afl-frida.c b/examples/afl_frida/afl-frida.c deleted file mode 100644 index 31bf8f25..00000000 --- a/examples/afl_frida/afl-frida.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - american fuzzy lop++ - afl-frida skeleton example - ------------------------------------------------- - - Copyright 2020 AFLplusplus Project. All rights reserved. - - Written mostly by meme -> https://github.com/meme/hotwax - - Modifications by Marc Heuse - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - HOW-TO - ====== - - You only need to change the following: - - 1. set the defines and function call parameters. - 2. dl load the library you want to fuzz, lookup the functions you need - and setup the calls to these. - 3. in the while loop you call the functions in the necessary order - - incl the cleanup. the cleanup is important! - - Just look these steps up in the code, look for "// STEP x:" - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef __APPLE__ - #include - #include -#endif - -int debug = 0; - -// STEP 1: - -// The presets are for the example libtestinstr.so: - -/* What is the name of the library to fuzz */ -#define TARGET_LIBRARY "libtestinstr.so" - -/* What is the name of the function to fuzz */ -#define TARGET_FUNCTION "testinstr" - -/* here you need to specify the parameter for the target function */ -static void *(*o_function)(uint8_t *, int); - -// END STEP 1 - -#include "frida-gum.h" - -G_BEGIN_DECLS - -#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) -G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, - FAKE_EVENT_SINK, GObject) - -struct _GumFakeEventSink { - - GObject parent; - GumEventType mask; - -}; - -GumEventSink *gum_fake_event_sink_new(void); -void gum_fake_event_sink_reset(GumFakeEventSink *self); - -G_END_DECLS - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data); -static void gum_fake_event_sink_finalize(GObject *obj); -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink); -static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev); -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data); -void afl_setup(void); -void afl_start_forkserver(void); -int __afl_persistent_loop(unsigned int max_cnt); - -static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) { - - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = gum_fake_event_sink_finalize; - -} - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data) { - - GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface; - iface->query_mask = gum_fake_event_sink_query_mask; - iface->process = gum_fake_event_sink_process; - -} - -G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK, - gum_fake_event_sink_iface_init)) - -#include "../../config.h" - -// Shared memory fuzzing. -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; - -// Notify AFL about persistent mode. -static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); - -// Notify AFL about deferred forkserver. -static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); - -// Because we do our own logging. -extern uint8_t * __afl_area_ptr; -static __thread guint64 previous_pc; - -// Frida stuff below. -typedef struct { - - GumAddress base_address; - guint64 code_start, code_end; - -} range_t; - -inline static void afl_maybe_log(guint64 current_pc) { - - // fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc); - - current_pc = (current_pc >> 4) ^ (current_pc << 8); - current_pc &= MAP_SIZE - 1; - - __afl_area_ptr[current_pc ^ previous_pc]++; - previous_pc = current_pc >> 1; - -} - -static void on_basic_block(GumCpuContext *context, gpointer user_data) { - - afl_maybe_log((guint64)user_data); - -} - -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data) { - - range_t *range = (range_t *)user_data; - - const cs_insn *instr; - gboolean begin = TRUE; - while (gum_stalker_iterator_next(iterator, &instr)) { - - if (begin) { - - if (instr->address >= range->code_start && - instr->address <= range->code_end) { - - gum_stalker_iterator_put_callout(iterator, on_basic_block, - (gpointer)instr->address, NULL); - begin = FALSE; - - } - - } - - gum_stalker_iterator_keep(iterator); - - } - -} - -static void gum_fake_event_sink_init(GumFakeEventSink *self) { - -} - -static void gum_fake_event_sink_finalize(GObject *obj) { - - G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj); - -} - -GumEventSink *gum_fake_event_sink_new(void) { - - GumFakeEventSink *sink; - sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL); - return GUM_EVENT_SINK(sink); - -} - -void gum_fake_event_sink_reset(GumFakeEventSink *self) { - -} - -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) { - - return 0; - -} - -typedef struct library_list { - - uint8_t *name; - uint64_t addr_start, addr_end; - -} library_list_t; - -#define MAX_LIB_COUNT 256 -static library_list_t liblist[MAX_LIB_COUNT]; -static u32 liblist_cnt; - -void read_library_information() { - -#if defined(__linux__) - FILE *f; - u8 buf[1024], *b, *m, *e, *n; - - if ((f = fopen("/proc/self/maps", "r")) == NULL) { - - fprintf(stderr, "Error: cannot open /proc/self/maps\n"); - exit(-1); - - } - - if (debug) fprintf(stderr, "Library list:\n"); - while (fgets(buf, sizeof(buf), f)) { - - if (strstr(buf, " r-x")) { - - if (liblist_cnt >= MAX_LIB_COUNT) { - - fprintf( - stderr, - "Warning: too many libraries to old, maximum count of %d reached\n", - liblist_cnt); - return; - - } - - b = buf; - m = index(buf, '-'); - e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); - if (n && - ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) - n = NULL; - else - n++; - if (b && m && e && n && *n) { - - *m++ = 0; - *e = 0; - if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - - if (rindex(n, '/') != NULL) { - - n = rindex(n, '/'); - n++; - - } - - liblist[liblist_cnt].name = strdup(n); - liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); - liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); - if (debug) - fprintf( - stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - liblist_cnt++; - - } - - } - - } - - if (debug) fprintf(stderr, "\n"); - -#elif defined(__FreeBSD__) - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - char * buf, *start, *end; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (debug) fprintf(stderr, "Library list:\n"); - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } - - len = len * 4 / 3; - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) { return; } - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - start = buf; - end = buf + len; - - while (start < end) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; - size_t size = region->kve_structsize; - - if (size == 0) { break; } - - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_EXEC)) { - - liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; - liblist[liblist_cnt].addr_start = region->kve_start; - liblist[liblist_cnt].addr_end = region->kve_end; - - if (debug) { - - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - - } - - liblist_cnt++; - - } - - start += size; - - } - -#endif - -} - -library_list_t *find_library(char *name) { - - char *filename = rindex(name, '/'); - - if (filename) - filename++; - else - filename = name; - -#if defined(__linux__) - u32 i; - for (i = 0; i < liblist_cnt; i++) - if (strcmp(liblist[i].name, filename) == 0) return &liblist[i]; -#elif defined(__APPLE__) && defined(__LP64__) - kern_return_t err; - static library_list_t lib; - - // get the list of all loaded modules from dyld - // the task_info mach API will get the address of the dyld all_image_info - // struct for the given task from which we can get the names and load - // addresses of all modules - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - err = task_info(mach_task_self(), TASK_DYLD_INFO, - (task_info_t)&task_dyld_info, &count); - - const struct dyld_all_image_infos *all_image_infos = - (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; - const struct dyld_image_info *image_infos = all_image_infos->infoArray; - - for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { - - const char * image_name = image_infos[i].imageFilePath; - mach_vm_address_t image_load_address = - (mach_vm_address_t)image_infos[i].imageLoadAddress; - if (strstr(image_name, name)) { - - lib.name = name; - lib.addr_start = (u64)image_load_address; - lib.addr_end = 0; - return &lib; - - } - - } - -#endif - - return NULL; - -} - -static void gum_fake_event_sink_process(GumEventSink * sink, - const GumEvent *ev) { - -} - -/* Because this CAN be called more than once, it will return the LAST range */ -static int enumerate_ranges(const GumRangeDetails *details, - gpointer user_data) { - - GumMemoryRange *code_range = (GumMemoryRange *)user_data; - memcpy(code_range, details->range, sizeof(*code_range)); - return 0; - -} - -int main() { - -#ifndef __APPLE__ - (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR -#endif - - // STEP 2: load the library you want to fuzz and lookup the functions, - // inclusive of the cleanup functions. - // If there is just one function, then there is nothing to change - // or add here. - - void *dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); - if (!dl) { - - fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY); - exit(-1); - - } - - if (!(o_function = dlsym(dl, TARGET_FUNCTION))) { - - fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION); - exit(-1); - - } - - // END STEP 2 - - read_library_information(); - library_list_t *lib = find_library(TARGET_LIBRARY); - - if (lib == NULL) { - - fprintf(stderr, "Could not find target library\n"); - exit(-1); - - } - - gum_init_embedded(); - if (!gum_stalker_is_supported()) { - - gum_deinit_embedded(); - return 1; - - } - - GumStalker *stalker = gum_stalker_new(); - - /* - This does not work here as we load a shared library. pretty sure this - would also be easily solvable with frida gum, but I already have all the - code I need from afl-untracer - - GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); - GumMemoryRange code_range; - gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, - &code_range); - guint64 code_start = code_range.base_address - base_address; - guint64 code_end = (code_range.base_address + code_range.size) - base_address; - range_t instr_range = {base_address, code_start, code_end}; - */ - range_t instr_range = {0, lib->addr_start, lib->addr_end}; - - GumStalkerTransformer *transformer = - gum_stalker_transformer_make_from_callback(instr_basic_block, - &instr_range, NULL); - - GumEventSink *event_sink = gum_fake_event_sink_new(); - - // to ensure that the signatures are not optimized out - memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); - memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, - sizeof(AFL_DEFER_FORKSVR) + 1); - __afl_manual_init(); - - // - // any expensive target library initialization that has to be done just once - // - put that here - // - - gum_stalker_follow_me(stalker, transformer, event_sink); - - while (__afl_persistent_loop(UINT32_MAX) != 0) { - - previous_pc = 0; // Required! - -#ifdef _DEBUG - fprintf(stderr, "CLIENT crc: %016llx len: %u\n", - hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); - fprintf(stderr, "RECV:"); - for (int i = 0; i < *__afl_fuzz_len; i++) - fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); - fprintf(stderr, "\n"); -#endif - - // STEP 3: ensure the minimum length is present and setup the target - // function to fuzz. - - if (*__afl_fuzz_len > 0) { - - __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate - (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); - - } - - // END STEP 3 - - } - - gum_stalker_unfollow_me(stalker); - - while (gum_stalker_garbage_collect(stalker)) - g_usleep(10000); - - g_object_unref(stalker); - g_object_unref(transformer); - g_object_unref(event_sink); - gum_deinit_embedded(); - - return 0; - -} - diff --git a/examples/afl_frida/afl-frida.h b/examples/afl_frida/afl-frida.h deleted file mode 100644 index efa3440f..00000000 --- a/examples/afl_frida/afl-frida.h +++ /dev/null @@ -1,53 +0,0 @@ -extern int is_persistent; - -G_BEGIN_DECLS - -#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) - -G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, - FAKE_EVENT_SINK, GObject) - -struct _GumFakeEventSink { - - GObject parent; - GumEventType mask; - -}; - -GumEventSink *gum_fake_event_sink_new(void); -void gum_fake_event_sink_reset(GumFakeEventSink *self); - -G_END_DECLS - -typedef struct { - - GumAddress base_address; - guint64 code_start, code_end; - -} range_t; - -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data); -#pragma once - -void afl_setup(void); -void afl_start_forkserver(void); -int __afl_persistent_loop(unsigned int max_cnt); - -inline static inline void afl_maybe_log(guint64 current_pc) { - - extern unsigned int afl_instr_rms; - extern uint8_t * afl_area_ptr; - - static __thread guint64 previous_pc; - - current_pc = (current_pc >> 4) ^ (current_pc << 8); - current_pc &= MAP_SIZE - 1; - - if (current_pc >= afl_instr_rms) return; - - afl_area_ptr[current_pc ^ previous_pc]++; - previous_pc = current_pc >> 1; - -} - diff --git a/examples/afl_frida/libtestinstr.c b/examples/afl_frida/libtestinstr.c deleted file mode 100644 index 96b1cf21..00000000 --- a/examples/afl_frida/libtestinstr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -void testinstr(char *buf, int len) { - - if (len < 1) return; - buf[len] = 0; - - // we support three input cases - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - -} - diff --git a/examples/afl_network_proxy/GNUmakefile b/examples/afl_network_proxy/GNUmakefile deleted file mode 100644 index 25a3df82..00000000 --- a/examples/afl_network_proxy/GNUmakefile +++ /dev/null @@ -1,43 +0,0 @@ -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -DOC_PATH = $(PREFIX)/share/doc/afl - -PROGRAMS = afl-network-client afl-network-server - -HASH=\# - -CFLAGS += -Wno-pointer-sign - -ifdef STATIC - CFLAGS += -static -endif - -ifeq "$(shell echo '$(HASH)include @int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" - CFLAGS += -DUSE_DEFLATE=1 - LDFLAGS += -ldeflate - $(info libdeflate-dev was detected, using compression) -else - $(warn did not find libdeflate-dev, cannot use compression) -endif - -all: $(PROGRAMS) - -help: - @echo make options: - @echo STATIC - build as static binaries - @echo COMPRESS_TESTCASES - compress test cases - -afl-network-client: afl-network-client.c - $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) - -afl-network-server: afl-network-server.c - $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) - -clean: - rm -f $(PROGRAMS) *~ core - -install: all - install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH) - install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH) - install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md - diff --git a/examples/afl_network_proxy/Makefile b/examples/afl_network_proxy/Makefile deleted file mode 100644 index 0b306dde..00000000 --- a/examples/afl_network_proxy/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @echo please use GNU make, thanks! diff --git a/examples/afl_network_proxy/README.md b/examples/afl_network_proxy/README.md deleted file mode 100644 index a5ac3578..00000000 --- a/examples/afl_network_proxy/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# afl-network-proxy - -If you want to run afl-fuzz over the network than this is what you need :) -Note that the impact on fuzzing speed will be huge, expect a loss of 90%. - -## When to use this - -1. when you have to fuzz a target that has to run on a system that cannot - contain the fuzzing output (e.g. /tmp too small and file system is read-only) -2. when the target instantly reboots on crashes -3. ... any other reason you would need this - -## how to get it running - -### Compiling - -Just type `make` and let the autodetection do everything for you. - -Note that you will get a 40-50% performance increase if you have libdeflate-dev -installed. The GNUmakefile will autodetect it if present. - -If your target has large test cases (10+kb) that are ascii only or large chunks -of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them. -For most targets this hurts performance though so it is disabled by default. - -### on the target - -Run `afl-network-server` with your target with the -m and -t values you need. -Important is the -i parameter which is the TCP port to listen on. -e.g.: -``` -afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@ -``` - -### on the (afl-fuzz) master - -Just run afl-fuzz with your normal options, however the target should be -`afl-network-client` with the IP and PORT of the `afl-network-server` and -increase the -t value: -``` -afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111 -``` -Note the '+' on the -t parameter value. The afl-network-server will take -care of proper timeouts hence afl-fuzz should not. The '+' increases the -timeout and the value itself should be 500-1000 higher than the one on -afl-network-server. - -### networking - -The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to -either. Note that also the outgoing interface can be specified with a '%' for -`afl-network-client`, e.g. `fe80::1234%eth0`. - -Also make sure your default TCP window size is larger than your MAP_SIZE -(130kb is a good value). -On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem` - -## how to compile and install - -`make && sudo make install` - diff --git a/examples/afl_network_proxy/afl-network-client.c b/examples/afl_network_proxy/afl-network-client.c deleted file mode 100644 index a2451fdc..00000000 --- a/examples/afl_network_proxy/afl-network-client.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - american fuzzy lop++ - afl-network-client - --------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#ifndef USEMMAP - #include -#endif -#include -#include -#include -#include -#include - -#ifdef USE_DEFLATE - #include -#endif - -u8 *__afl_area_ptr; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -#else -__thread u32 __afl_map_size = MAP_SIZE; -#endif - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - -} - -static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status, res = 0x0fffffff; // res is a dummy pid - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) return 0; - - /* we have a testcase - read it */ - status = read(0, buf, max_len); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; - - if (status < 1) - return 0; - else - return status; - -} - -static void __afl_end_testcase(int status) { - - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); - -} - -/* you just need to modify the while() loop in this main() */ - -int main(int argc, char *argv[]) { - - u8 * interface, *buf, *ptr; - s32 s = -1; - struct addrinfo hints, *hres, *aip; - u32 * lenptr, max_len = 65536; -#ifdef USE_DEFLATE - u8 * buf2; - u32 * lenptr1, *lenptr2, buf2_len, compress_len; - size_t decompress_len; -#endif - - if (argc < 3 || argc > 4) { - - printf("Syntax: %s host port [max-input-size]\n\n", argv[0]); - printf("Requires host and port of the remote afl-proxy-server instance.\n"); - printf( - "IPv4 and IPv6 are supported, also binding to an interface with " - "\"%%\"\n"); - printf("The max-input-size default is %u.\n", max_len); - printf( - "The default map size is %u and can be changed with setting " - "AFL_MAP_SIZE.\n", - __afl_map_size); - exit(-1); - - } - - if ((interface = strchr(argv[1], '%')) != NULL) *interface++ = 0; - - if (argc > 3) - if ((max_len = atoi(argv[3])) < 0) - FATAL("max-input-size may not be negative or larger than 2GB: %s", - argv[3]); - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) - if ((__afl_map_size = atoi(ptr)) < 8) - FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr); - - if ((buf = malloc(max_len + 4)) == NULL) - PFATAL("can not allocate %u memory", max_len + 4); - lenptr = (u32 *)buf; - -#ifdef USE_DEFLATE - buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size); - if ((buf2 = malloc(buf2_len + 8)) == NULL) - PFATAL("can not allocate %u memory", buf2_len + 8); - lenptr1 = (u32 *)buf2; - lenptr2 = (u32 *)(buf2 + 4); -#endif - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; - - if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0) - PFATAL("could not resolve target %s", argv[1]); - - for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) { - - if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) { - -#ifdef SO_BINDTODEVICE - if (interface != NULL) - if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface, - strlen(interface) + 1) < 0) - fprintf(stderr, "Warning: could not bind to device %s\n", interface); -#else - fprintf(stderr, - "Warning: binding to interface is not supported for your OS\n"); -#endif - -#ifdef SO_PRIORITY - int priority = 7; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) { - - priority = 6; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, - sizeof(priority)) < 0) - WARNF("could not set priority on socket"); - - } - -#endif - - if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1; - - } - - } - -#ifdef USE_DEFLATE - struct libdeflate_compressor *compressor; - compressor = libdeflate_alloc_compressor(1); - struct libdeflate_decompressor *decompressor; - decompressor = libdeflate_alloc_decompressor(); - fprintf(stderr, "Compiled with compression support\n"); -#endif - - if (s == -1) - FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]); - else - fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]); - - /* we initialize the shared memory map and start the forkserver */ - __afl_map_shm(); - __afl_start_forkserver(); - - int i = 1, j, status, ret, received; - - // fprintf(stderr, "Waiting for first testcase\n"); - while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) { - - // fprintf(stderr, "Sending testcase with len %u\n", *lenptr); -#ifdef USE_DEFLATE - #ifdef COMPRESS_TESTCASES - // we only compress the testcase if it does not fit in the TCP packet - if (*lenptr > 1500 - 20 - 32 - 4) { - - // set highest byte to signify compression - *lenptr1 = (*lenptr | 0xff000000); - *lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr, - buf2 + 8, buf2_len); - if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8) - PFATAL("sending test data failed"); - // fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2); - // for (u32 i = 0; i < *lenptr; i++) - // fprintf(stderr, "%02x", buf[i + 4]); - // fprintf(stderr, "\n"); - // for (u32 i = 0; i < *lenptr2; i++) - // fprintf(stderr, "%02x", buf2[i + 8]); - // fprintf(stderr, "\n"); - - } else { - - #endif -#endif - if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4) - PFATAL("sending test data failed"); -#ifdef USE_DEFLATE - #ifdef COMPRESS_TESTCASES - // fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr); - - } - - #endif -#endif - - received = 0; - while (received < 4 && - (ret = recv(s, &status + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) - FATAL("did not receive waitpid data (%d, %d)", received, ret); - // fprintf(stderr, "Received status\n"); - - received = 0; -#ifdef USE_DEFLATE - while (received < 4 && - (ret = recv(s, &compress_len + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) - FATAL("did not receive compress_len (%d, %d)", received, ret); - // fprintf(stderr, "Received status\n"); - - received = 0; - while (received < compress_len && - (ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0) - received += ret; - if (received != compress_len) - FATAL("did not receive coverage data (%d, %d)", received, ret); - - if (libdeflate_deflate_decompress(decompressor, buf2, compress_len, - __afl_area_ptr, __afl_map_size, - &decompress_len) != LIBDEFLATE_SUCCESS || - decompress_len != __afl_map_size) - FATAL("decompression failed"); - // fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len); - // for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x", - // __afl_area_ptr[i]); fprintf(stderr, "\n"); -#else - while (received < __afl_map_size && - (ret = recv(s, __afl_area_ptr + received, __afl_map_size - received, - 0)) > 0) - received += ret; - if (received != __afl_map_size) - FATAL("did not receive coverage data (%d, %d)", received, ret); -#endif - // fprintf(stderr, "Received coverage\n"); - - /* report the test case is done and wait for the next */ - __afl_end_testcase(status); - // fprintf(stderr, "Waiting for next testcase %d\n", ++i); - - } - -#ifdef USE_DEFLATE - libdeflate_free_compressor(compressor); - libdeflate_free_decompressor(decompressor); -#endif - - return 0; - -} - diff --git a/examples/afl_network_proxy/afl-network-server.c b/examples/afl_network_proxy/afl-network-server.c deleted file mode 100644 index 513dc8f2..00000000 --- a/examples/afl_network_proxy/afl-network-server.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - american fuzzy lop++ - network proxy server - ------------------------------------------- - - Originally written by Michal Zalewski - - Forkserver design by Jann Horn - - Now maintained by Marc Heuse , - Heiko Eißfeldt and - Andrea Fioraldi and - Dominik Maier - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define AFL_MAIN - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" -#include "hash.h" -#include "forkserver.h" -#include "sharedmem.h" -#include "common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_DEFLATE - #include -struct libdeflate_compressor * compressor; -struct libdeflate_decompressor *decompressor; -#endif - -static u8 *in_file, /* Minimizer input test case */ - *out_file; - -static u8 *in_data; /* Input data for trimming */ -static u8 *buf2; - -static s32 in_len; -static s32 buf2_len; -static u32 map_size = MAP_SIZE; - -static volatile u8 stop_soon; /* Ctrl-C pressed? */ - -/* See if any bytes are set in the bitmap. */ - -static inline u8 anything_set(afl_forkserver_t *fsrv) { - - u32 *ptr = (u32 *)fsrv->trace_bits; - u32 i = (map_size >> 2); - - while (i--) { - - if (*(ptr++)) { return 1; } - - } - - return 0; - -} - -static void at_exit_handler(void) { - - afl_fsrv_killall(); - -} - -/* Write output file. */ - -static s32 write_to_file(u8 *path, u8 *mem, u32 len) { - - s32 ret; - - unlink(path); /* Ignore errors */ - - ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); - - if (ret < 0) { PFATAL("Unable to create '%s'", path); } - - ck_write(ret, mem, len, path); - - lseek(ret, 0, SEEK_SET); - - return ret; - -} - -/* Execute target application. Returns 0 if the changes are a dud, or - 1 if they should be kept. */ - -static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, - u8 first_run) { - - afl_fsrv_write_to_testcase(fsrv, mem, len); - - fsrv_run_result_t ret = - afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon); - - if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); } - - if (stop_soon) { - - SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST); - exit(1); - - } - - return ret; - -} - -/* Handle Ctrl-C and the like. */ - -static void handle_stop_sig(int sig) { - - stop_soon = 1; - afl_fsrv_killall(); - -} - -/* Do basic preparations - persistent fds, filenames, etc. */ - -static void set_up_environment(afl_forkserver_t *fsrv) { - - u8 *x; - - fsrv->dev_null_fd = open("/dev/null", O_RDWR); - if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } - - if (!out_file) { - - u8 *use_dir = "."; - - if (access(use_dir, R_OK | W_OK | X_OK)) { - - use_dir = get_afl_env("TMPDIR"); - if (!use_dir) { use_dir = "/tmp"; } - - } - - out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid()); - - } - - unlink(out_file); - - fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); - - if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } - - /* Set sane defaults... */ - - x = get_afl_env("ASAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "abort_on_error=1")) { - - FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); - - } - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - x = get_afl_env("MSAN_OPTIONS"); - - if (x) { - - if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) { - - FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY( - MSAN_ERROR) " - please fix!"); - - } - - if (!strstr(x, "symbolize=0")) { - - FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); - - } - - } - - setenv("ASAN_OPTIONS", - "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", - 0); - - setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" - "symbolize=0:" - "abort_on_error=1:" - "allocator_may_return_null=1:" - "msan_track_origins=0", 0); - - if (get_afl_env("AFL_PRELOAD")) { - - if (fsrv->qemu_mode) { - - u8 *qemu_preload = getenv("QEMU_SET_ENV"); - u8 *afl_preload = getenv("AFL_PRELOAD"); - u8 *buf; - - s32 i, afl_preload_size = strlen(afl_preload); - for (i = 0; i < afl_preload_size; ++i) { - - if (afl_preload[i] == ',') { - - PFATAL( - "Comma (',') is not allowed in AFL_PRELOAD when -Q is " - "specified!"); - - } - - } - - if (qemu_preload) { - - buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - qemu_preload, afl_preload, afl_preload); - - } else { - - buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", - afl_preload, afl_preload); - - } - - setenv("QEMU_SET_ENV", buf, 1); - - afl_free(buf); - - } else { - - setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); - setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); - - } - - } - -} - -/* Setup signal handlers, duh. */ - -static void setup_signal_handlers(void) { - - struct sigaction sa; - - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; - sa.sa_sigaction = NULL; - - sigemptyset(&sa.sa_mask); - - /* Various ways of saying "stop". */ - - sa.sa_handler = handle_stop_sig; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - -} - -/* Display usage hints. */ - -static void usage(u8 *argv0) { - - SAYF( - "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" - - "Required parameters:\n" - - " -i port - the port to listen for the client to connect to\n\n" - - "Execution control settings:\n" - - " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n" - " -U - use unicorn-based instrumentation (Unicorn mode)\n" - " -W - use qemu-based instrumentation with Wine (Wine " - "mode)\n\n" - - "Environment variables used:\n" - "TMPDIR: directory to use for temporary input files\n" - "ASAN_OPTIONS: custom settings for ASAN\n" - " (must contain abort_on_error=1 and symbolize=0)\n" - "MSAN_OPTIONS: custom settings for MSAN\n" - " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" - "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" - " the target was compiled for\n" - "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" - - , argv0, EXEC_TIMEOUT, MEM_LIMIT); - - exit(1); - -} - -int recv_testcase(int s, void **buf) { - - u32 size; - s32 ret; - size_t received; - - received = 0; - while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) FATAL("did not receive size information"); - if (size == 0) FATAL("did not receive valid size information"); - // fprintf(stderr, "received size information of %d\n", size); - - if ((size & 0xff000000) != 0xff000000) { - - *buf = afl_realloc(buf, size); - if (unlikely(!*buf)) { PFATAL("Alloc"); } - received = 0; - // fprintf(stderr, "unCOMPRESS (%u)\n", size); - while (received < size && - (ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0) - received += ret; - - } else { - -#ifdef USE_DEFLATE - u32 clen; - size -= 0xff000000; - *buf = afl_realloc(buf, size); - if (unlikely(!*buf)) { PFATAL("Alloc"); } - received = 0; - while (received < 4 && - (ret = recv(s, &clen + received, 4 - received, 0)) > 0) - received += ret; - if (received != 4) FATAL("did not receive clen1 information"); - // fprintf(stderr, "received clen information of %d\n", clen); - if (clen < 1) - FATAL("did not receive valid compressed len information: %u", clen); - buf2 = afl_realloc((void **)&buf2, clen); - buf2_len = clen; - if (unlikely(!buf2)) { PFATAL("Alloc"); } - received = 0; - while (received < clen && - (ret = recv(s, buf2 + received, clen - received, 0)) > 0) - received += ret; - if (received != clen) FATAL("did not receive compressed information"); - if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, - size, &received) != LIBDEFLATE_SUCCESS) - FATAL("decompression failed"); - // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); - // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); - // fprintf(stderr, "\n"); - // for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x", - // ((u8*)(*buf))[i]); fprintf(stderr, "\n"); -#else - FATAL("Received compressed data but not compiled with compression support"); -#endif - - } - - // fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len); - if (received != size) - FATAL("did not receive testcase data %lu != %u, %d", received, size, ret); - // fprintf(stderr, "received testcase\n"); - return size; - -} - -/* Main entry point */ - -int main(int argc, char **argv_orig, char **envp) { - - s32 opt, s, sock, on = 1, port = -1; - u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; - char **use_argv; - struct sockaddr_in6 serveraddr, clientaddr; - int addrlen = sizeof(clientaddr); - char str[INET6_ADDRSTRLEN]; - char ** argv = argv_cpy_dup(argc, argv_orig); - u8 * send_buf; -#ifdef USE_DEFLATE - u32 *lenptr; -#endif - - afl_forkserver_t fsrv_var = {0}; - afl_forkserver_t *fsrv = &fsrv_var; - afl_fsrv_init(fsrv); - map_size = get_map_size(); - fsrv->map_size = map_size; - - if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc"); - - while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) { - - switch (opt) { - - case 'i': - - if (port > 0) { FATAL("Multiple -i options not supported"); } - port = atoi(optarg); - if (port < 1 || port > 65535) - FATAL("invalid port definition, must be between 1-65535: %s", optarg); - break; - - case 'f': - - if (out_file) { FATAL("Multiple -f options not supported"); } - fsrv->use_stdin = 0; - out_file = optarg; - break; - - case 'm': { - - u8 suffix = 'M'; - - if (mem_limit_given) { FATAL("Multiple -m options not supported"); } - mem_limit_given = 1; - - if (!optarg) { FATAL("Wrong usage of -m"); } - - if (!strcmp(optarg, "none")) { - - fsrv->mem_limit = 0; - break; - - } - - if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 || - optarg[0] == '-') { - - FATAL("Bad syntax used for -m"); - - } - - switch (suffix) { - - case 'T': - fsrv->mem_limit *= 1024 * 1024; - break; - case 'G': - fsrv->mem_limit *= 1024; - break; - case 'k': - fsrv->mem_limit /= 1024; - break; - case 'M': - break; - - default: - FATAL("Unsupported suffix or bad syntax for -m"); - - } - - if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); } - - if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) { - - FATAL("Value of -m out of range on 32-bit systems"); - - } - - } - - break; - - case 't': - - if (timeout_given) { FATAL("Multiple -t options not supported"); } - timeout_given = 1; - - if (!optarg) { FATAL("Wrong usage of -t"); } - - fsrv->exec_tmout = atoi(optarg); - - if (fsrv->exec_tmout < 10 || optarg[0] == '-') { - - FATAL("Dangerously low value of -t"); - - } - - break; - - case 'Q': - - if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } - if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } - - fsrv->qemu_mode = 1; - break; - - case 'U': - - if (unicorn_mode) { FATAL("Multiple -Q options not supported"); } - if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } - - unicorn_mode = 1; - break; - - case 'W': /* Wine+QEMU mode */ - - if (use_wine) { FATAL("Multiple -W options not supported"); } - fsrv->qemu_mode = 1; - use_wine = 1; - - if (!mem_limit_given) { fsrv->mem_limit = 0; } - - break; - - case 'h': - usage(argv[0]); - return -1; - break; - - default: - usage(argv[0]); - - } - - } - - if (optind == argc || port < 1) { usage(argv[0]); } - - check_environment_vars(envp); - - sharedmem_t shm = {0}; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); - - in_data = afl_realloc((void **)&in_data, 65536); - if (unlikely(!in_data)) { PFATAL("Alloc"); } - - atexit(at_exit_handler); - setup_signal_handlers(); - - set_up_environment(fsrv); - - fsrv->target_path = find_binary(argv[optind]); - detect_file_args(argv + optind, out_file, &fsrv->use_stdin); - - if (fsrv->qemu_mode) { - - if (use_wine) { - - use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind, - argv + optind); - - } else { - - use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind, - argv + optind); - - } - - } else { - - use_argv = argv + optind; - - } - - if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed"); - -#ifdef SO_REUSEADDR - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { - - WARNF("setsockopt(SO_REUSEADDR) failed"); - - } - -#endif - -#ifdef SO_PRIORITY - int priority = 7; - if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) { - - priority = 6; - if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < - 0) - WARNF("could not set priority on socket"); - - } - -#endif - - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin6_family = AF_INET6; - serveraddr.sin6_port = htons(port); - serveraddr.sin6_addr = in6addr_any; - - if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) - PFATAL("bind() failed"); - - if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } - - afl_fsrv_start( - fsrv, use_argv, &stop_soon, - (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) - ? 1 - : 0); - -#ifdef USE_DEFLATE - compressor = libdeflate_alloc_compressor(1); - decompressor = libdeflate_alloc_decompressor(); - buf2 = afl_realloc((void **)&buf2, map_size + 16); - buf2_len = map_size + 16; - if (unlikely(!buf2)) { PFATAL("alloc"); } - lenptr = (u32 *)(buf2 + 4); - fprintf(stderr, "Compiled with compression support\n"); -#endif - - fprintf(stderr, - "Waiting for incoming connection from afl-network-client on port %d " - "...\n", - port); - - if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); } - fprintf(stderr, "Received connection, starting ...\n"); - -#ifdef SO_PRIORITY - priority = 7; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { - - priority = 6; - if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) - WARNF("could not set priority on socket"); - - } - -#endif - - while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { - - // fprintf(stderr, "received %u\n", in_len); - (void)run_target(fsrv, use_argv, in_data, in_len, 1); - - memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); - -#ifdef USE_DEFLATE - memcpy(buf2, &fsrv->child_status, 4); - *lenptr = (u32)libdeflate_deflate_compress( - compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8); - // fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr); - // for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x", - // fsrv->trace_bits[i]); fprintf(stderr, "\n"); - if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr) - FATAL("could not send data"); -#else - memcpy(send_buf, &fsrv->child_status, 4); - if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size) - FATAL("could not send data"); -#endif - - // fprintf(stderr, "sent result\n"); - - } - - unlink(out_file); - if (out_file) { ck_free(out_file); } - out_file = NULL; - - afl_shm_deinit(&shm); - afl_fsrv_deinit(fsrv); - if (fsrv->target_path) { ck_free(fsrv->target_path); } - afl_free(in_data); -#if USE_DEFLATE - afl_free(buf2); - libdeflate_free_compressor(compressor); - libdeflate_free_decompressor(decompressor); -#endif - - argv_cpy_free(argv); - - exit(0); - -} - diff --git a/examples/afl_proxy/Makefile b/examples/afl_proxy/Makefile deleted file mode 100644 index 4b368f8d..00000000 --- a/examples/afl_proxy/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: afl-proxy - -afl-proxy: afl-proxy.c - $(CC) -I../../include -o afl-proxy afl-proxy.c - -clean: - rm -f afl-proxy *~ core diff --git a/examples/afl_proxy/README.md b/examples/afl_proxy/README.md deleted file mode 100644 index 3c768a19..00000000 --- a/examples/afl_proxy/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# afl-proxy - -afl-proxy is an example skeleton file which can easily be used to fuzz -and instrument non-standard things. - -You only need to change the while() loop of the main() to send the -data of buf[] with length len to the target and write the coverage -information to __afl_area_ptr[__afl_map_size] - diff --git a/examples/afl_proxy/afl-proxy.c b/examples/afl_proxy/afl-proxy.c deleted file mode 100644 index f2dfeac1..00000000 --- a/examples/afl_proxy/afl-proxy.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - american fuzzy lop++ - afl-proxy skeleton example - --------------------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - - HOW-TO - ====== - - You only need to change the while() loop of the main() to send the - data of buf[] with length len to the target and write the coverage - information to __afl_area_ptr[__afl_map_size] - - -*/ - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -u8 *__afl_area_ptr; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -#else -__thread u32 __afl_map_size = MAP_SIZE; -#endif - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ - -static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - -} - -static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status, res = 0xffffff; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) return 0; - - /* we have a testcase - read it */ - status = read(0, buf, max_len); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; - - if (status < 1) - return 0; - else - return status; - -} - -static void __afl_end_testcase(void) { - - int status = 0xffffff; - - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); - -} - -/* you just need to modify the while() loop in this main() */ - -int main(int argc, char *argv[]) { - - /* This is were the testcase data is written into */ - u8 buf[1024]; // this is the maximum size for a test case! set it! - u32 len; - - /* here you specify the map size you need that you are reporting to - afl-fuzz. */ - __afl_map_size = MAP_SIZE; // default is 65536 - - /* then we initialize the shared memory map and start the forkserver */ - __afl_map_shm(); - __afl_start_forkserver(); - - while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { - - /* here you have to create the magic that feeds the buf/len to the - target and write the coverage to __afl_area_ptr */ - - // ... the magic ... - - /* report the test case is done and wait for the next */ - __afl_end_testcase(); - - } - - return 0; - -} - diff --git a/examples/afl_untracer/Makefile b/examples/afl_untracer/Makefile deleted file mode 100644 index 14a09b41..00000000 --- a/examples/afl_untracer/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -ifdef DEBUG - OPT=-O0 -else - OPT=-O3 -endif - -all: afl-untracer libtestinstr.so - -afl-untracer: afl-untracer.c - $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl - -libtestinstr.so: libtestinstr.c - $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c - -clean: - rm -f afl-untracer libtestinstr.so *~ core diff --git a/examples/afl_untracer/README.md b/examples/afl_untracer/README.md deleted file mode 100644 index ada0c916..00000000 --- a/examples/afl_untracer/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# afl-untracer - fast fuzzing of binary-only libraries - -## Introduction - -afl-untracer is an example skeleton file which can easily be used to fuzz -a closed source library. - -It requires less memory and is x3-5 faster than qemu_mode however it is way -more course grained and does not provide interesting features like compcov -or cmplog. - -Supported is so far Intel (i386/x86_64) and AARCH64. - -## How-to - -### Modify afl-untracer.c - -Read and modify afl-untracer.c then `make`. -To adapt afl-untracer.c to your needs, read the header of the file and then -search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. - -### Generate patches.txt file - -To generate the `patches.txt` file for your target library use the -`ida_get_patchpoints.py` script for IDA Pro or -`ghidra_get_patchpoints.java` for Ghidra. - -The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`. - -To easily run the scripts without needing to run the GUI with Ghidra: -``` -/opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java -rm -rf /tmp/tmp$$ -``` -The file is created at `~/Desktop/patches.txt` - -### Fuzzing - -Example (after modifying afl-untracer.c to your needs, compiling and creating -patches.txt): -``` -LD_LIBRARY_PATH=/path/to/target/library AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer -``` -(or even remote via afl-network-proxy). - -### Testing and debugging - -For testing/debugging you can try: -``` -make DEBUG=1 -AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer -``` -and then you can easily set breakpoints to "breakpoint" and "fuzz". - -# Background - -This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) -and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). -This implementation is slower because the traps are not patched out with each -run, but on the other hand gives much better coverage information. diff --git a/examples/afl_untracer/TODO b/examples/afl_untracer/TODO deleted file mode 100644 index fffffacf..00000000 --- a/examples/afl_untracer/TODO +++ /dev/null @@ -1,2 +0,0 @@ - * add shmem fuzzing - * add snapshot feature? diff --git a/examples/afl_untracer/afl-untracer.c b/examples/afl_untracer/afl-untracer.c deleted file mode 100644 index cb6f948c..00000000 --- a/examples/afl_untracer/afl-untracer.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - american fuzzy lop++ - afl-untracer skeleton example - --------------------------------------------------- - - Written by Marc Heuse - - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - - HOW-TO - ====== - - You only need to change the following: - - 1. decide if you want to receive data from stdin [DEFAULT] or file(name) - -> use_stdin = 0 if via file, and what the maximum input size is - 2. dl load the library you want to fuzz, lookup the functions you need - and setup the calls to these - 3. in the while loop you call the functions in the necessary order - - incl the cleanup. the cleanup is important! - - Just look these steps up in the code, look for "// STEP x:" - - -*/ - -#define __USE_GNU -#define _GNU_SOURCE - -#ifdef __ANDROID__ - #include "android-ashmem.h" -#endif -#include "config.h" -#include "types.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(__linux__) - #include -#elif defined(__APPLE__) && defined(__LP64__) - #include -#elif defined(__FreeBSD__) - #include - #include -#else - #error "Unsupported platform" -#endif - -#define MEMORY_MAP_DECREMENT 0x200000000000 -#define MAX_LIB_COUNT 128 - -// STEP 1: - -/* here you need to specify the parameter for the target function */ -static void *(*o_function)(u8 *buf, int len); - -/* use stdin (1) or a file on the commandline (0) */ -static u32 use_stdin = 1; - -/* This is were the testcase data is written into */ -static u8 buf[10000]; // this is the maximum size for a test case! set it! - -/* If you want to have debug output set this to 1, can also be set with - AFL_DEBUG */ -static u32 debug = 0; - -// END STEP 1 - -typedef struct library_list { - - u8 *name; - u64 addr_start, addr_end; - -} library_list_t; - -#ifdef __ANDROID__ -u32 __afl_map_size = MAP_SIZE; -u32 do_exit; -#else -__thread u32 __afl_map_size = MAP_SIZE; -__thread u32 do_exit; -#endif - -static pid_t pid = 65537; -static pthread_t __afl_thread; -static u8 __afl_dummy[MAP_SIZE]; -static u8 * __afl_area_ptr = __afl_dummy; -static u8 * inputfile; // this will point to argv[1] -static u32 len; - -static library_list_t liblist[MAX_LIB_COUNT]; -static u32 liblist_cnt; - -static void sigtrap_handler(int signum, siginfo_t *si, void *context); -static void fuzz(void); - -/* read the library information */ -void read_library_information(void) { - -#if defined(__linux__) - FILE *f; - u8 buf[1024], *b, *m, *e, *n; - - if ((f = fopen("/proc/self/maps", "r")) == NULL) - FATAL("cannot open /proc/self/maps"); - - if (debug) fprintf(stderr, "Library list:\n"); - while (fgets(buf, sizeof(buf), f)) { - - if (strstr(buf, " r-x")) { - - if (liblist_cnt >= MAX_LIB_COUNT) { - - WARNF("too many libraries to old, maximum count of %d reached", - liblist_cnt); - return; - - } - - b = buf; - m = index(buf, '-'); - e = index(buf, ' '); - if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); - if (n && - ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) - n = NULL; - else - n++; - if (b && m && e && n && *n) { - - *m++ = 0; - *e = 0; - if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; - - liblist[liblist_cnt].name = strdup(n); - liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); - liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); - if (debug) - fprintf( - stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - liblist_cnt++; - - } - - } - - } - - if (debug) fprintf(stderr, "\n"); - -#elif defined(__FreeBSD__) - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - char * buf, *start, *end; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (debug) fprintf(stderr, "Library list:\n"); - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } - - len = len * 4 / 3; - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) { return; } - - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - start = buf; - end = buf + len; - - while (start < end) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; - size_t size = region->kve_structsize; - - if (size == 0) { break; } - - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_EXEC)) { - - liblist[liblist_cnt].name = - region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; - liblist[liblist_cnt].addr_start = region->kve_start; - liblist[liblist_cnt].addr_end = region->kve_end; - - if (debug) { - - fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, - liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_start, - liblist[liblist_cnt].addr_end - 1); - - } - - liblist_cnt++; - - } - - start += size; - - } - -#endif - -} - -library_list_t *find_library(char *name) { - -#if defined(__linux__) - u32 i; - - for (i = 0; i < liblist_cnt; i++) - if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i]; -#elif defined(__APPLE__) && defined(__LP64__) - kern_return_t err; - static library_list_t lib; - - // get the list of all loaded modules from dyld - // the task_info mach API will get the address of the dyld all_image_info - // struct for the given task from which we can get the names and load - // addresses of all modules - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - err = task_info(mach_task_self(), TASK_DYLD_INFO, - (task_info_t)&task_dyld_info, &count); - - const struct dyld_all_image_infos *all_image_infos = - (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; - const struct dyld_image_info *image_infos = all_image_infos->infoArray; - - for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { - - const char * image_name = image_infos[i].imageFilePath; - mach_vm_address_t image_load_address = - (mach_vm_address_t)image_infos[i].imageLoadAddress; - if (strstr(image_name, name)) { - - lib.name = name; - lib.addr_start = (u64)image_load_address; - lib.addr_end = 0; - return &lib; - - } - - } - -#endif - - return NULL; - -} - -/* for having an easy breakpoint location after loading the shared library */ -// this seems to work for clang too. nice :) requires gcc 4.4+ -#pragma GCC push_options -#pragma GCC optimize("O0") -void breakpoint(void) { - - if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); - -} - -#pragma GCC pop_options - -/* Error reporting to forkserver controller */ - -void send_forkserver_error(int error) { - - u32 status; - if (!error || error > 0xffff) return; - status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); - if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; - -} - -/* SHM setup. */ - -static void __afl_map_shm(void) { - - char *id_str = getenv(SHM_ENV_VAR); - char *ptr; - - if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { - - u32 val = atoi(ptr); - if (val > 0) __afl_map_size = val; - - } - - if (__afl_map_size > MAP_SIZE) { - - if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { - - fprintf(stderr, - "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - if (id_str) { - - send_forkserver_error(FS_ERROR_MAP_SIZE); - exit(-1); - - } - - } else { - - fprintf(stderr, - "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " - "be able to run this instrumented program!\n", - __afl_map_size); - - } - - } - - if (id_str) { - -#ifdef USEMMAP - const char * shm_file_path = id_str; - int shm_fd = -1; - unsigned char *shm_base = NULL; - - /* create the shared memory segment as if it was a file */ - shm_fd = shm_open(shm_file_path, O_RDWR, 0600); - if (shm_fd == -1) { - - fprintf(stderr, "shm_open() failed\n"); - send_forkserver_error(FS_ERROR_SHM_OPEN); - exit(1); - - } - - /* map the shared memory segment to the address space of the process */ - shm_base = - mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); - - if (shm_base == MAP_FAILED) { - - close(shm_fd); - shm_fd = -1; - - fprintf(stderr, "mmap() failed\n"); - send_forkserver_error(FS_ERROR_MMAP); - exit(2); - - } - - __afl_area_ptr = shm_base; -#else - u32 shm_id = atoi(id_str); - - __afl_area_ptr = shmat(shm_id, 0, 0); - -#endif - - if (__afl_area_ptr == (void *)-1) { - - send_forkserver_error(FS_ERROR_SHMAT); - exit(1); - - } - - /* Write something into the bitmap so that the parent doesn't give up */ - - __afl_area_ptr[0] = 1; - - } - -} - -/* Fork server logic. */ -inline static void __afl_start_forkserver(void) { - - u8 tmp[4] = {0, 0, 0, 0}; - u32 status = 0; - - if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) - status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - if (status) status |= (FS_OPT_ENABLED); - memcpy(tmp, &status, 4); - - /* Phone home and tell the parent that we're OK. */ - if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1; - // fprintf(stderr, "write0 %d\n", do_exit); - -} - -inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) { - - s32 status; - - /* Wait for parent by reading from the pipe. Abort if read fails. */ - if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1; - // fprintf(stderr, "read %d\n", do_exit); - - /* we have a testcase - read it if we read from stdin */ - if (use_stdin) { - - if ((status = read(0, buf, max_len)) <= 0) exit(-1); - - } else - - status = 1; - // fprintf(stderr, "stdin: %d %d\n", use_stdin, status); - - /* report that we are starting the target */ - if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1; - // fprintf(stderr, "write1 %d\n", do_exit); - - __afl_area_ptr[0] = 1; // put something in the map - - return status; - -} - -inline static void __afl_end_testcase(int status) { - - if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1; - // fprintf(stderr, "write2 %d\n", do_exit); - if (do_exit) exit(0); - -} - -#ifdef __aarch64__ - #define SHADOW(addr) \ - ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ - MEMORY_MAP_DECREMENT - \ - ((uintptr_t)addr & 0x7) * 0x10000000000)) -#else - #define SHADOW(addr) \ - ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ - MEMORY_MAP_DECREMENT - \ - ((uintptr_t)addr & 0x3) * 0x10000000000)) -#endif - -void setup_trap_instrumentation(void) { - - library_list_t *lib_base = NULL; - size_t lib_size = 0; - u8 * lib_addr; - char * line = NULL; - size_t nread, len = 0; - char * filename = getenv("AFL_UNTRACER_FILE"); - if (!filename) filename = getenv("TRAPFUZZ_FILE"); - if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set"); - - FILE *patches = fopen(filename, "r"); - if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); - - // Index into the coverage bitmap for the current trap instruction. -#ifdef __aarch64__ - uint64_t bitmap_index = 0; -#else - uint32_t bitmap_index = 0; -#endif - - while ((nread = getline(&line, &len, patches)) != -1) { - - char *end = line + len; - - char *col = strchr(line, ':'); - if (col) { - - // It's a library:size pair - *col++ = 0; - - lib_base = find_library(line); - if (!lib_base) FATAL("Library %s does not appear to be loaded", line); - - // we ignore the defined lib_size - lib_size = strtoul(col, NULL, 16); -#if (__linux__) - if (lib_size < lib_base->addr_end - lib_base->addr_start) - lib_size = lib_base->addr_end - lib_base->addr_start; -#endif - if (lib_size % 0x1000 != 0) - WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000", - lib_size); - - lib_addr = (u8 *)lib_base->addr_start; - - // Make library code writable. - if (mprotect((void *)lib_addr, lib_size, - PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - FATAL("Failed to mprotect library %s writable", line); - - // Create shadow memory. -#ifdef __aarch64__ - for (int i = 0; i < 8; i++) { - -#else - for (int i = 0; i < 4; i++) { - -#endif - - void *shadow_addr = SHADOW(lib_addr + i); - void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); - if (debug) - fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow, - shadow + lib_size - 1, lib_addr); - if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory"); - - } - - // Done, continue with next line. - continue; - - } - - // It's an offset, parse it and do the patching. - unsigned long offset = strtoul(line, NULL, 16); - - if (offset > lib_size) - FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large", - offset, lib_size); - - if (bitmap_index >= __afl_map_size) - FATAL("Too many basic blocks to instrument"); - -#ifdef __arch64__ - uint64_t -#else - uint32_t -#endif - *shadow = SHADOW(lib_addr + offset); - if (*shadow != 0) continue; // skip duplicates - - // Make lookup entry in shadow memory. - -#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ - defined(__i386__)) - - // this is for Intel x64 - - uint8_t orig_byte = lib_addr[offset]; - *shadow = (bitmap_index << 8) | orig_byte; - lib_addr[offset] = 0xcc; // replace instruction with debug trap - if (debug) - fprintf(stderr, - "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n", - lib_addr, offset, lib_addr + offset, orig_byte, shadow, - bitmap_index, *shadow); - -#elif defined(__aarch64__) - - // this is for aarch64 - - uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); - uint32_t orig_bytes = *patch_bytes; - *shadow = (bitmap_index << 32) | orig_bytes; - *patch_bytes = 0xd4200000; // replace instruction with debug trap - if (debug) - fprintf(stderr, - "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", - lib_addr, offset, lib_addr + offset, orig_bytes, shadow, - bitmap_index, *shadow); - -#else - // this will be ARM and AARCH64 - // for ARM we will need to identify if the code is in thumb or ARM - #error "non x86_64/aarch64 not supported yet" - //__arm__: - // linux thumb: 0xde01 - // linux arm: 0xe7f001f0 - //__aarch64__: - // linux aarch64: 0xd4200000 -#endif - - bitmap_index++; - - } - - free(line); - fclose(patches); - - // Install signal handler for SIGTRAP. - struct sigaction s; - s.sa_flags = SA_SIGINFO; - s.sa_sigaction = sigtrap_handler; - sigemptyset(&s.sa_mask); - sigaction(SIGTRAP, &s, 0); - - if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index); - __afl_map_size = bitmap_index; - if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3); - -} - -/* the signal handler for the traps / debugging interrupts - No debug output here because this would cost speed */ -static void sigtrap_handler(int signum, siginfo_t *si, void *context) { - - uint64_t addr; - // Must re-execute the instruction, so decrement PC by one instruction. - ucontext_t *ctx = (ucontext_t *)context; -#if defined(__APPLE__) && defined(__LP64__) - ctx->uc_mcontext->__ss.__rip -= 1; - addr = ctx->uc_mcontext->__ss.__rip; -#elif defined(__linux__) - #if defined(__x86_64__) || defined(__i386__) - ctx->uc_mcontext.gregs[REG_RIP] -= 1; - addr = ctx->uc_mcontext.gregs[REG_RIP]; - #elif defined(__aarch64__) - ctx->uc_mcontext.pc -= 4; - addr = ctx->uc_mcontext.pc; - #else - #error "Unsupported processor" - #endif -#elif defined(__FreeBSD__) && defined(__LP64__) - ctx->uc_mcontext.mc_rip -= 1; - addr = ctx->uc_mcontext.mc_rip; -#else - #error "Unsupported platform" -#endif - - // fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, - // si->si_addr); - - // If the trap didn't come from our instrumentation, then we probably will - // just segfault here - uint8_t *faultaddr; - if (unlikely(si->si_addr)) - faultaddr = (u8 *)si->si_addr - 1; - else - faultaddr = (u8 *)addr; - // if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr)); - uint32_t shadow = *SHADOW(faultaddr); - uint8_t orig_byte = shadow & 0xff; - uint32_t index = shadow >> 8; - - // if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", - // shadow, orig_byte, index); - - // Index zero is invalid so that it is still possible to catch actual trap - // instructions in instrumented libraries. - if (unlikely(index == 0)) abort(); - - // Restore original instruction - *faultaddr = orig_byte; - - __afl_area_ptr[index] = 128; - -} - -/* the MAIN function */ -int main(int argc, char *argv[]) { - - (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR - - pid = getpid(); - if (getenv("AFL_DEBUG")) debug = 1; - - /* by default we use stdin, but also a filename can be passed, in this - case the input is argv[1] and we have to disable stdin */ - if (argc > 1) { - - use_stdin = 0; - inputfile = argv[1]; - - } - - // STEP 2: load the library you want to fuzz and lookup the functions, - // inclusive of the cleanup functions - // NOTE: above the main() you have to define the functions! - - void *dl = dlopen("./libtestinstr.so", RTLD_LAZY); - if (!dl) FATAL("could not find target library"); - o_function = dlsym(dl, "testinstr"); - if (!o_function) FATAL("could not resolve target function from library"); - if (debug) fprintf(stderr, "Function address: %p\n", o_function); - - // END STEP 2 - - /* setup instrumentation, shared memory and forkserver */ - breakpoint(); - read_library_information(); - setup_trap_instrumentation(); - __afl_map_shm(); - __afl_start_forkserver(); - - while (1) { - - // instead of fork() we could also use the snapshot lkm or do our own mini - // snapshot feature like in https://github.com/marcinguy/fuzzer - // -> snapshot.c - if ((pid = fork()) == -1) PFATAL("fork failed"); - - if (pid) { - - u32 status; - if (waitpid(pid, &status, 0) < 0) exit(1); - /* report the test case is done and wait for the next */ - __afl_end_testcase(status); - - } else { - - pid = getpid(); - while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { - - // in this function the fuzz magic happens, this is STEP 3 - fuzz(); - - // we can use _exit which is faster because our target library - // was loaded via dlopen and therefore cannot have deconstructors - // registered. - _exit(0); - - } - - } - - } - - return 0; - -} - -#ifndef _DEBUG -inline -#endif - static void - fuzz(void) { - - // STEP 3: call the function to fuzz, also the functions you might - // need to call to prepare the function and - important! - - // to clean everything up - - // in this example we use the input file, not stdin! - (*o_function)(buf, len); - - // normally you also need to cleanup - //(*o_LibFree)(foo); - - // END STEP 3 - -} - diff --git a/examples/afl_untracer/ghidra_get_patchpoints.java b/examples/afl_untracer/ghidra_get_patchpoints.java deleted file mode 100644 index d341bea4..00000000 --- a/examples/afl_untracer/ghidra_get_patchpoints.java +++ /dev/null @@ -1,84 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// Find patch points for untracer tools (e.g. afl++ examples/afl_untracer) -// -// Copy to ..../Ghidra/Features/Search/ghidra_scripts/ -// Writes the results to ~/Desktop/patches.txt -// -// This is my very first Ghidra script. I am sure this could be done better. -// -//@category Search - -import ghidra.app.script.GhidraScript; -import ghidra.program.model.address.*; -import ghidra.program.model.block.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.symbol.*; -import ghidra.program.model.mem.*; - -import java.io.*; - -public class ghidra_get_patchpoints extends GhidraScript { - - @Override - public void run() throws Exception { - - long segment_start = 0; - Memory memory = currentProgram.getMemory(); - MultEntSubModel model = new MultEntSubModel(currentProgram); - CodeBlockIterator subIter = model.getCodeBlocks(monitor); - BufferedWriter out = new BufferedWriter(new FileWriter(System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "patches.txt")); - - while (subIter.hasNext()) { - - CodeBlock multiEntryBlock = subIter.next(); - SimpleBlockModel basicBlockModel = new SimpleBlockModel(currentProgram); - CodeBlockIterator bbIter = basicBlockModel.getCodeBlocksContaining(multiEntryBlock, monitor); - - while (bbIter.hasNext()) { - - CodeBlock basicBlock = bbIter.next(); - - if (segment_start == 0) { - - Address firstAddr = basicBlock.getFirstStartAddress(); - long firstBlockAddr = firstAddr.getAddressableWordOffset(); - MemoryBlock mb = memory.getBlock(firstAddr); - Address startAddr = mb.getStart(); - Address endAddr = mb.getEnd(); - segment_start = startAddr.getAddressableWordOffset(); - if ((firstBlockAddr - segment_start) >= 0x1000) - segment_start += 0x1000; - long segment_end = endAddr.getAddressableWordOffset(); - long segment_size = segment_end - segment_start; - if ((segment_size % 0x1000) > 0) - segment_size = (((segment_size / 0x1000) + 1) * 0x1000); - out.write(currentProgram.getName() + ":0x" + Long.toHexString(segment_size) + "\n"); - //println("Start: " + Long.toHexString(segment_start)); - //println("End: " + Long.toHexString(segment_end)); - - } - - if (basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start > 0) - out.write("0x" + Long.toHexString(basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start) + "\n"); - - } - } - - out.close(); - - } -} diff --git a/examples/afl_untracer/ida_get_patchpoints.py b/examples/afl_untracer/ida_get_patchpoints.py deleted file mode 100644 index 43cf6d89..00000000 --- a/examples/afl_untracer/ida_get_patchpoints.py +++ /dev/null @@ -1,62 +0,0 @@ -# -# IDAPython script for IDA Pro -# Slightly modified from https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/findPatchPoints.py -# - -import idautils -import idaapi -import ida_nalt -import idc - -# See https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml - -from os.path import expanduser -home = expanduser("~") - -patchpoints = set() - -max_offset = 0 -for seg_ea in idautils.Segments(): - name = idc.get_segm_name(seg_ea) - #print("Segment: " + name) - if name != "__text" and name != ".text": - continue - - start = idc.get_segm_start(seg_ea) - end = idc.get_segm_end(seg_ea) - first = 0 - subtract_addr = 0 - #print("Start: " + hex(start) + " End: " + hex(end)) - for func_ea in idautils.Functions(start, end): - f = idaapi.get_func(func_ea) - if not f: - continue - for block in idaapi.FlowChart(f): - if start <= block.start_ea < end: - if first == 0: - if block.start_ea >= 0x1000: - subtract_addr = 0x1000 - first = 1 - - max_offset = max(max_offset, block.start_ea) - patchpoints.add(block.start_ea - subtract_addr) - #else: - # print("Warning: broken CFG?") - -# Round up max_offset to page size -size = max_offset -rem = size % 0x1000 -if rem != 0: - size += 0x1000 - rem - -print("Writing to " + home + "/Desktop/patches.txt") - -with open(home + "/Desktop/patches.txt", "w") as f: - f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\n') - f.write('\n'.join(map(hex, sorted(patchpoints)))) - f.write('\n') - -print("Done, found {} patchpoints".format(len(patchpoints))) - -# For headless script running remove the comment from the next line -#ida_pro.qexit() diff --git a/examples/afl_untracer/libtestinstr.c b/examples/afl_untracer/libtestinstr.c deleted file mode 100644 index 96b1cf21..00000000 --- a/examples/afl_untracer/libtestinstr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -void testinstr(char *buf, int len) { - - if (len < 1) return; - buf[len] = 0; - - // we support three input cases - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - -} - diff --git a/examples/afl_untracer/libtestinstr.so b/examples/afl_untracer/libtestinstr.so deleted file mode 100755 index 389a946c..00000000 Binary files a/examples/afl_untracer/libtestinstr.so and /dev/null differ diff --git a/examples/afl_untracer/patches.txt b/examples/afl_untracer/patches.txt deleted file mode 100644 index 7e964249..00000000 --- a/examples/afl_untracer/patches.txt +++ /dev/null @@ -1,34 +0,0 @@ -libtestinstr.so:0x1000 -0x10 -0x12 -0x20 -0x36 -0x30 -0x40 -0x50 -0x63 -0x6f -0x78 -0x80 -0xa4 -0xb0 -0xb8 -0x100 -0xc0 -0xc9 -0xd7 -0xe3 -0xe8 -0xf8 -0x105 -0x11a -0x135 -0x141 -0x143 -0x14e -0x15a -0x15c -0x168 -0x16a -0x16b -0x170 diff --git a/examples/aflpp_driver/GNUmakefile b/examples/aflpp_driver/GNUmakefile deleted file mode 100644 index c1a087d7..00000000 --- a/examples/aflpp_driver/GNUmakefile +++ /dev/null @@ -1,46 +0,0 @@ -ifeq "" "$(LLVM_CONFIG)" - LLVM_CONFIG=llvm-config -endif - -LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) -ifneq "" "$(LLVM_BINDIR)" - LLVM_BINDIR := $(LLVM_BINDIR)/ -endif - -CFLAGS := -O3 -funroll-loops -g - -all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so - -aflpp_driver.o: aflpp_driver.c - -$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c - -libAFLDriver.a: aflpp_driver.o - ar ru libAFLDriver.a aflpp_driver.o - cp -vf libAFLDriver.a ../../ - -debug: - $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c - $(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c - #$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c - #$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c - ar ru libAFLDriver.a afl-performance.o aflpp_driver.o - -aflpp_qemu_driver.o: aflpp_qemu_driver.c - $(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c - -libAFLQemuDriver.a: aflpp_qemu_driver.o - ar ru libAFLQemuDriver.a aflpp_qemu_driver.o - cp -vf libAFLQemuDriver.a ../../ - -aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o - $(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so - -aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c - $(LLVM_BINDIR)clang -fPIC $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c - -test: debug - #clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c - afl-clang-fast -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test aflpp_driver_test.c libAFLDriver.a afl-performance.o - -clean: - rm -f *.o libAFLDriver*.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so *~ core aflpp_driver_test diff --git a/examples/aflpp_driver/Makefile b/examples/aflpp_driver/Makefile deleted file mode 100644 index 3666a74d..00000000 --- a/examples/aflpp_driver/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -all: - @gmake all || echo please install GNUmake diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c deleted file mode 100644 index 017aa72b..00000000 --- a/examples/aflpp_driver/aflpp_driver.c +++ /dev/null @@ -1,326 +0,0 @@ -//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -//===----------------------------------------------------------------------===// - -/* This file allows to fuzz libFuzzer-style target functions - (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. - -Usage: -################################################################################ -cat << EOF > test_fuzzer.cc -#include -#include -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - - if (size > 0 && data[0] == 'H') - if (size > 1 && data[1] == 'I') - if (size > 2 && data[2] == '!') - __builtin_trap(); - return 0; - -} - -EOF -# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. -clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c -# Build afl-llvm-rt.o.c from the AFL distribution. -clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c -# Build this file, link it with afl-llvm-rt.o.o and the target code. -clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o -# Run AFL: -rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; -$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out -################################################################################ -AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file -specified. If the file does not exist, it is created. This is useful for getting -stack traces (when using ASAN for example) or original error messages on hard -to reproduce bugs. Note that any content written to stderr will be written to -this file instead of stderr's usual location. - -AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. -If 1, close stdout at startup. If 2 close stderr; if 3 close both. - -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "cmplog.h" - -#ifdef _DEBUG - #include "hash.h" -#endif - -#ifndef MAP_FIXED_NOREPLACE - #define MAP_FIXED_NOREPLACE 0x100000 -#endif - -#define MAX_DUMMY_SIZE 256000 - -// Platform detection. Copied from FuzzerInternal.h -#ifdef __linux__ - #define LIBFUZZER_LINUX 1 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __APPLE__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 1 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __NetBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 1 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 0 -#elif __FreeBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 1 - #define LIBFUZZER_OPENBSD 0 -#elif __OpenBSD__ - #define LIBFUZZER_LINUX 0 - #define LIBFUZZER_APPLE 0 - #define LIBFUZZER_NETBSD 0 - #define LIBFUZZER_FREEBSD 0 - #define LIBFUZZER_OPENBSD 1 -#else - #error "Support for your platform has not been implemented" -#endif - -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; - -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - -// Notify AFL about persistent mode. -static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; -int __afl_persistent_loop(unsigned int); - -// Notify AFL about deferred forkserver. -static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -void __afl_manual_init(); - -// Use this optionally defined function to output sanitizer messages even if -// user asks to close stderr. -__attribute__((weak)) void __sanitizer_set_report_fd(void *); - -// Keep track of where stderr content is being written to, so that -// dup_and_close_stderr can use the correct one. -static FILE *output_file; - -// Experimental feature to use afl_driver without AFL's deferred mode. -// Needs to run before __afl_auto_init. -__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { - - if (getenv("AFL_DRIVER_DONT_DEFER")) { - - if (unsetenv("__AFL_DEFER_FORKSRV")) { - - perror("Failed to unset __AFL_DEFER_FORKSRV"); - abort(); - - } - - } - -} - -// If the user asks us to duplicate stderr, then do it. -static void maybe_duplicate_stderr() { - - char *stderr_duplicate_filename = - getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - - if (!stderr_duplicate_filename) return; - - FILE *stderr_duplicate_stream = - freopen(stderr_duplicate_filename, "a+", stderr); - - if (!stderr_duplicate_stream) { - - fprintf( - stderr, - "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); - abort(); - - } - - output_file = stderr_duplicate_stream; - -} - -// Most of these I/O functions were inspired by/copied from libFuzzer's code. -static void discard_output(int fd) { - - FILE *temp = fopen("/dev/null", "w"); - if (!temp) abort(); - dup2(fileno(temp), fd); - fclose(temp); - -} - -static void close_stdout() { - - discard_output(STDOUT_FILENO); - -} - -// Prevent the targeted code from writing to "stderr" but allow sanitizers and -// this driver to do so. -static void dup_and_close_stderr() { - - int output_fileno = fileno(output_file); - int output_fd = dup(output_fileno); - if (output_fd <= 0) abort(); - FILE *new_output_file = fdopen(output_fd, "w"); - if (!new_output_file) abort(); - if (!__sanitizer_set_report_fd) return; - __sanitizer_set_report_fd((void *)(long int)output_fd); - discard_output(output_fileno); - -} - -// Close stdout and/or stderr if user asks for it. -static void maybe_close_fd_mask() { - - char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); - if (!fd_mask_str) return; - int fd_mask = atoi(fd_mask_str); - if (fd_mask & 2) dup_and_close_stderr(); - if (fd_mask & 1) close_stdout(); - -} - -// Define LLVMFuzzerMutate to avoid link failures for targets that use it -// with libFuzzer's LLVMFuzzerCustomMutator. -size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { - - // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); - return 0; - -} - -// Execute any files provided as parameters. -static int ExecuteFilesOnyByOne(int argc, char **argv) { - - unsigned char *buf = malloc(MAX_FILE); - for (int i = 1; i < argc; i++) { - - int fd = open(argv[i], O_RDONLY); - if (fd == -1) continue; - ssize_t length = read(fd, buf, MAX_FILE); - if (length > 0) { - - printf("Reading %zu bytes from %s\n", length, argv[i]); - LLVMFuzzerTestOneInput(buf, length); - printf("Execution successful.\n"); - - } - - } - - free(buf); - return 0; - -} - -int main(int argc, char **argv) { - - printf( - "======================= INFO =========================\n" - "This binary is built for afl++.\n" - "To run the target function on individual input(s) execute this:\n" - " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" - "To fuzz with afl-fuzz execute this:\n" - " afl-fuzz [afl-flags] -- %s [-N]\n" - "afl-fuzz will run N iterations before re-spawning the process (default: " - "1000)\n" - "======================================================\n", - argv[0], argv[0]); - - output_file = stderr; - maybe_duplicate_stderr(); - maybe_close_fd_mask(); - if (LLVMFuzzerInitialize) { - - fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); - LLVMFuzzerInitialize(&argc, &argv); - fprintf(stderr, "continue...\n"); - - } - - // Do any other expensive one-time initialization here. - - uint8_t dummy_input[64] = {0}; - memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); - memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, - sizeof(AFL_DEFER_FORKSVR)); - int N = INT_MAX; - if (argc == 2 && argv[1][0] == '-') - N = atoi(argv[1] + 1); - else if (argc == 2 && (N = atoi(argv[1])) > 0) - printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); - else if (argc > 1) { - - __afl_sharedmem_fuzzing = 0; - __afl_manual_init(); - return ExecuteFilesOnyByOne(argc, argv); - - } - - assert(N > 0); - - // if (!getenv("AFL_DRIVER_DONT_DEFER")) - __afl_manual_init(); - - // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization - // on the first execution of LLVMFuzzerTestOneInput is ignored. - LLVMFuzzerTestOneInput(dummy_input, 1); - - int num_runs = 0; - while (__afl_persistent_loop(N)) { - -#ifdef _DEBUG - fprintf(stderr, "CLIENT crc: %016llx len: %u\n", - hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), - *__afl_fuzz_len); - fprintf(stderr, "RECV:"); - for (int i = 0; i < *__afl_fuzz_len; i++) - fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); - fprintf(stderr, "\n"); -#endif - if (*__afl_fuzz_len) { - - num_runs++; - LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); - - } - - } - - printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); - -} - diff --git a/examples/aflpp_driver/aflpp_driver_test.c b/examples/aflpp_driver/aflpp_driver_test.c deleted file mode 100644 index b4ff6bc6..00000000 --- a/examples/aflpp_driver/aflpp_driver_test.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -#include "hash.h" - -void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { - - if (Size < 5) return; - - if (Data[0] == 'F') - if (Data[1] == 'A') - if (Data[2] == '$') - if (Data[3] == '$') - if (Data[4] == '$') abort(); - -} - -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { - - if (Size) - fprintf(stderr, "FUNC crc: %016llx len: %lu\n", - hash64((u8 *)Data, (unsigned int)Size, - (unsigned long long int)0xa5b35705), - Size); - - crashme(Data, Size); - - return 0; - -} - diff --git a/examples/aflpp_driver/aflpp_qemu_driver.c b/examples/aflpp_driver/aflpp_qemu_driver.c deleted file mode 100644 index 4f3e5f71..00000000 --- a/examples/aflpp_driver/aflpp_qemu_driver.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include - -// libFuzzer interface is thin, so we don't include any libFuzzer headers. -int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); -__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); - -static const size_t kMaxAflInputSize = 1 * 1024 * 1024; -static uint8_t AflInputBuf[kMaxAflInputSize]; - -void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { - - size_t l = read(0, AflInputBuf, kMaxAflInputSize); - LLVMFuzzerTestOneInput(AflInputBuf, l); - -} - -int main(int argc, char **argv) { - - if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); - // Do any other expensive one-time initialization here. - - if (getenv("AFL_QEMU_DRIVER_NO_HOOK")) { - - afl_qemu_driver_stdin_input(); - - } else { - - uint8_t dummy_input[1024000] = {0}; - LLVMFuzzerTestOneInput(dummy_input, 1); - - } - - return 0; - -} - diff --git a/examples/aflpp_driver/aflpp_qemu_driver_hook.c b/examples/aflpp_driver/aflpp_qemu_driver_hook.c deleted file mode 100644 index 823cc42d..00000000 --- a/examples/aflpp_driver/aflpp_qemu_driver_hook.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) - -#define REGS_RDI 7 -#define REGS_RSI 6 - -void afl_persistent_hook(uint64_t *regs, uint64_t guest_base, - uint8_t *input_buf, uint32_t input_len) { - - memcpy(g2h(regs[REGS_RDI]), input_buf, input_len); - regs[REGS_RSI] = input_len; - -} - -int afl_persistent_hook_init(void) { - - return 1; - -} - diff --git a/examples/analysis_scripts/queue2csv.sh b/examples/analysis_scripts/queue2csv.sh deleted file mode 100755 index 2528b438..00000000 --- a/examples/analysis_scripts/queue2csv.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash - -test -z "$1" -o -z "$2" -o "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" -o '!' -d "$1" && { - echo "Syntax: [-n] $0 out-directory file.csv [\"tools/target --opt @@\"]" - echo Option -n will suppress the CSV header. - echo If the target execution command is supplied then also edge coverage is gathered. - exit 1 -} - -function getval() { - VAL="" - if [ "$file" != "${file/$1/}" ]; then - TMP="${file/*$1:/}" - VAL="${TMP/,*/}" - fi -} - -SKIP= -if [ "$1" = "-n" ]; then - SKIP=1 - shift -fi - -test -n "$4" && { echo "Error: too many commandline options. Target command and options including @@ have to be passed within \"\"!"; exit 1; } - -test -d "$1"/queue && OUT="$1/queue" || OUT="$1" - -OK=`ls $OUT/id:000000,time:0,orig:* 2> /dev/null` -if [ -n "$OK" ]; then - LISTCMD="ls $OUT/id:"* -else - LISTCMD="ls -tr $OUT/" -fi - -ID=;SRC=;TIME=;OP=;POS=;REP=;EDGES=;EDGES_TOTAL=; -DIR="$OUT/../stats" -rm -rf "$DIR" -> "$2" || exit 1 -mkdir "$DIR" || exit 1 -> "$DIR/../edges.txt" || exit 1 - -{ - - if [ -z "$SKIP" ]; then - echo "time;\"filename\";id;src;new_cov;edges;total_edges;\"op\";pos;rep;unique_edges" - fi - - $LISTCMD | grep -v ,sync: | sed 's/.*id:/id:/g' | while read file; do - - if [ -n "$3" ]; then - - TMP=${3/@@/$OUT/$file} - - if [ "$TMP" = "$3" ]; then - - cat "$OUT/$file" | afl-showmap -o "$DIR/$file" -q -- $3 >/dev/null 2>&1 - - else - - afl-showmap -o "$DIR/$file" -q -- $TMP >/dev/null 2>&1 - - fi - - { cat "$DIR/$file" | sed 's/:.*//' ; cat "$DIR/../edges.txt" ; } | sort -nu > $DIR/../edges.txt.tmp - mv $DIR/../edges.txt.tmp $DIR/../edges.txt - EDGES=$(cat "$DIR/$file" | wc -l) - EDGES_TOTAL=$(cat "$DIR/../edges.txt" | wc -l) - - fi - - getval id; ID="$VAL" - getval src; SRC="$VAL" - getval time; TIME="$VAL" - getval op; OP="$VAL" - getval pos; POS="$VAL" - getval rep; REP="$VAL" - if [ "$file" != "${file/+cov/}" ]; then - COV=1 - else - COV="" - fi - - if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then - echo "$TIME;\"$file\";$ID;$SRC;$COV;$EDGES;$EDGES_TOTAL;\"$OP\";$POS;$REP;UNIQUE$file" - else - echo "$TIME;\"$file\";$ID;$SRC;$COV;;;\"$OP\";$POS;$REP;" - fi - - done - -} | tee "$DIR/../queue.csv" > "$2" || exit 1 - -if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then - - cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt - - if [ -s "$DIR/../unique.txt" ]; then - - ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do - - CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l) - DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l) - UNIQUE=$(($CNT - $DIFF)) - sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2" - - done - - rm -f "$DIR/../tmp.txt" - - else - - sed -i 's/;UNIQUE.*/;/' "$DIR/../queue.csv" "$2" - - fi - -fi - -mv "$DIR/../queue.csv" "$DIR/queue.csv" -if [ -e "$DIR/../edges.txt" ]; then mv "$DIR/../edges.txt" "$DIR/edges.txt"; fi -if [ -e "$DIR/../unique.txt" ]; then mv "$DIR/../unique.txt" "$DIR/unique.txt"; fi - -echo "Created $2" diff --git a/examples/argv_fuzzing/Makefile b/examples/argv_fuzzing/Makefile deleted file mode 100644 index 5a0ac6e6..00000000 --- a/examples/argv_fuzzing/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -# -# american fuzzy lop++ - argvfuzz -# -------------------------------- -# -# Copyright 2019-2020 Kjell Braden -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) - -all: argvfuzz32.so argvfuzz64.so - -argvfuzz32.so: argvfuzz.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)" - -argvfuzz64.so: argvfuzz.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz64 build failure (that's fine)" - -install: argvfuzz32.so argvfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi - -clean: - rm -f argvfuzz32.so argvfuzz64.so diff --git a/examples/argv_fuzzing/README.md b/examples/argv_fuzzing/README.md deleted file mode 100644 index fa8cad80..00000000 --- a/examples/argv_fuzzing/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# argvfuzz - -afl supports fuzzing file inputs or stdin. When source is available, -`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. - -`argvfuzz` tries to provide the same functionality for binaries. When loaded -using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace -argv using the same logic of `argv-fuzz-inl.h`. - -A few conditions need to be fulfilled for this mechanism to work correctly: - -1. As it relies on hooking the loader, it cannot work on static binaries. -2. If the target binary does not use the default libc's `_start` implementation - (crt1.o), the hook may not run. -3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the - target binary expects argv to be living on the stack, things may go wrong. diff --git a/examples/argv_fuzzing/argv-fuzz-inl.h b/examples/argv_fuzzing/argv-fuzz-inl.h deleted file mode 100644 index c15c0271..00000000 --- a/examples/argv_fuzzing/argv-fuzz-inl.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - american fuzzy lop++ - sample argv fuzzing wrapper - ------------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file shows a simple way to fuzz command-line parameters with stock - afl-fuzz. To use, add: - - #include "/path/to/argv-fuzz-inl.h" - - ...to the file containing main(), ideally placing it after all the - standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of - main(). - - This will cause the program to read NUL-delimited input from stdin and - put it in argv[]. Two subsequent NULs terminate the array. Empty - params are encoded as a lone 0x02. Lone 0x02 can't be generated, but - that shouldn't matter in real life. - - If you would like to always preserve argv[0], use this instead: - AFL_INIT_SET0("prog_name"); - -*/ - -#ifndef _HAVE_ARGV_FUZZ_INL -#define _HAVE_ARGV_FUZZ_INL - -#include - -#define AFL_INIT_ARGV() \ - do { \ - \ - argv = afl_init_argv(&argc); \ - \ - } while (0) - -#define AFL_INIT_SET0(_p) \ - do { \ - \ - argv = afl_init_argv(&argc); \ - argv[0] = (_p); \ - if (!argc) argc = 1; \ - \ - } while (0) - -#define MAX_CMDLINE_LEN 100000 -#define MAX_CMDLINE_PAR 50000 - -static char **afl_init_argv(int *argc) { - - static char in_buf[MAX_CMDLINE_LEN]; - static char *ret[MAX_CMDLINE_PAR]; - - char *ptr = in_buf; - int rc = 0; - - if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} - - while (*ptr && rc < MAX_CMDLINE_PAR) { - - ret[rc] = ptr; - if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; - rc++; - - while (*ptr) - ptr++; - ptr++; - - } - - *argc = rc; - - return ret; - -} - -#undef MAX_CMDLINE_LEN -#undef MAX_CMDLINE_PAR - -#endif /* !_HAVE_ARGV_FUZZ_INL */ - diff --git a/examples/argv_fuzzing/argvfuzz.c b/examples/argv_fuzzing/argvfuzz.c deleted file mode 100644 index 4251ca4c..00000000 --- a/examples/argv_fuzzing/argvfuzz.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries - ------------------------------------------------------------ - - Copyright 2019-2020 Kjell Braden - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define _GNU_SOURCE /* for RTLD_NEXT */ -#include -#include -#include -#include -#include "argv-fuzz-inl.h" - -int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), - void (*rtld_fini)(void), void *stack_end) { - - int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), - void *stack_end); - int sub_argc; - char **sub_argv; - - (void)argc; - (void)argv; - - orig = dlsym(RTLD_NEXT, __func__); - - if (!orig) { - - fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); - exit(EXIT_FAILURE); - - } - - sub_argv = afl_init_argv(&sub_argc); - - return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); - -} - diff --git a/examples/asan_cgroups/limit_memory.sh b/examples/asan_cgroups/limit_memory.sh deleted file mode 100755 index 1f0f04ad..00000000 --- a/examples/asan_cgroups/limit_memory.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env bash -# -# american fuzzy lop++ - limit memory using cgroups -# ----------------------------------------------- -# -# Written by Samir Khakimov and -# David A. Wheeler -# -# Edits to bring the script in line with afl-cmin and other companion scripts -# by Michal Zalewski. All bugs are my fault. -# -# Copyright 2015 Institute for Defense Analyses. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This tool allows the amount of actual memory allocated to a program -# to be limited on Linux systems using cgroups, instead of the traditional -# setrlimit() API. This helps avoid the address space problems discussed in -# docs/notes_for_asan.md. -# -# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some -# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed -# task. -# - -echo "cgroup tool for afl-fuzz by and " -echo - -unset NEW_USER -MEM_LIMIT="50" - -while getopts "+u:m:" opt; do - - case "$opt" in - - "u") - NEW_USER="$OPTARG" - ;; - - "m") - MEM_LIMIT="$[OPTARG]" - ;; - - "?") - exit 1 - ;; - - esac - -done - -if [ "$MEM_LIMIT" -lt "5" ]; then - echo "[-] Error: malformed or dangerously low value of -m." 1>&2 - exit 1 -fi - -shift $((OPTIND-1)) - -TARGET_BIN="$1" - -if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then - - cat 1>&2 <<_EOF_ -Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] - -Required parameters: - - -u user - run the fuzzer as a specific user after setting up limits - -Optional parameters: - - -m megs - set memory limit to a specified value ($MEM_LIMIT MB) - -This tool configures cgroups-based memory limits for a fuzzing job to simplify -the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in -conjunction with '-m none' passed to the afl-fuzz binary itself, say: - - $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target - -_EOF_ - - exit 1 - -fi - -# Basic sanity checks - -if [ ! "`uname -s`" = "Linux" ]; then - echo "[-] Error: this tool does not support non-Linux systems." 1>&2 - exit 1 -fi - -if [ ! "`id -u`" = "0" ]; then - echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 - exit 1 -fi - -if ! type cgcreate 2>/dev/null 1>&2; then - - echo "[-] Error: you need to install cgroup tools first." 1>&2 - - if type apt-get 2>/dev/null 1>&2; then - echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 - elif type yum 2>/dev/null 1>&2; then - echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 - fi - - exit 1 - -fi - -if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then - echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 - exit 1 -fi - -# Create a new cgroup path if necessary... We used PID-keyed groups to keep -# parallel afl-fuzz tasks separate from each other. - -CID="afl-$NEW_USER-$$" - -CPATH="/sys/fs/cgroup/memory/$CID" - -if [ ! -d "$CPATH" ]; then - - cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 - -fi - -# Set the appropriate limit... - -if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null - echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -elif grep -qE 'partition|file' /proc/swaps; then - - echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 - exit 1 - -else - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -fi - -# All right. At this point, we can just run the command. - -cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" - -cgdelete -g "memory:$CID" diff --git a/examples/bash_shellshock/shellshock-fuzz.diff b/examples/bash_shellshock/shellshock-fuzz.diff deleted file mode 100644 index 3fa05bf8..00000000 --- a/examples/bash_shellshock/shellshock-fuzz.diff +++ /dev/null @@ -1,59 +0,0 @@ -This patch shows a very simple way to find post-Shellshock bugs in bash, as -discussed here: - - http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html - -In essence, it shows a way to fuzz environmental variables. Instructions: - -1) Download bash 4.3, apply this patch, compile with: - - CC=/path/to/afl-gcc ./configure - make clean all - - Note that the harness puts the fuzzed output in $TEST_VARIABLE. With - Florian's Shellshock patch (bash43-028), this is no longer passed down - to the parser. - -2) Create and cd to an empty directory, put the compiled bash binary in - there, and run these commands: - - mkdir in_dir - echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt - -3) Run the fuzzer with: - - /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : - - The -d parameter is advisable only if the tested shell is fairly slow - or if you are in a hurry; will cover more ground faster, but - less systematically. - -4) Watch for crashes in out_dir/crashes/. Also watch for any new files - created in cwd if you're interested in non-crash RCEs (files will be - created whenever the shell executes "foo>bar" or something like - that). You can correlate their creation date with new entries in - out_dir/queue/. - - You can also modify the bash binary to directly check for more subtle - fault conditions, or use the synthesized entries in out_dir/queue/ - as a seed for other, possibly slower or more involved testing regimes. - - Expect several hours to get decent coverage. - ---- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 -+++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 -@@ -371,6 +371,14 @@ - env = environ; - #endif /* __OPENNT */ - -+ { -+ -+ static char val[1024 * 16]; -+ read(0, val, sizeof(val) - 1); -+ setenv("TEST_VARIABLE", val, 1); -+ -+ } -+ - USE_VAR(argc); - USE_VAR(argv); - USE_VAR(env); diff --git a/examples/canvas_harness/canvas_harness.html b/examples/canvas_harness/canvas_harness.html deleted file mode 100644 index a37b6937..00000000 --- a/examples/canvas_harness/canvas_harness.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - -

- -
- - - -

Results

- -
    - - - - diff --git a/examples/clang_asm_normalize/as b/examples/clang_asm_normalize/as deleted file mode 100755 index 45537cae..00000000 --- a/examples/clang_asm_normalize/as +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - clang assembly normalizer -# ---------------------------------------------- -# -# Originally written by Michal Zalewski -# The idea for this wrapper comes from Ryan Govostes. -# -# Copyright 2013, 2014 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This 'as' wrapper should allow you to instrument unruly, hand-written -# assembly with afl-as. -# -# Usage: -# -# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ -# AFL_PATH=/path/to/this/directory/ make clean all - -if [ "$#" -lt "2" ]; then - echo "[-] Error: this utility can't be called directly." 1>&2 - exit 1 -fi - -if [ "$AFL_REAL_PATH" = "" ]; then - echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 - exit 1 -fi - -if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then - echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 - exit 1 -fi - -unset __AFL_AS_CMDLINE __AFL_FNAME - -while [ ! "$#" = "0" ]; do - - if [ "$#" = "1" ]; then - __AFL_FNAME="$1" - else - __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" - fi - - shift - -done - -test "$TMPDIR" = "" && TMPDIR=/tmp - -TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` - -test "$TMPFILE" = "" && exit 1 - -clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" - -ERR="$?" - -if [ ! "$ERR" = "0" ]; then - rm -f "$TMPFILE" - exit $ERR -fi - -"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" - -ERR="$?" - -rm -f "$TMPFILE" - -exit "$ERR" diff --git a/examples/crash_triage/triage_crashes.sh b/examples/crash_triage/triage_crashes.sh deleted file mode 100755 index bf763cba..00000000 --- a/examples/crash_triage/triage_crashes.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - crash triage utility -# ----------------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Note that this assumes that the targeted application reads from stdin -# and requires no other cmdline parameters. Modify as needed if this is -# not the case. -# -# Note that on OpenBSD, you may need to install a newer version of gdb -# (e.g., from ports). You can set GDB=/some/path to point to it if -# necessary. -# - -echo "crash triage utility for afl-fuzz by Michal Zalewski" -echo - -ulimit -v 100000 2>/dev/null -ulimit -d 100000 2>/dev/null - -if [ "$#" -lt "2" ]; then - echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 - echo 1>&2 - exit 1 -fi - -DIR="$1" -BIN="$2" -shift -shift - -if [ "$AFL_ALLOW_TMP" = "" ]; then - - echo "$DIR" | grep -qE '^(/var)?/tmp/' - T1="$?" - - echo "$BIN" | grep -qE '^(/var)?/tmp/' - T2="$?" - - if [ "$T1" = "0" -o "$T2" = "0" ]; then - echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 - exit 1 - fi - -fi - -if - [ "$GDB" = "" ]; then - GDB=gdb -fi - -if [ ! -f "$BIN" -o ! -x "$BIN" ]; then - echo "[-] Error: binary '$2' not found or is not executable." 1>&2 - exit 1 -fi - -if [ ! -d "$DIR/queue" ]; then - echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 - exit 1 -fi - -CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) - -if [ "$CCOUNT" = "0" ]; then - echo "No crashes recorded in the target directory - nothing to be done." - exit 0 -fi - -echo - -for crash in $DIR/crashes/id:*; do - - id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` - sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` - - # Grab the args, converting @@ to $crash - - use_args="" - use_stdio=1 - - for a in $@; do - - if [ "$a" = "@@" ] ; then - use_args="$use_args $crash" - unset use_stdio - else - use_args="$use_args $a" - fi - - done - - # Strip the trailing space - use_args="${use_args# }" - - echo "+++ ID $id, SIGNAL $sig +++" - echo - - if [ "$use_stdio" = "1" ]; then - $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0 - -#define INITIAL_GROWTH_SIZE (64) - -#define RAND_BELOW(limit) (rand() % (limit)) - -/* Use in a struct: creates a name_buf and a name_size variable. */ -#define BUF_VAR(type, name) \ - type * name##_buf; \ - size_t name##_size; -/* this fills in `&structptr->something_buf, &structptr->something_size`. */ -#define BUF_PARAMS(struct, name) \ - (void **)&struct->name##_buf, &struct->name##_size - -typedef struct { - -} afl_t; - -static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { - - static s8 interesting_8[] = {INTERESTING_8}; - static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; - static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; - - switch (RAND_BELOW(12)) { - - case 0: { - - /* Flip a single bit somewhere. Spooky! */ - - s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); - - out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); - - break; - - } - - case 1: { - - /* Set byte to interesting value. */ - - u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; - out_buf[(RAND_BELOW(end - begin) + begin)] = val; - - break; - - } - - case 2: { - - /* Set word to interesting value, randomly choosing endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u16 *)(out_buf + byte_idx) = - interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; - break; - case 1: - *(u16 *)(out_buf + byte_idx) = - SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); - break; - - } - - break; - - } - - case 3: { - - /* Set dword to interesting value, randomly choosing endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u32 *)(out_buf + byte_idx) = - interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u32 *)(out_buf + byte_idx) = - SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 4: { - - /* Set qword to interesting value, randomly choosing endian. */ - - if (end - begin < 8) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 7) break; - - switch (RAND_BELOW(2)) { - - case 0: - *(u64 *)(out_buf + byte_idx) = - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; - break; - case 1: - *(u64 *)(out_buf + byte_idx) = SWAP64( - (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); - break; - - } - - break; - - } - - case 5: { - - /* Randomly subtract from byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 6: { - - /* Randomly add to byte. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); - - break; - - } - - case 7: { - - /* Randomly subtract from word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 8: { - - /* Randomly add to word, random endian. */ - - if (end - begin < 2) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 1) break; - - if (RAND_BELOW(2)) { - - *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u16 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u16 *)(out_buf + byte_idx) = - SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 9: { - - /* Randomly subtract from dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); - - } - - break; - - } - - case 10: { - - /* Randomly add to dword, random endian. */ - - if (end - begin < 4) break; - - s32 byte_idx = (RAND_BELOW(end - begin) + begin); - - if (byte_idx >= end - 3) break; - - if (RAND_BELOW(2)) { - - *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); - - } else { - - u32 num = 1 + RAND_BELOW(ARITH_MAX); - - *(u32 *)(out_buf + byte_idx) = - SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); - - } - - break; - - } - - case 11: { - - /* Just set a random byte to a random value. Because, - why not. We use XOR with 1-255 to eliminate the - possibility of a no-op. */ - - out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); - - break; - - } - - } - -} - -/* This function calculates the next power of 2 greater or equal its argument. - @return The rounded up power of 2 (if no overflow) or 0 on overflow. -*/ -static inline size_t next_pow2(size_t in) { - - if (in == 0 || in > (size_t)-1) - return 0; /* avoid undefined behaviour under-/overflow */ - size_t out = in - 1; - out |= out >> 1; - out |= out >> 2; - out |= out >> 4; - out |= out >> 8; - out |= out >> 16; - return out + 1; - -} - -/* This function makes sure *size is > size_needed after call. - It will realloc *buf otherwise. - *size will grow exponentially as per: - https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ - Will return NULL and free *buf if size_needed is <1 or realloc failed. - @return For convenience, this function returns *buf. - */ -static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { - - /* No need to realloc */ - if (likely(size_needed && *size >= size_needed)) return *buf; - - /* No initial size was set */ - if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; - - /* grow exponentially */ - size_t next_size = next_pow2(size_needed); - - /* handle overflow */ - if (!next_size) { next_size = size_needed; } - - /* alloc */ - *buf = realloc(*buf, next_size); - *size = *buf ? next_size : 0; - - return *buf; - -} - -/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ -static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, - size_t *size2) { - - void * scratch_buf = *buf1; - size_t scratch_size = *size1; - *buf1 = *buf2; - *size1 = *size2; - *buf2 = scratch_buf; - *size2 = scratch_size; - -} - -#undef INITIAL_GROWTH_SIZE - -#endif - diff --git a/examples/custom_mutators/example.c b/examples/custom_mutators/example.c deleted file mode 100644 index 23add128..00000000 --- a/examples/custom_mutators/example.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - New Custom Mutator for AFL++ - Written by Khaled Yakdan - Andrea Fioraldi - Shengtuo Hu - Dominik Maier -*/ - -// You need to use -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" - -#include -#include -#include -#include - -#define DATA_SIZE (100) - -static const char *commands[] = { - - "GET", - "PUT", - "DEL", - -}; - -typedef struct my_mutator { - - afl_t *afl; - - // any additional data here! - size_t trim_size_current; - int trimmming_steps; - int cur_step; - - // Reused buffers: - BUF_VAR(u8, fuzz); - BUF_VAR(u8, data); - BUF_VAR(u8, havoc); - BUF_VAR(u8, trim); - BUF_VAR(u8, post_process); - -} my_mutator_t; - -/** - * Initialize this custom mutator - * - * @param[in] afl a pointer to the internal state object. Can be ignored for - * now. - * @param[in] seed A seed for this mutator - the same seed should always mutate - * in the same way. - * @return Pointer to the data object this custom mutator instance should use. - * There may be multiple instances of this mutator in one afl-fuzz run! - * Return NULL on error. - */ -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { - - srand(seed); // needed also by surgical_havoc_mutate() - - my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); - if (!data) { - - perror("afl_custom_init alloc"); - return NULL; - - } - - data->afl = afl; - - return data; - -} - -/** - * Perform custom mutations on a given input - * - * (Optional for now. Required in the future) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Pointer to input data to be mutated - * @param[in] buf_size Size of input data - * @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on - * error. - * @param[in] add_buf Buffer containing the additional test case - * @param[in] add_buf_size Size of the additional test case - * @param[in] max_size Maximum size of the mutated output. The mutation must not - * produce data larger than max_size. - * @return Size of the mutated output. - */ -size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, - u8 **out_buf, uint8_t *add_buf, - size_t add_buf_size, // add_buf can be NULL - size_t max_size) { - - // Make sure that the packet size does not exceed the maximum size expected by - // the fuzzer - size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; - - // maybe_grow is optimized to be quick for reused buffers. - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } - - // Randomly select a command string to add as a header to the packet - memcpy(mutated_out, commands[rand() % 3], 3); - - // Mutate the payload of the packet - int i; - for (i = 0; i < 8; ++i) { - - // Randomly perform one of the (no len modification) havoc mutations - surgical_havoc_mutate(mutated_out, 3, mutated_size); - - } - - *out_buf = mutated_out; - return mutated_size; - -} - -/** - * A post-processing function to use right before AFL writes the test case to - * disk in order to execute the target. - * - * (Optional) If this functionality is not needed, simply don't define this - * function. - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Buffer containing the test case to be executed - * @param[in] buf_size Size of the test case - * @param[out] out_buf Pointer to the buffer containing the test case after - * processing. External library should allocate memory for out_buf. - * The buf pointer may be reused (up to the given buf_size); - * @return Size of the output buffer after processing or the needed amount. - * A return of 0 indicates an error. - */ -size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, - size_t buf_size, uint8_t **out_buf) { - - uint8_t *post_process_buf = - maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5); - if (!post_process_buf) { - - perror("custom mutator realloc failed."); - *out_buf = NULL; - return 0; - - } - - memcpy(post_process_buf + 5, buf, buf_size); - post_process_buf[0] = 'A'; - post_process_buf[1] = 'F'; - post_process_buf[2] = 'L'; - post_process_buf[3] = '+'; - post_process_buf[4] = '+'; - - *out_buf = post_process_buf; - - return buf_size + 5; - -} - -/** - * This method is called at the start of each trimming operation and receives - * the initial buffer. It should return the amount of iteration steps possible - * on this input (e.g. if your input has n elements and you want to remove - * them one by one, return n, if you do a binary search, return log(n), - * and so on...). - * - * If your trimming algorithm doesn't allow you to determine the amount of - * (remaining) steps easily (esp. while running), then you can alternatively - * return 1 here and always return 0 in post_trim until you are finished and - * no steps remain. In that case, returning 1 in post_trim will end the - * trimming routine. The whole current index/max iterations stuff is only used - * to show progress. - * - * (Optional) - * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param buf Buffer containing the test case - * @param buf_size Size of the test case - * @return The amount of possible iteration steps to trim the input. - * negative on error. - */ -int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, - size_t buf_size) { - - // We simply trim once - data->trimmming_steps = 1; - - data->cur_step = 0; - - if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) { - - perror("init_trim grow"); - return -1; - - } - - memcpy(data->trim_buf, buf, buf_size); - - data->trim_size_current = buf_size; - - return data->trimmming_steps; - -} - -/** - * This method is called for each trimming operation. It doesn't have any - * arguments because we already have the initial buffer from init_trim and we - * can memorize the current state in *data. This can also save - * reparsing steps for each iteration. It should return the trimmed input - * buffer, where the returned data must not exceed the initial input data in - * length. Returning anything that is larger than the original data (passed - * to init_trim) will result in a fatal abort of AFLFuzz. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[out] out_buf Pointer to the buffer containing the trimmed test case. - * External library should allocate memory for out_buf. - * AFL++ will not release the memory after saving the test case. - * Keep a ref in *data. - * *out_buf = NULL is treated as error. - * @return Pointer to the size of the trimmed test case - */ -size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) { - - *out_buf = data->trim_buf; - - // Remove the last byte of the trimming input - return data->trim_size_current - 1; - -} - -/** - * This method is called after each trim operation to inform you if your - * trimming step was successful or not (in terms of coverage). If you receive - * a failure here, you should reset your input to the last known good state. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param success Indicates if the last trim operation was successful. - * @return The next trim iteration index (from 0 to the maximum amount of - * steps returned in init_trim). negative ret on failure. - */ -int32_t afl_custom_post_trim(my_mutator_t *data, int success) { - - if (success) { - - ++data->cur_step; - return data->cur_step; - - } - - return data->trimmming_steps; - -} - -/** - * Perform a single custom mutation on a given input. - * This mutation is stacked with the other muatations in havoc. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param[in] buf Pointer to the input data to be mutated and the mutated - * output - * @param[in] buf_size Size of input data - * @param[out] out_buf The output buffer. buf can be reused, if the content - * fits. *out_buf = NULL is treated as error. - * @param[in] max_size Maximum size of the mutated output. The mutation must - * not produce data larger than max_size. - * @return Size of the mutated output. - */ -size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, - u8 **out_buf, size_t max_size) { - - if (buf_size == 0) { - - *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); - if (!*out_buf) { - - perror("custom havoc: maybe_grow"); - return 0; - - } - - **out_buf = rand() % 256; - buf_size = 1; - - } else { - - // We reuse buf here. It's legal and faster. - *out_buf = buf; - - } - - size_t victim = rand() % buf_size; - (*out_buf)[victim] += rand() % 10; - - return buf_size; - -} - -/** - * Return the probability (in percentage) that afl_custom_havoc_mutation - * is called in havoc. By default it is 6 %. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @return The probability (0-100). - */ -uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) { - - return 5; // 5 % - -} - -/** - * Determine whether the fuzzer should fuzz the queue entry or not. - * - * (Optional) - * - * @param[in] data pointer returned in afl_custom_init for this fuzz case - * @param filename File name of the test case in the queue entry - * @return Return True(1) if the fuzzer will fuzz the queue entry, and - * False(0) otherwise. - */ -uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) { - - return 1; - -} - -/** - * Allow for additional analysis (e.g. calling a different tool that does a - * different kind of coverage and saves this for the custom mutator). - * - * (Optional) - * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param filename_new_queue File name of the new queue entry - * @param filename_orig_queue File name of the original queue entry - */ -void afl_custom_queue_new_entry(my_mutator_t * data, - const uint8_t *filename_new_queue, - const uint8_t *filename_orig_queue) { - - /* Additional analysis on the original or new test case */ - -} - -/** - * Deinitialize everything - * - * @param data The data ptr from afl_custom_init - */ -void afl_custom_deinit(my_mutator_t *data) { - - free(data->post_process_buf); - free(data->havoc_buf); - free(data->data_buf); - free(data->fuzz_buf); - free(data->trim_buf); - free(data); - -} - diff --git a/examples/custom_mutators/example.py b/examples/custom_mutators/example.py deleted file mode 100644 index cf659e5a..00000000 --- a/examples/custom_mutators/example.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -''' -Example Python Module for AFLFuzz - -@author: Christian Holler (:decoder) - -@license: - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at http://mozilla.org/MPL/2.0/. - -@contact: choller@mozilla.com -''' - -import random - - -COMMANDS = [ - b"GET", - b"PUT", - b"DEL", - b"AAAAAAAAAAAAAAAAA", -] - - -def init(seed): - ''' - Called once when AFLFuzz starts up. Used to seed our RNG. - - @type seed: int - @param seed: A 32-bit random value - ''' - random.seed(seed) - - -def deinit(): - pass - - -def fuzz(buf, add_buf, max_size): - ''' - Called per fuzzing iteration. - - @type buf: bytearray - @param buf: The buffer that should be mutated. - - @type add_buf: bytearray - @param add_buf: A second buffer that can be used as mutation source. - - @type max_size: int - @param max_size: Maximum size of the mutated output. The mutation must not - produce data larger than max_size. - - @rtype: bytearray - @return: A new bytearray containing the mutated data - ''' - ret = bytearray(100) - - ret[:3] = random.choice(COMMANDS) - - return ret - -# Uncomment and implement the following methods if you want to use a custom -# trimming algorithm. See also the documentation for a better API description. - -# def init_trim(buf): -# ''' -# Called per trimming iteration. -# -# @type buf: bytearray -# @param buf: The buffer that should be trimmed. -# -# @rtype: int -# @return: The maximum number of trimming steps. -# ''' -# global ... -# -# # Initialize global variables -# -# # Figure out how many trimming steps are possible. -# # If this is not possible for your trimming, you can -# # return 1 instead and always return 0 in post_trim -# # until you are done (then you return 1). -# -# return steps -# -# def trim(): -# ''' -# Called per trimming iteration. -# -# @rtype: bytearray -# @return: A new bytearray containing the trimmed data. -# ''' -# global ... -# -# # Implement the actual trimming here -# -# return bytearray(...) -# -# def post_trim(success): -# ''' -# Called after each trimming operation. -# -# @type success: bool -# @param success: Indicates if the last trim operation was successful. -# -# @rtype: int -# @return: The next trim index (0 to max number of steps) where max -# number of steps indicates the trimming is done. -# ''' -# global ... -# -# if not success: -# # Restore last known successful input, determine next index -# else: -# # Just determine the next index, based on what was successfully -# # removed in the last step -# -# return next_index -# -# def post_process(buf): -# ''' -# Called just before the execution to write the test case in the format -# expected by the target -# -# @type buf: bytearray -# @param buf: The buffer containing the test case to be executed -# -# @rtype: bytearray -# @return: The buffer containing the test case after -# ''' -# return buf -# -# def havoc_mutation(buf, max_size): -# ''' -# Perform a single custom mutation on a given input. -# -# @type buf: bytearray -# @param buf: The buffer that should be mutated. -# -# @type max_size: int -# @param max_size: Maximum size of the mutated output. The mutation must not -# produce data larger than max_size. -# -# @rtype: bytearray -# @return: A new bytearray containing the mutated data -# ''' -# return mutated_buf -# -# def havoc_mutation_probability(): -# ''' -# Called for each `havoc_mutation`. Return the probability (in percentage) -# that `havoc_mutation` is called in havoc. Be default it is 6%. -# -# @rtype: int -# @return: The probability (0-100) -# ''' -# return prob -# -# def queue_get(filename): -# ''' -# Called at the beginning of each fuzz iteration to determine whether the -# test case should be fuzzed -# -# @type filename: str -# @param filename: File name of the test case in the current queue entry -# -# @rtype: bool -# @return: Return True if the custom mutator decides to fuzz the test case, -# and False otherwise -# ''' -# return True -# -# def queue_new_entry(filename_new_queue, filename_orig_queue): -# ''' -# Called after adding a new test case to the queue -# -# @type filename_new_queue: str -# @param filename_new_queue: File name of the new queue entry -# -# @type filename_orig_queue: str -# @param filename_orig_queue: File name of the original queue entry -# ''' -# pass diff --git a/examples/custom_mutators/post_library_gif.so.c b/examples/custom_mutators/post_library_gif.so.c deleted file mode 100644 index ac10f409..00000000 --- a/examples/custom_mutators/post_library_gif.so.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - american fuzzy lop++ - postprocessor library example - -------------------------------------------------- - - Originally written by Michal Zalewski - Edited by Dominik Maier, 2020 - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - Postprocessor libraries can be passed to afl-fuzz to perform final cleanup - of any mutated test cases - for example, to fix up checksums in PNG files. - - Please heed the following warnings: - - 1) In almost all cases, it is more productive to comment out checksum logic - in the targeted binary (as shown in ../libpng_no_checksum/). One possible - exception is the process of fuzzing binary-only software in QEMU mode. - - 2) The use of postprocessors for anything other than checksums is - questionable and may cause more harm than good. AFL is normally pretty good - about dealing with length fields, magic values, etc. - - 3) Postprocessors that do anything non-trivial must be extremely robust to - gracefully handle malformed data and other error conditions - otherwise, - they will crash and take afl-fuzz down with them. Be wary of reading past - *len and of integer overflows when calculating file offsets. - - In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, - honestly know what you're doing =) - - With that out of the way: the postprocessor library is passed to afl-fuzz - via AFL_POST_LIBRARY. The library must be compiled with: - - gcc -shared -Wall -O3 post_library.so.c -o post_library.so - - AFL will call the afl_custom_post_process() function for every mutated output - buffer. From there, you have three choices: - - 1) If you don't want to modify the test case, simply set `*out_buf = in_buf` - and return the original `len`. - - 2) If you want to skip this test case altogether and have AFL generate a - new one, return 0 or set `*out_buf = NULL`. - Use this sparingly - it's faster than running the target program - with patently useless inputs, but still wastes CPU time. - - 3) If you want to modify the test case, allocate an appropriately-sized - buffer, move the data into that buffer, make the necessary changes, and - then return the new pointer as out_buf. Return an appropriate len - afterwards. - - Note that the buffer will *not* be freed for you. To avoid memory leaks, - you need to free it or reuse it on subsequent calls (as shown below). - - *** Feel free to reuse the original 'in_buf' BUFFER and return it. *** - - Aight. The example below shows a simple postprocessor that tries to make - sure that all input files start with "GIF89a". - - PS. If you don't like C, you can try out the unix-based wrapper from - Ben Nagy instead: https://github.com/bnagy/aflfix - - */ - -#include -#include -#include - -/* Header that must be present at the beginning of every test case: */ - -#define HEADER "GIF89a" - -typedef struct post_state { - - unsigned char *buf; - size_t size; - -} post_state_t; - -void *afl_custom_init(void *afl) { - - post_state_t *state = malloc(sizeof(post_state_t)); - if (!state) { - - perror("malloc"); - return NULL; - - } - - state->buf = calloc(sizeof(unsigned char), 4096); - if (!state->buf) { - - free(state); - perror("calloc"); - return NULL; - - } - - return state; - -} - -/* The actual postprocessor routine called by afl-fuzz: */ - -size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, - unsigned int len, unsigned char **out_buf) { - - /* Skip execution altogether for buffers shorter than 6 bytes (just to - show how it's done). We can trust len to be sane. */ - - if (len < strlen(HEADER)) return 0; - - /* Do nothing for buffers that already start with the expected header. */ - - if (!memcmp(in_buf, HEADER, strlen(HEADER))) { - - *out_buf = in_buf; - return len; - - } - - /* Allocate memory for new buffer, reusing previous allocation if - possible. */ - - *out_buf = realloc(data->buf, len); - - /* If we're out of memory, the most graceful thing to do is to return the - original buffer and give up on modifying it. Let AFL handle OOM on its - own later on. */ - - if (!*out_buf) { - - *out_buf = in_buf; - return len; - - } - - /* Copy the original data to the new location. */ - - memcpy(*out_buf, in_buf, len); - - /* Insert the new header. */ - - memcpy(*out_buf, HEADER, strlen(HEADER)); - - /* Return the new len. It hasn't changed, so it's just len. */ - - return len; - -} - -/* Gets called afterwards */ -void afl_custom_deinit(post_state_t *data) { - - free(data->buf); - free(data); - -} - diff --git a/examples/custom_mutators/post_library_png.so.c b/examples/custom_mutators/post_library_png.so.c deleted file mode 100644 index 941f7e55..00000000 --- a/examples/custom_mutators/post_library_png.so.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - american fuzzy lop++ - postprocessor for PNG - ------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - Adapted to the new API, 2020 by Dominik Maier - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - See post_library.so.c for a general discussion of how to implement - postprocessors. This specific postprocessor attempts to fix up PNG - checksums, providing a slightly more complicated example than found - in post_library.so.c. - - Compile with: - - gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz - - */ - -#include -#include -#include -#include -#include - -#include - -/* A macro to round an integer up to 4 kB. */ - -#define UP4K(_i) ((((_i) >> 12) + 1) << 12) - -typedef struct post_state { - - unsigned char *buf; - size_t size; - -} post_state_t; - -void *afl_custom_init(void *afl) { - - post_state_t *state = malloc(sizeof(post_state_t)); - if (!state) { - - perror("malloc"); - return NULL; - - } - - state->buf = calloc(sizeof(unsigned char), 4096); - if (!state->buf) { - - free(state); - perror("calloc"); - return NULL; - - } - - return state; - -} - -size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, - unsigned int len, - const unsigned char **out_buf) { - - unsigned char *new_buf = (unsigned char *)in_buf; - unsigned int pos = 8; - - /* Don't do anything if there's not enough room for the PNG header - (8 bytes). */ - - if (len < 8) { - - *out_buf = in_buf; - return len; - - } - - /* Minimum size of a zero-length PNG chunk is 12 bytes; if we - don't have that, we can bail out. */ - - while (pos + 12 <= len) { - - unsigned int chunk_len, real_cksum, file_cksum; - - /* Chunk length is the first big-endian dword in the chunk. */ - - chunk_len = ntohl(*(uint32_t *)(in_buf + pos)); - - /* Bail out if chunk size is too big or goes past EOF. */ - - if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break; - - /* Chunk checksum is calculated for chunk ID (dword) and the actual - payload. */ - - real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); - - /* The in-file checksum is the last dword past the chunk data. */ - - file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len); - - /* If the checksums do not match, we need to fix the file. */ - - if (real_cksum != file_cksum) { - - /* First modification? Make a copy of the input buffer. Round size - up to 4 kB to minimize the number of reallocs needed. */ - - if (new_buf == in_buf) { - - if (len <= data->size) { - - new_buf = data->buf; - - } else { - - new_buf = realloc(data->buf, UP4K(len)); - if (!new_buf) { - - *out_buf = in_buf; - return len; - - } - - data->buf = new_buf; - data->size = UP4K(len); - memcpy(new_buf, in_buf, len); - - } - - } - - *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; - - } - - /* Skip the entire chunk and move to the next one. */ - - pos += 12 + chunk_len; - - } - - *out_buf = new_buf; - return len; - -} - -/* Gets called afterwards */ -void afl_custom_deinit(post_state_t *data) { - - free(data->buf); - free(data); - -} - diff --git a/examples/custom_mutators/simple-chunk-replace.py b/examples/custom_mutators/simple-chunk-replace.py deleted file mode 100644 index df2f4ca7..00000000 --- a/examples/custom_mutators/simple-chunk-replace.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -''' -Simple Chunk Cross-Over Replacement Module for AFLFuzz - -@author: Christian Holler (:decoder) - -@license: - -This Source Code Form is subject to the terms of the Mozilla Public -License, v. 2.0. If a copy of the MPL was not distributed with this -file, You can obtain one at http://mozilla.org/MPL/2.0/. - -@contact: choller@mozilla.com -''' - -import random - - -def init(seed): - ''' - Called once when AFLFuzz starts up. Used to seed our RNG. - - @type seed: int - @param seed: A 32-bit random value - ''' - # Seed our RNG - random.seed(seed) - - -def fuzz(buf, add_buf, max_size): - ''' - Called per fuzzing iteration. - - @type buf: bytearray - @param buf: The buffer that should be mutated. - - @type add_buf: bytearray - @param add_buf: A second buffer that can be used as mutation source. - - @type max_size: int - @param max_size: Maximum size of the mutated output. The mutation must not - produce data larger than max_size. - - @rtype: bytearray - @return: A new bytearray containing the mutated data - ''' - # Make a copy of our input buffer for returning - ret = bytearray(buf) - - # Take a random fragment length between 2 and 32 (or less if add_buf is shorter) - fragment_len = random.randint(1, min(len(add_buf), 32)) - - # Determine a random source index where to take the data chunk from - rand_src_idx = random.randint(0, len(add_buf) - fragment_len) - - # Determine a random destination index where to put the data chunk - rand_dst_idx = random.randint(0, len(buf)) - - # Make the chunk replacement - ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len] - - # Return data - return ret diff --git a/examples/custom_mutators/simple_example.c b/examples/custom_mutators/simple_example.c deleted file mode 100644 index d888ec1f..00000000 --- a/examples/custom_mutators/simple_example.c +++ /dev/null @@ -1,74 +0,0 @@ -// This simple example just creates random buffer <= 100 filled with 'A' -// needs -I /path/to/AFLplusplus/include -#include "custom_mutator_helpers.h" - -#include -#include -#include -#include - -#ifndef _FIXED_CHAR - #define _FIXED_CHAR 0x41 -#endif - -typedef struct my_mutator { - - afl_t *afl; - - // Reused buffers: - BUF_VAR(u8, fuzz); - -} my_mutator_t; - -my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { - - srand(seed); - my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); - if (!data) { - - perror("afl_custom_init alloc"); - return NULL; - - } - - data->afl = afl; - - return data; - -} - -size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, - u8 **out_buf, uint8_t *add_buf, - size_t add_buf_size, // add_buf can be NULL - size_t max_size) { - - int size = (rand() % 100) + 1; - if (size > max_size) size = max_size; - u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size); - if (!mutated_out) { - - *out_buf = NULL; - perror("custom mutator allocation (maybe_grow)"); - return 0; /* afl-fuzz will very likely error out after this. */ - - } - - memset(mutated_out, _FIXED_CHAR, size); - - *out_buf = mutated_out; - return size; - -} - -/** - * Deinitialize everything - * - * @param data The data ptr from afl_custom_init - */ -void afl_custom_deinit(my_mutator_t *data) { - - free(data->fuzz_buf); - free(data); - -} - diff --git a/examples/custom_mutators/wrapper_afl_min.py b/examples/custom_mutators/wrapper_afl_min.py deleted file mode 100644 index ecb03b55..00000000 --- a/examples/custom_mutators/wrapper_afl_min.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python - -from XmlMutatorMin import XmlMutatorMin - -# Default settings (production mode) - -__mutator__ = None -__seed__ = "RANDOM" -__log__ = False -__log_file__ = "wrapper.log" - - -# AFL functions -def log(text): - """ - Logger - """ - - global __seed__ - global __log__ - global __log_file__ - - if __log__: - with open(__log_file__, "a") as logf: - logf.write("[%s] %s\n" % (__seed__, text)) - - -def init(seed): - """ - Called once when AFL starts up. Seed is used to identify the AFL instance in log files - """ - - global __mutator__ - global __seed__ - - # Get the seed - __seed__ = seed - - # Create a global mutation class - try: - __mutator__ = XmlMutatorMin(__seed__, verbose=__log__) - log("init(): Mutator created") - except RuntimeError as e: - log("init(): Can't create mutator: %s" % e.message) - - -def fuzz(buf, add_buf, max_size): - """ - Called for each fuzzing iteration. - """ - - global __mutator__ - - # Do we have a working mutator object? - if __mutator__ is None: - log("fuzz(): Can't fuzz, no mutator available") - return buf - - # Try to use the AFL buffer - via_buffer = True - - # Interpret the AFL buffer (an array of bytes) as a string - if via_buffer: - try: - buf_str = str(buf) - log("fuzz(): AFL buffer converted to a string") - except Exception: - via_buffer = False - log("fuzz(): Can't convert AFL buffer to a string") - - # Load XML from the AFL string - if via_buffer: - try: - __mutator__.init_from_string(buf_str) - log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str)) - except Exception: - via_buffer = False - log("fuzz(): Can't initialize mutator with AFL buffer") - - # If init from AFL buffer wasn't succesful - if not via_buffer: - log("fuzz(): Returning unmodified AFL buffer") - return buf - - # Sucessful initialization -> mutate - try: - __mutator__.mutate(max=5) - log("fuzz(): Input mutated") - except Exception: - log("fuzz(): Can't mutate input => returning buf") - return buf - - # Convert mutated data to a array of bytes - try: - data = bytearray(__mutator__.save_to_string()) - log("fuzz(): Mutated data converted as bytes") - except Exception: - log("fuzz(): Can't convert mutated data to bytes => returning buf") - return buf - - # Everything went fine, returning mutated content - log("fuzz(): Returning %d bytes" % len(data)) - return data - - -# Main (for debug) -if __name__ == '__main__': - - __log__ = True - __log_file__ = "/dev/stdout" - __seed__ = "RANDOM" - - init(__seed__) - - in_1 = bytearray("ffff
    zzzzzzzzzzzz") - in_2 = bytearray("") - out = fuzz(in_1, in_2) - print(out) diff --git a/examples/defork/Makefile b/examples/defork/Makefile deleted file mode 100644 index e8240dba..00000000 --- a/examples/defork/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -# -# american fuzzy lop++ - defork -# ---------------------------------- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) -#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" -# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) -# M32FLAG = -mbe32 -# endif -#endif - -all: defork32.so defork64.so - -defork32.so: defork.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork32 build failure (that's fine)" - -defork64.so: defork.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork64 build failure (that's fine)" - -install: defork32.so defork64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f defork32.so ]; then set -e; install -m 755 defork32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f defork64.so ]; then set -e; install -m 755 defork64.so $(DESTDIR)$(HELPER_PATH)/; fi - -target: - ../../afl-clang forking_target.c -o forking_target -Wall -Wextra -Werror - -clean: - rm -f defork32.so defork64.so forking_target diff --git a/examples/defork/README.md b/examples/defork/README.md deleted file mode 100644 index 7e950323..00000000 --- a/examples/defork/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# defork - -when the target forks, this breaks all normal fuzzing runs. -Sometimes, though, it is enough to just run the child process. -If this is the case, then this LD_PRELOAD library will always return 0 on fork, -the target will belive it is running as the child, post-fork. - -This is defork.c from the amazing preeny project -https://github.com/zardus/preeny - -It is altered for afl++ to work with its fork-server: the initial fork will go through, the second fork will be blocked. diff --git a/examples/defork/defork.c b/examples/defork/defork.c deleted file mode 100644 index f71d1124..00000000 --- a/examples/defork/defork.c +++ /dev/null @@ -1,50 +0,0 @@ -#define __GNU_SOURCE -#include -#include -#include -#include - -#include "../../include/config.h" - -/* we want to fork once (for the afl++ forkserver), - then immediately return as child on subsequent forks. */ -static bool forked = 0; - -pid_t (*original_fork)(void); - -/* In case we are not running in afl, we use a dummy original_fork */ -static pid_t nop(void) { - - return 0; - -} - -__attribute__((constructor)) void preeny_fork_orig() { - - if (getenv(SHM_ENV_VAR)) { - - printf("defork: running in AFL++. Allowing forkserver.\n"); - original_fork = dlsym(RTLD_NEXT, "socket"); - - } else { - - printf("defork: no AFL++ detected. Disabling fork from the start.\n"); - original_fork = &nop; - - } - -} - -pid_t fork(void) { - - /* If we forked before, or if we're in the child (pid==0), - we don't want to fork anymore, else, we are still in the forkserver. - The forkserver parent needs to fork infinite times, each child should never - fork again. This can be written without branches and I hate myself for it. - */ - pid_t ret = !forked && original_fork(); - forked = !ret; - return ret; - -} - diff --git a/examples/defork/forking_target.c b/examples/defork/forking_target.c deleted file mode 100644 index 628d23c9..00000000 --- a/examples/defork/forking_target.c +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include -#include - -/* This is an example target for defork.c - fuzz using -``` -mkdir in; echo a > ./in/a -AFL_PRELOAD=./defork64.so ../../afl-fuzz -i in -o out -- ./forking_target @@ -``` -*/ - -int main(int argc, char **argv) { - - if (argc < 2) { - - printf("Example tool to test defork.\nUsage ./forking_target \n"); - return -1; - - } - - pid_t pid = fork(); - if (pid == 0) { - - printf("We're in the child.\n"); - FILE *f = fopen(argv[1], "r"); - char buf[4096]; - fread(buf, 1, 4096, f); - fclose(f); - uint32_t offset = buf[100] + (buf[101] << 8); - char test_val = buf[offset]; - return test_val < 100; - - } else if (pid < 0) { - - perror("fork"); - return -1; - - } else { - - printf("We are in the parent - defork didn't work! :( (pid=%d)\n", - (int)pid); - - } - - return 0; - -} - diff --git a/examples/distributed_fuzzing/sync_script.sh b/examples/distributed_fuzzing/sync_script.sh deleted file mode 100755 index b28ff6cd..00000000 --- a/examples/distributed_fuzzing/sync_script.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - fuzzer synchronization tool -# -------------------------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2014 Google Inc. All rights reserved. -# Copyright 2019-2020 AFLplusplus Project. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# To make this script work: -# -# - Edit FUZZ_HOSTS, FUZZ_DOMAIN, FUZZ_USER, and SYNC_DIR to reflect your -# environment. -# -# - Make sure that the system you are running this on can log into FUZZ_HOSTS -# without a password (authorized_keys or otherwise). -# -# - Make sure that every fuzzer is running with -o pointing to SYNC_DIR and -S -# that consists of its local host name, followed by an underscore, and then -# by some host-local fuzzer ID. -# - -# Hosts to synchronize the data across. -FUZZ_HOSTS='host1 host2 host3 host4' - -# Domain for all hosts -FUZZ_DOMAIN='example.com' - -# Remote user for SSH -FUZZ_USER=bob - -# Directory to synchronize -SYNC_DIR='/home/bob/sync_dir' - -# We only capture -M main nodes, set the name to your chosen naming scheme -MAIN_NAME='main' - -# Interval (seconds) between sync attempts (eg one hour) -SYNC_INTERVAL=$((60 * 60)) - -if [ "$AFL_ALLOW_TMP" = "" ]; then - - if [ "$PWD" = "/tmp" -o "$PWD" = "/var/tmp" ]; then - echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 - exit 1 - fi - -fi - -rm -rf .sync_tmp 2>/dev/null -mkdir .sync_tmp || exit 1 - -while :; do - - # Pull data in... - - for host in $FUZZ_HOSTS; do - - echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ - "cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz" - - done - - # Distribute data. For large fleets, see tips in the docs/ directory. - - for dst_host in $FUZZ_HOSTS; do - - echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." - - for src_host in $FUZZ_HOSTS; do - - test "$src_host" = "$dst_host" && continue - - echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ - "cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz" - - done - - done - - echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." - - sleep $SYNC_INTERVAL - -done - diff --git a/examples/libpng_no_checksum/libpng-nocrc.patch b/examples/libpng_no_checksum/libpng-nocrc.patch deleted file mode 100644 index 0a3793a0..00000000 --- a/examples/libpng_no_checksum/libpng-nocrc.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 -+++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 -@@ -268,7 +268,11 @@ - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); -- return ((int)(crc != png_ptr->crc)); -+ -+ if (crc != png_ptr->crc) -+ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); -+ -+ return ((int)(1 != 1)); - } - - else diff --git a/examples/persistent_mode/Makefile b/examples/persistent_mode/Makefile deleted file mode 100644 index 6fa1c30e..00000000 --- a/examples/persistent_mode/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -all: - afl-clang-fast -o persistent_demo persistent_demo.c - afl-clang-fast -o persistent_demo_new persistent_demo_new.c - AFL_DONT_OPTIMIZE=1 afl-clang-fast -o test-instr test-instr.c - -document: - AFL_DONT_OPTIMIZE=1 afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c - -clean: - rm -f persistent_demo persistent_demo_new test-instr diff --git a/examples/persistent_mode/persistent_demo.c b/examples/persistent_mode/persistent_demo.c deleted file mode 100644 index 4cedc32c..00000000 --- a/examples/persistent_mode/persistent_demo.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - american fuzzy lop++ - persistent mode example - -------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file demonstrates the high-performance "persistent mode" that may be - suitable for fuzzing certain fast and well-behaved libraries, provided that - they are stateless or that their internal state can be easily reset - across runs. - - To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work). - - */ - -#include -#include -#include -#include -#include - -/* Main entry point. */ - -int main(int argc, char **argv) { - - ssize_t len; /* how much input did we read? */ - char buf[100]; /* Example-only buffer, you'd replace it with other global or - local variables appropriate for your use case. */ - - /* The number passed to __AFL_LOOP() controls the maximum number of - iterations before the loop exits and the program is allowed to - terminate normally. This limits the impact of accidental memory leaks - and similar hiccups. */ - - __AFL_INIT(); - while (__AFL_LOOP(1000)) { - - /*** PLACEHOLDER CODE ***/ - - /* STEP 1: Fully re-initialize all critical variables. In our example, this - involves zeroing buf[], our input buffer. */ - - memset(buf, 0, 100); - - /* STEP 2: Read input data. When reading from stdin, no special preparation - is required. When reading from a named file, you need to close - the old descriptor and reopen the file first! - - Beware of reading from buffered FILE* objects such as stdin. Use - raw file descriptors or call fopen() / fdopen() in every pass. */ - - len = read(0, buf, 100); - - /* STEP 3: This is where we'd call the tested library on the read data. - We just have some trivial inline code that faults on 'foo!'. */ - - /* do we have enough data? */ - if (len < 8) continue; - - if (buf[0] == 'f') { - - printf("one\n"); - if (buf[1] == 'o') { - - printf("two\n"); - if (buf[2] == 'o') { - - printf("three\n"); - if (buf[3] == '!') { - - printf("four\n"); - if (buf[4] == '!') { - - printf("five\n"); - if (buf[5] == '!') { - - printf("six\n"); - abort(); - - } - - } - - } - - } - - } - - } - - /*** END PLACEHOLDER CODE ***/ - - } - - /* Once the loop is exited, terminate normally - AFL will restart the process - when this happens, with a clean slate when it comes to allocated memory, - leftover file descriptors, etc. */ - - return 0; - -} - diff --git a/examples/persistent_mode/persistent_demo_new.c b/examples/persistent_mode/persistent_demo_new.c deleted file mode 100644 index a29792ff..00000000 --- a/examples/persistent_mode/persistent_demo_new.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - american fuzzy lop++ - persistent mode example - -------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file demonstrates the high-performance "persistent mode" that may be - suitable for fuzzing certain fast and well-behaved libraries, provided that - they are stateless or that their internal state can be easily reset - across runs. - - To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work). - - */ - -#include -#include -#include -#include -#include - -/* this lets the source compile without afl-clang-fast/lto */ -#ifndef __AFL_FUZZ_TESTCASE_LEN - -ssize_t fuzz_len; -unsigned char fuzz_buf[1024000]; - - #define __AFL_FUZZ_TESTCASE_LEN fuzz_len - #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf - #define __AFL_FUZZ_INIT() void sync(void); - #define __AFL_LOOP(x) \ - ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) - #define __AFL_INIT() sync() - -#endif - -__AFL_FUZZ_INIT(); - -/* Main entry point. */ - -int main(int argc, char **argv) { - - ssize_t len; /* how much input did we read? */ - unsigned char *buf; /* test case buffer pointer */ - - /* The number passed to __AFL_LOOP() controls the maximum number of - iterations before the loop exits and the program is allowed to - terminate normally. This limits the impact of accidental memory leaks - and similar hiccups. */ - - __AFL_INIT(); - buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP! - - while (__AFL_LOOP(1000)) { // increase if you have good stability - - len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! - - fprintf(stderr, "input: %zd \"%s\"\n", len, buf); - - /* do we have enough data? */ - if (len < 8) continue; - - if (strcmp((char *)buf, "thisisateststring") == 0) printf("teststring\n"); - - if (buf[0] == 'f') { - - printf("one\n"); - if (buf[1] == 'o') { - - printf("two\n"); - if (buf[2] == 'o') { - - printf("three\n"); - if (buf[3] == '!') { - - printf("four\n"); - if (buf[4] == '!') { - - printf("five\n"); - if (buf[6] == '!') { - - printf("six\n"); - abort(); - - } - - } - - } - - } - - } - - } - - /*** END PLACEHOLDER CODE ***/ - - } - - /* Once the loop is exited, terminate normally - AFL will restart the process - when this happens, with a clean slate when it comes to allocated memory, - leftover file descriptors, etc. */ - - return 0; - -} - diff --git a/examples/persistent_mode/test-instr.c b/examples/persistent_mode/test-instr.c deleted file mode 100644 index a6188b22..00000000 --- a/examples/persistent_mode/test-instr.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - american fuzzy lop++ - a trivial program to test the build - -------------------------------------------------------- - Originally written by Michal Zalewski - Copyright 2014 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - http://www.apache.org/licenses/LICENSE-2.0 - */ - -#include -#include -#include -#include -#include -#include -#include - -__AFL_FUZZ_INIT(); - -int main(int argc, char **argv) { - - __AFL_INIT(); - unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; - - while (__AFL_LOOP(2147483647)) { // MAX_INT if you have 100% stability - - unsigned int len = __AFL_FUZZ_TESTCASE_LEN; - -#ifdef _AFL_DOCUMENT_MUTATIONS - static unsigned int counter = 0; - char fn[32]; - sprintf(fn, "%09u:test-instr", counter); - int fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd_doc >= 0) { - - if (write(fd_doc, buf, len) != __afl_fuzz_len) { - - fprintf(stderr, "write of mutation file failed: %s\n", fn); - unlink(fn); - - } - - close(fd_doc); - - } - - counter++; -#endif - - // fprintf(stderr, "len: %u\n", len); - - if (!len) continue; - - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); - - } - - return 0; - -} - diff --git a/examples/qemu_persistent_hook/Makefile b/examples/qemu_persistent_hook/Makefile deleted file mode 100644 index 85db1b46..00000000 --- a/examples/qemu_persistent_hook/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -all: - $(CC) -no-pie test.c -o test - $(CC) -fPIC -shared read_into_rdi.c -o read_into_rdi.so - -clean: - rm -rf in out test read_into_rdi.so diff --git a/examples/qemu_persistent_hook/README.md b/examples/qemu_persistent_hook/README.md deleted file mode 100644 index 3f908c22..00000000 --- a/examples/qemu_persistent_hook/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# QEMU persistent hook example - -Compile the test binary and the library: - -``` -make -``` - -Fuzz with: - -``` -export AFL_QEMU_PERSISTENT_ADDR=0x$(nm test | grep "T target_func" | awk '{print $1}') -export AFL_QEMU_PERSISTENT_HOOK=./read_into_rdi.so - -mkdir in -echo 0000 > in/in - -../../afl-fuzz -Q -i in -o out -- ./test -``` diff --git a/examples/qemu_persistent_hook/read_into_rdi.c b/examples/qemu_persistent_hook/read_into_rdi.c deleted file mode 100644 index f4a8ae59..00000000 --- a/examples/qemu_persistent_hook/read_into_rdi.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "../../qemu_mode/qemuafl/qemuafl/api.h" - -#include -#include - -void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base, - uint8_t *input_buf, uint32_t input_buf_len) { -\ -#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) -#define h2g(x) ((uint64_t)(x)-guest_base) - - // In this example the register RDI is pointing to the memory location - // of the target buffer, and the length of the input is in RSI. - // This can be seen with a debugger, e.g. gdb (and "disass main") - - printf("Placing input into 0x%lx\n", regs->rdi); - - if (input_buf_len > 1024) input_buf_len = 1024; - memcpy(g2h(regs->rdi), input_buf, input_buf_len); - regs->rsi = input_buf_len; - -#undef g2h -#undef h2g - -} - -int afl_persistent_hook_init(void) { - - // 1 for shared memory input (faster), 0 for normal input (you have to use - // read(), input_buf will be NULL) - return 1; - -} - diff --git a/examples/qemu_persistent_hook/test.c b/examples/qemu_persistent_hook/test.c deleted file mode 100644 index afeff202..00000000 --- a/examples/qemu_persistent_hook/test.c +++ /dev/null @@ -1,35 +0,0 @@ -#include - -int target_func(unsigned char *buf, int size) { - - printf("buffer:%p, size:%p\n", buf, size); - switch (buf[0]) { - - case 1: - if (buf[1] == '\x44') { puts("a"); } - break; - case 0xff: - if (buf[2] == '\xff') { - - if (buf[1] == '\x44') { puts("b"); } - - } - - break; - default: - break; - - } - - return 1; - -} - -char data[1024]; - -int main() { - - target_func(data, 1024); - -} - diff --git a/examples/socket_fuzzing/Makefile b/examples/socket_fuzzing/Makefile deleted file mode 100644 index 9476e2d5..00000000 --- a/examples/socket_fuzzing/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# -# american fuzzy lop++ - socket_fuzz -# ---------------------------------- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) -UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? - -_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) -LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) -LDFLAGS += $(LDFLAGS_ADD) - -# on gcc for arm there is no -m32, but -mbe32 -M32FLAG = -m32 -M64FLAG = -m64 - -CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) -CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? -CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) -CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? - -_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) -__M32FLAG=$(_M32FLAG:00=-mbe32) -___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) -M32FLAG=$(___M32FLAG) -#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" -# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) -# M32FLAG = -mbe32 -# endif -#endif - -all: socketfuzz32.so socketfuzz64.so - -socketfuzz32.so: socketfuzz.c - -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz32 build failure (that's fine)" - -socketfuzz64.so: socketfuzz.c - -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz64 build failure (that's fine)" - -install: socketfuzz32.so socketfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - if [ -f socketfuzz64.so ]; then set -e; install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi - -clean: - rm -f socketfuzz32.so socketfuzz64.so diff --git a/examples/socket_fuzzing/README.md b/examples/socket_fuzzing/README.md deleted file mode 100644 index 79f28bea..00000000 --- a/examples/socket_fuzzing/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# socketfuzz - -when you want to fuzz a network service and you can not/do not want to modify -the source (or just have a binary), then this LD_PRELOAD library will allow -for sending input to stdin which the target binary will think is coming from -a network socket. - -This is desock_dup.c from the amazing preeny project -https://github.com/zardus/preeny - -It is packaged in afl++ to have it at hand if needed diff --git a/examples/socket_fuzzing/socketfuzz.c b/examples/socket_fuzzing/socketfuzz.c deleted file mode 100644 index 3ec8383b..00000000 --- a/examples/socket_fuzzing/socketfuzz.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This is desock_dup.c from the amazing preeny project - * https://github.com/zardus/preeny - * - * It is packaged in afl++ to have it at hand if needed - * - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include // -#include // -#include // -#include // -#include -#include -#include -#include -#include -#include -#include -//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " - -// -// originals -// -int (*original_close)(int); -int (*original_dup2)(int, int); -__attribute__((constructor)) void preeny_desock_dup_orig() { - - original_close = dlsym(RTLD_NEXT, "close"); - original_dup2 = dlsym(RTLD_NEXT, "dup2"); - -} - -int close(int sockfd) { - - if (sockfd <= 2) { - - fprintf(stderr, "Info: Disabling close on %d\n", sockfd); - return 0; - - } else { - - return original_close(sockfd); - - } - -} - -int dup2(int old, int new) { - - if (new <= 2) { - - fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); - return 0; - - } else { - - return original_dup2(old, new); - - } - -} - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); - return 0; - -} - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating bind on port %d\n", - ntohs(((struct sockaddr_in *)addr)->sin_port)); - return 0; - -} - -int listen(int sockfd, int backlog) { - - (void)sockfd; - (void)backlog; - return 0; - -} - -int setsockopt(int sockfd, int level, int optid, const void *optdata, - socklen_t optdatalen) { - - (void)sockfd; - (void)level; - (void)optid; - (void)optdata; - (void)optdatalen; - return 0; - -} - diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md index 6ccb5fd3..12449efd 100644 --- a/instrumentation/README.gcc_plugin.md +++ b/instrumentation/README.gcc_plugin.md @@ -147,7 +147,7 @@ The numerical value specified within the loop controls the maximum number of iterations before AFL will restart the process from scratch. This minimizes the impact of memory leaks and similar glitches; 1000 is a good starting point. -A more detailed template is shown in ../examples/persistent_mode/. +A more detailed template is shown in ../utils/persistent_mode/. Similarly to the previous mode, the feature works only with afl-gcc-fast or afl-clang-fast; #ifdef guards can be used to suppress it when using other compilers. diff --git a/instrumentation/README.persistent_mode.md b/instrumentation/README.persistent_mode.md index 49f5ee8b..2cf76adf 100644 --- a/instrumentation/README.persistent_mode.md +++ b/instrumentation/README.persistent_mode.md @@ -11,7 +11,7 @@ and that its state can be resetted so that multiple calls can be performed without resource leaks and former runs having no impact on following runs (this can be seen by the `stability` indicator in the `afl-fuzz` UI). -Examples can be found in [examples/persistent_mode](../examples/persistent_mode). +Examples can be found in [utils/persistent_mode](../utils/persistent_mode). ## 2) TLDR; @@ -150,7 +150,7 @@ the impact of memory leaks and similar glitches; 1000 is a good starting point, and going much higher increases the likelihood of hiccups without giving you any real performance benefits. -A more detailed template is shown in ../examples/persistent_mode/. +A more detailed template is shown in ../utils/persistent_mode/. Similarly to the previous mode, the feature works only with afl-clang-fast; #ifdef guards can be used to suppress it when using other compilers. diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 1c5d240c..58e48e91 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -179,7 +179,7 @@ match. ## 12) Gotchas, feedback, bugs If you need to fix up checksums or do other cleanup on mutated test cases, see -examples/custom_mutators/ for a viable solution. +utils/custom_mutators/ for a viable solution. Do not mix QEMU mode with ASAN, MSAN, or the likes; QEMU doesn't appreciate the "shadow VM" trick employed by the sanitizers and will probably just diff --git a/qemu_mode/README.persistent.md b/qemu_mode/README.persistent.md index d9e7e1cc..2ca5c873 100644 --- a/qemu_mode/README.persistent.md +++ b/qemu_mode/README.persistent.md @@ -172,4 +172,4 @@ and so the input_buf variables of the hook becomes meaningful. Otherwise, you have to read the input from a file like stdin. An example that you can use with little modification for your target can -be found here: [examples/qemu_persistent_hook](../examples/qemu_persistent_hook) +be found here: [utils/qemu_persistent_hook](../utils/qemu_persistent_hook) diff --git a/src/afl-as.c b/src/afl-as.c index 7d70bfcd..3d6f7d5e 100644 --- a/src/afl-as.c +++ b/src/afl-as.c @@ -27,7 +27,7 @@ utility has right now is to be able to skip them gracefully and allow the compilation process to continue. - That said, see examples/clang_asm_normalize/ for a solution that may + That said, see utils/clang_asm_normalize/ for a solution that may allow clang users to make things work even with hand-crafted assembly. Just note that there is no equivalent for GCC. diff --git a/test/test-custom-mutators.sh b/test/test-custom-mutators.sh index d4d21048..24c95ac7 100755 --- a/test/test-custom-mutators.sh +++ b/test/test-custom-mutators.sh @@ -5,7 +5,7 @@ $ECHO "$BLUE[*] Testing: custom mutator" test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { # normalize path - CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../examples/custom_mutators;pwd) + CUSTOM_MUTATOR_PATH=$(cd $(pwd)/../utils/custom_mutators;pwd) test -e test-custom-mutator.c -a -e ${CUSTOM_MUTATOR_PATH}/example.c -a -e ${CUSTOM_MUTATOR_PATH}/example.py && { unset AFL_CC # Compile the vulnerable program for single mutator @@ -29,8 +29,8 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { } } # Compile the custom mutator - cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 - cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../examples/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x41 -g -fPIC -shared -I../include ../utils/custom_mutators/simple_example.c -o libexamplemutator.so > /dev/null 2>&1 + cc -D_FIXED_CHAR=0x42 -g -fPIC -shared -I../include ../utils/custom_mutators/simple_example.c -o libexamplemutator2.so > /dev/null 2>&1 test -e test-custom-mutator -a -e ./libexamplemutator.so && { # Create input directory mkdir -p in @@ -109,7 +109,7 @@ test "1" = "`../afl-fuzz | grep -i 'without python' >/dev/null; echo $?`" && { #test "$CODE" = 1 && { $ECHO "$YELLOW[!] custom mutator tests currently will not fail travis" ; CODE=0 ; } - make -C ../examples/custom_mutators clean > /dev/null 2>&1 + make -C ../utils/custom_mutators clean > /dev/null 2>&1 rm -f test-custom-mutator rm -f test-custom-mutators } || { diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index af8674c9..71d86364 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -94,7 +94,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-gcc-fast -o test-persistent ../examples/persistent_mode/persistent_demo.c > /dev/null 2>&1 + ../afl-gcc-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index bdb08559..e10f4cf7 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -57,7 +57,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { CODE=1 } rm -f test-compcov test.out instrumentlist.txt - ../afl-clang-lto -o test-persistent ../examples/persistent_mode/persistent_mode.c > /dev/null 2>&1 + ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_mode.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 14778e1c..4fcaf367 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -209,7 +209,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { INCOMPLETE=1 } rm -rf errors test-cmplog in core.* - ../afl-clang-fast -o test-persistent ../examples/persistent_mode/persistent_demo.c > /dev/null 2>&1 + ../afl-clang-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 00000000..336b6b6c --- /dev/null +++ b/utils/README.md @@ -0,0 +1,54 @@ +# AFL++ Examples + +Here's a quick overview of the stuff you can find in this directory: + + - afl_network_proxy - fuzz a target over the network: afl-fuzz on + a host, target on an embedded system. + + - afl_proxy - skeleton file example to show how to fuzz + something where you gather coverage data via + different means, e.g. hw debugger + + - afl_untracer - fuzz binary-only libraries much faster but with + less coverage than qemu_mode + + - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed + (e.g., to test setuid programs). + + - asan_cgroups - a contributed script to simplify fuzzing ASAN + binaries with robust memory limits on Linux. + + - bash_shellshock - a simple hack used to find a bunch of + post-Shellshock bugs in bash. + + - canvas_harness - a test harness used to find browser bugs with a + corpus generated using simple image parsing + binaries & afl-fuzz. + + - clang_asm_normalize - a script that makes it easy to instrument + hand-written assembly, provided that you have clang. + + - crash_triage - a very rudimentary example of how to annotate crashes + with additional gdb metadata. + + - custom_mutators - examples for the afl++ custom mutator interface in + C and Python + + - distributed_fuzzing - a sample script for synchronizing fuzzer instances + across multiple machines (see parallel_fuzzing.md). + + - libpng_no_checksum - a sample patch for removing CRC checks in libpng. + + - persistent_mode - an example of how to use the LLVM persistent process + mode to speed up certain fuzzing jobs. + + - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin + for fuzzing access with afl++ + +Note that the minimize_corpus.sh tool has graduated from the utils/ +directory and is now available as ../afl-cmin. The LLVM mode has likewise +graduated to ../instrumentation/*. + +Most of the tools in this directory are meant chiefly as examples that need to +be tweaked for your specific needs. They come with some basic documentation, +but are not necessarily production-grade. diff --git a/utils/afl_frida/GNUmakefile b/utils/afl_frida/GNUmakefile new file mode 100644 index 00000000..c154f3a4 --- /dev/null +++ b/utils/afl_frida/GNUmakefile @@ -0,0 +1,23 @@ +ifdef DEBUG + OPT=-O0 -D_DEBUG=\"1\" +else + OPT=-O3 -funroll-loops +endif + +all: afl-frida libtestinstr.so + +libfrida-gum.a: + @echo Download and extract frida-gum-devkit-VERSION-PLATFORM.tar.xz for your platform from https://github.com/frida/frida/releases/latest + @exit 1 + +afl-frida: afl-frida.c libfrida-gum.a + $(CC) -g $(OPT) -o afl-frida -Wno-format -Wno-pointer-sign -I. -fpermissive -fPIC afl-frida.c ../../afl-llvm-rt.o libfrida-gum.a -ldl -lresolv -pthread + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-frida *~ core *.o libtestinstr.so + +deepclean: clean + rm -f libfrida-gum.a frida-gum* diff --git a/utils/afl_frida/Makefile b/utils/afl_frida/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_frida/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_frida/README.md b/utils/afl_frida/README.md new file mode 100644 index 00000000..7743479b --- /dev/null +++ b/utils/afl_frida/README.md @@ -0,0 +1,34 @@ +# afl-frida - faster fuzzing of binary-only libraries + +## Introduction + +afl-frida is an example skeleton file which can easily be used to fuzz +a closed source library. + +It requires less memory and is x5-10 faster than qemu_mode but does not +provide interesting features like compcov or cmplog. + +## How-to + +### Modify afl-frida.c + +Read and modify afl-frida.c then `make`. +To adapt afl-frida.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Fuzzing + +Example (after modifying afl-frida.c to your needs and compile it): +``` +LD_LIBRARY_PATH=/path/to/the/target/library afl-fuzz -i in -o out -- ./afl-frida +``` +(or even remote via afl-network-proxy). + +# Speed and stability + +The speed is very good, about x12 of fork() qemu_mode. +However the stability is low. Reason is currently unknown. + +# Background + +This code is copied for a larger part from https://github.com/meme/hotwax diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c new file mode 100644 index 00000000..31bf8f25 --- /dev/null +++ b/utils/afl_frida/afl-frida.c @@ -0,0 +1,542 @@ +/* + american fuzzy lop++ - afl-frida skeleton example + ------------------------------------------------- + + Copyright 2020 AFLplusplus Project. All rights reserved. + + Written mostly by meme -> https://github.com/meme/hotwax + + Modifications by Marc Heuse + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + HOW-TO + ====== + + You only need to change the following: + + 1. set the defines and function call parameters. + 2. dl load the library you want to fuzz, lookup the functions you need + and setup the calls to these. + 3. in the while loop you call the functions in the necessary order - + incl the cleanup. the cleanup is important! + + Just look these steps up in the code, look for "// STEP x:" + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ + #include + #include +#endif + +int debug = 0; + +// STEP 1: + +// The presets are for the example libtestinstr.so: + +/* What is the name of the library to fuzz */ +#define TARGET_LIBRARY "libtestinstr.so" + +/* What is the name of the function to fuzz */ +#define TARGET_FUNCTION "testinstr" + +/* here you need to specify the parameter for the target function */ +static void *(*o_function)(uint8_t *, int); + +// END STEP 1 + +#include "frida-gum.h" + +G_BEGIN_DECLS + +#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) +G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, + FAKE_EVENT_SINK, GObject) + +struct _GumFakeEventSink { + + GObject parent; + GumEventType mask; + +}; + +GumEventSink *gum_fake_event_sink_new(void); +void gum_fake_event_sink_reset(GumFakeEventSink *self); + +G_END_DECLS + +static void gum_fake_event_sink_iface_init(gpointer g_iface, + gpointer iface_data); +static void gum_fake_event_sink_finalize(GObject *obj); +static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink); +static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev); +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) { + + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = gum_fake_event_sink_finalize; + +} + +static void gum_fake_event_sink_iface_init(gpointer g_iface, + gpointer iface_data) { + + GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface; + iface->query_mask = gum_fake_event_sink_query_mask; + iface->process = gum_fake_event_sink_process; + +} + +G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK, + gum_fake_event_sink_iface_init)) + +#include "../../config.h" + +// Shared memory fuzzing. +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +int __afl_persistent_loop(unsigned int); + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +void __afl_manual_init(); + +// Because we do our own logging. +extern uint8_t * __afl_area_ptr; +static __thread guint64 previous_pc; + +// Frida stuff below. +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + +} range_t; + +inline static void afl_maybe_log(guint64 current_pc) { + + // fprintf(stderr, "PC: %p ^ %p\n", current_pc, previous_pc); + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + __afl_area_ptr[current_pc ^ previous_pc]++; + previous_pc = current_pc >> 1; + +} + +static void on_basic_block(GumCpuContext *context, gpointer user_data) { + + afl_maybe_log((guint64)user_data); + +} + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data) { + + range_t *range = (range_t *)user_data; + + const cs_insn *instr; + gboolean begin = TRUE; + while (gum_stalker_iterator_next(iterator, &instr)) { + + if (begin) { + + if (instr->address >= range->code_start && + instr->address <= range->code_end) { + + gum_stalker_iterator_put_callout(iterator, on_basic_block, + (gpointer)instr->address, NULL); + begin = FALSE; + + } + + } + + gum_stalker_iterator_keep(iterator); + + } + +} + +static void gum_fake_event_sink_init(GumFakeEventSink *self) { + +} + +static void gum_fake_event_sink_finalize(GObject *obj) { + + G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj); + +} + +GumEventSink *gum_fake_event_sink_new(void) { + + GumFakeEventSink *sink; + sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL); + return GUM_EVENT_SINK(sink); + +} + +void gum_fake_event_sink_reset(GumFakeEventSink *self) { + +} + +static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) { + + return 0; + +} + +typedef struct library_list { + + uint8_t *name; + uint64_t addr_start, addr_end; + +} library_list_t; + +#define MAX_LIB_COUNT 256 +static library_list_t liblist[MAX_LIB_COUNT]; +static u32 liblist_cnt; + +void read_library_information() { + +#if defined(__linux__) + FILE *f; + u8 buf[1024], *b, *m, *e, *n; + + if ((f = fopen("/proc/self/maps", "r")) == NULL) { + + fprintf(stderr, "Error: cannot open /proc/self/maps\n"); + exit(-1); + + } + + if (debug) fprintf(stderr, "Library list:\n"); + while (fgets(buf, sizeof(buf), f)) { + + if (strstr(buf, " r-x")) { + + if (liblist_cnt >= MAX_LIB_COUNT) { + + fprintf( + stderr, + "Warning: too many libraries to old, maximum count of %d reached\n", + liblist_cnt); + return; + + } + + b = buf; + m = index(buf, '-'); + e = index(buf, ' '); + if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); + if (n && + ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) + n = NULL; + else + n++; + if (b && m && e && n && *n) { + + *m++ = 0; + *e = 0; + if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; + + if (rindex(n, '/') != NULL) { + + n = rindex(n, '/'); + n++; + + } + + liblist[liblist_cnt].name = strdup(n); + liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); + liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); + if (debug) + fprintf( + stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + liblist_cnt++; + + } + + } + + } + + if (debug) fprintf(stderr, "\n"); + +#elif defined(__FreeBSD__) + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + char * buf, *start, *end; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + size_t len; + + if (debug) fprintf(stderr, "Library list:\n"); + if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } + + len = len * 4 / 3; + + buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) { return; } + if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { + + munmap(buf, len); + return; + + } + + start = buf; + end = buf + len; + + while (start < end) { + + struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; + size_t size = region->kve_structsize; + + if (size == 0) { break; } + + if ((region->kve_protection & KVME_PROT_READ) && + !(region->kve_protection & KVME_PROT_EXEC)) { + + liblist[liblist_cnt].name = + region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; + liblist[liblist_cnt].addr_start = region->kve_start; + liblist[liblist_cnt].addr_end = region->kve_end; + + if (debug) { + + fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + + } + + liblist_cnt++; + + } + + start += size; + + } + +#endif + +} + +library_list_t *find_library(char *name) { + + char *filename = rindex(name, '/'); + + if (filename) + filename++; + else + filename = name; + +#if defined(__linux__) + u32 i; + for (i = 0; i < liblist_cnt; i++) + if (strcmp(liblist[i].name, filename) == 0) return &liblist[i]; +#elif defined(__APPLE__) && defined(__LP64__) + kern_return_t err; + static library_list_t lib; + + // get the list of all loaded modules from dyld + // the task_info mach API will get the address of the dyld all_image_info + // struct for the given task from which we can get the names and load + // addresses of all modules + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + err = task_info(mach_task_self(), TASK_DYLD_INFO, + (task_info_t)&task_dyld_info, &count); + + const struct dyld_all_image_infos *all_image_infos = + (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; + const struct dyld_image_info *image_infos = all_image_infos->infoArray; + + for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { + + const char * image_name = image_infos[i].imageFilePath; + mach_vm_address_t image_load_address = + (mach_vm_address_t)image_infos[i].imageLoadAddress; + if (strstr(image_name, name)) { + + lib.name = name; + lib.addr_start = (u64)image_load_address; + lib.addr_end = 0; + return &lib; + + } + + } + +#endif + + return NULL; + +} + +static void gum_fake_event_sink_process(GumEventSink * sink, + const GumEvent *ev) { + +} + +/* Because this CAN be called more than once, it will return the LAST range */ +static int enumerate_ranges(const GumRangeDetails *details, + gpointer user_data) { + + GumMemoryRange *code_range = (GumMemoryRange *)user_data; + memcpy(code_range, details->range, sizeof(*code_range)); + return 0; + +} + +int main() { + +#ifndef __APPLE__ + (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR +#endif + + // STEP 2: load the library you want to fuzz and lookup the functions, + // inclusive of the cleanup functions. + // If there is just one function, then there is nothing to change + // or add here. + + void *dl = dlopen(TARGET_LIBRARY, RTLD_LAZY); + if (!dl) { + + fprintf(stderr, "Could not load %s\n", TARGET_LIBRARY); + exit(-1); + + } + + if (!(o_function = dlsym(dl, TARGET_FUNCTION))) { + + fprintf(stderr, "Could not find function %s\n", TARGET_FUNCTION); + exit(-1); + + } + + // END STEP 2 + + read_library_information(); + library_list_t *lib = find_library(TARGET_LIBRARY); + + if (lib == NULL) { + + fprintf(stderr, "Could not find target library\n"); + exit(-1); + + } + + gum_init_embedded(); + if (!gum_stalker_is_supported()) { + + gum_deinit_embedded(); + return 1; + + } + + GumStalker *stalker = gum_stalker_new(); + + /* + This does not work here as we load a shared library. pretty sure this + would also be easily solvable with frida gum, but I already have all the + code I need from afl-untracer + + GumAddress base_address = gum_module_find_base_address(TARGET_LIBRARY); + GumMemoryRange code_range; + gum_module_enumerate_ranges(TARGET_LIBRARY, GUM_PAGE_RX, enumerate_ranges, + &code_range); + guint64 code_start = code_range.base_address - base_address; + guint64 code_end = (code_range.base_address + code_range.size) - base_address; + range_t instr_range = {base_address, code_start, code_end}; + */ + range_t instr_range = {0, lib->addr_start, lib->addr_end}; + + GumStalkerTransformer *transformer = + gum_stalker_transformer_make_from_callback(instr_basic_block, + &instr_range, NULL); + + GumEventSink *event_sink = gum_fake_event_sink_new(); + + // to ensure that the signatures are not optimized out + memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); + memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR) + 1); + __afl_manual_init(); + + // + // any expensive target library initialization that has to be done just once + // - put that here + // + + gum_stalker_follow_me(stalker, transformer, event_sink); + + while (__afl_persistent_loop(UINT32_MAX) != 0) { + + previous_pc = 0; // Required! + +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len), *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + + // STEP 3: ensure the minimum length is present and setup the target + // function to fuzz. + + if (*__afl_fuzz_len > 0) { + + __afl_fuzz_ptr[*__afl_fuzz_len] = 0; // if you need to null terminate + (*o_function)(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + + // END STEP 3 + + } + + gum_stalker_unfollow_me(stalker); + + while (gum_stalker_garbage_collect(stalker)) + g_usleep(10000); + + g_object_unref(stalker); + g_object_unref(transformer); + g_object_unref(event_sink); + gum_deinit_embedded(); + + return 0; + +} + diff --git a/utils/afl_frida/afl-frida.h b/utils/afl_frida/afl-frida.h new file mode 100644 index 00000000..efa3440f --- /dev/null +++ b/utils/afl_frida/afl-frida.h @@ -0,0 +1,53 @@ +extern int is_persistent; + +G_BEGIN_DECLS + +#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) + +G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, + FAKE_EVENT_SINK, GObject) + +struct _GumFakeEventSink { + + GObject parent; + GumEventType mask; + +}; + +GumEventSink *gum_fake_event_sink_new(void); +void gum_fake_event_sink_reset(GumFakeEventSink *self); + +G_END_DECLS + +typedef struct { + + GumAddress base_address; + guint64 code_start, code_end; + +} range_t; + +void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, + gpointer user_data); +#pragma once + +void afl_setup(void); +void afl_start_forkserver(void); +int __afl_persistent_loop(unsigned int max_cnt); + +inline static inline void afl_maybe_log(guint64 current_pc) { + + extern unsigned int afl_instr_rms; + extern uint8_t * afl_area_ptr; + + static __thread guint64 previous_pc; + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + if (current_pc >= afl_instr_rms) return; + + afl_area_ptr[current_pc ^ previous_pc]++; + previous_pc = current_pc >> 1; + +} + diff --git a/utils/afl_frida/libtestinstr.c b/utils/afl_frida/libtestinstr.c new file mode 100644 index 00000000..96b1cf21 --- /dev/null +++ b/utils/afl_frida/libtestinstr.c @@ -0,0 +1,35 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +void testinstr(char *buf, int len) { + + if (len < 1) return; + buf[len] = 0; + + // we support three input cases + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile new file mode 100644 index 00000000..25a3df82 --- /dev/null +++ b/utils/afl_network_proxy/GNUmakefile @@ -0,0 +1,43 @@ +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +DOC_PATH = $(PREFIX)/share/doc/afl + +PROGRAMS = afl-network-client afl-network-server + +HASH=\# + +CFLAGS += -Wno-pointer-sign + +ifdef STATIC + CFLAGS += -static +endif + +ifeq "$(shell echo '$(HASH)include @int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" + CFLAGS += -DUSE_DEFLATE=1 + LDFLAGS += -ldeflate + $(info libdeflate-dev was detected, using compression) +else + $(warn did not find libdeflate-dev, cannot use compression) +endif + +all: $(PROGRAMS) + +help: + @echo make options: + @echo STATIC - build as static binaries + @echo COMPRESS_TESTCASES - compress test cases + +afl-network-client: afl-network-client.c + $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) + +afl-network-server: afl-network-server.c + $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) + +clean: + rm -f $(PROGRAMS) *~ core + +install: all + install -d -m 755 $${DESTDIR}$(BIN_PATH) $${DESTDIR}$(DOC_PATH) + install -m 755 $(PROGRAMS) $${DESTDIR}$(BIN_PATH) + install -T -m 644 README.md $${DESTDIR}$(DOC_PATH)/README.network_proxy.md + diff --git a/utils/afl_network_proxy/Makefile b/utils/afl_network_proxy/Makefile new file mode 100644 index 00000000..0b306dde --- /dev/null +++ b/utils/afl_network_proxy/Makefile @@ -0,0 +1,2 @@ +all: + @echo please use GNU make, thanks! diff --git a/utils/afl_network_proxy/README.md b/utils/afl_network_proxy/README.md new file mode 100644 index 00000000..a5ac3578 --- /dev/null +++ b/utils/afl_network_proxy/README.md @@ -0,0 +1,61 @@ +# afl-network-proxy + +If you want to run afl-fuzz over the network than this is what you need :) +Note that the impact on fuzzing speed will be huge, expect a loss of 90%. + +## When to use this + +1. when you have to fuzz a target that has to run on a system that cannot + contain the fuzzing output (e.g. /tmp too small and file system is read-only) +2. when the target instantly reboots on crashes +3. ... any other reason you would need this + +## how to get it running + +### Compiling + +Just type `make` and let the autodetection do everything for you. + +Note that you will get a 40-50% performance increase if you have libdeflate-dev +installed. The GNUmakefile will autodetect it if present. + +If your target has large test cases (10+kb) that are ascii only or large chunks +of zero blocks then set `CFLAGS=-DCOMPRESS_TESTCASES=1` to compress them. +For most targets this hurts performance though so it is disabled by default. + +### on the target + +Run `afl-network-server` with your target with the -m and -t values you need. +Important is the -i parameter which is the TCP port to listen on. +e.g.: +``` +afl-network-server -i 1111 -m 25M -t 1000 -- /bin/target -f @@ +``` + +### on the (afl-fuzz) master + +Just run afl-fuzz with your normal options, however the target should be +`afl-network-client` with the IP and PORT of the `afl-network-server` and +increase the -t value: +``` +afl-fuzz -i in -o out -t 2000+ -- afl-network-client TARGET-IP 1111 +``` +Note the '+' on the -t parameter value. The afl-network-server will take +care of proper timeouts hence afl-fuzz should not. The '+' increases the +timeout and the value itself should be 500-1000 higher than the one on +afl-network-server. + +### networking + +The TARGET can be an IPv4 or IPv6 address, or a host name that resolves to +either. Note that also the outgoing interface can be specified with a '%' for +`afl-network-client`, e.g. `fe80::1234%eth0`. + +Also make sure your default TCP window size is larger than your MAP_SIZE +(130kb is a good value). +On Linux that is the middle value of `/proc/sys/net/ipv4/tcp_rmem` + +## how to compile and install + +`make && sudo make install` + diff --git a/utils/afl_network_proxy/afl-network-client.c b/utils/afl_network_proxy/afl-network-client.c new file mode 100644 index 00000000..a2451fdc --- /dev/null +++ b/utils/afl_network_proxy/afl-network-client.c @@ -0,0 +1,415 @@ +/* + american fuzzy lop++ - afl-network-client + --------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifndef USEMMAP + #include +#endif +#include +#include +#include +#include +#include + +#ifdef USE_DEFLATE + #include +#endif + +u8 *__afl_area_ptr; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +#else +__thread u32 __afl_map_size = MAP_SIZE; +#endif + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + +} + +static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status, res = 0x0fffffff; // res is a dummy pid + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) return 0; + + /* we have a testcase - read it */ + status = read(0, buf, max_len); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; + + if (status < 1) + return 0; + else + return status; + +} + +static void __afl_end_testcase(int status) { + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); + +} + +/* you just need to modify the while() loop in this main() */ + +int main(int argc, char *argv[]) { + + u8 * interface, *buf, *ptr; + s32 s = -1; + struct addrinfo hints, *hres, *aip; + u32 * lenptr, max_len = 65536; +#ifdef USE_DEFLATE + u8 * buf2; + u32 * lenptr1, *lenptr2, buf2_len, compress_len; + size_t decompress_len; +#endif + + if (argc < 3 || argc > 4) { + + printf("Syntax: %s host port [max-input-size]\n\n", argv[0]); + printf("Requires host and port of the remote afl-proxy-server instance.\n"); + printf( + "IPv4 and IPv6 are supported, also binding to an interface with " + "\"%%\"\n"); + printf("The max-input-size default is %u.\n", max_len); + printf( + "The default map size is %u and can be changed with setting " + "AFL_MAP_SIZE.\n", + __afl_map_size); + exit(-1); + + } + + if ((interface = strchr(argv[1], '%')) != NULL) *interface++ = 0; + + if (argc > 3) + if ((max_len = atoi(argv[3])) < 0) + FATAL("max-input-size may not be negative or larger than 2GB: %s", + argv[3]); + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) + if ((__afl_map_size = atoi(ptr)) < 8) + FATAL("illegal map size, may not be < 8 or >= 2^30: %s", ptr); + + if ((buf = malloc(max_len + 4)) == NULL) + PFATAL("can not allocate %u memory", max_len + 4); + lenptr = (u32 *)buf; + +#ifdef USE_DEFLATE + buf2_len = (max_len > __afl_map_size ? max_len : __afl_map_size); + if ((buf2 = malloc(buf2_len + 8)) == NULL) + PFATAL("can not allocate %u memory", buf2_len + 8); + lenptr1 = (u32 *)buf2; + lenptr2 = (u32 *)(buf2 + 4); +#endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + if (getaddrinfo(argv[1], argv[2], &hints, &hres) != 0) + PFATAL("could not resolve target %s", argv[1]); + + for (aip = hres; aip != NULL && s == -1; aip = aip->ai_next) { + + if ((s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) >= 0) { + +#ifdef SO_BINDTODEVICE + if (interface != NULL) + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, interface, + strlen(interface) + 1) < 0) + fprintf(stderr, "Warning: could not bind to device %s\n", interface); +#else + fprintf(stderr, + "Warning: binding to interface is not supported for your OS\n"); +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, + sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + if (connect(s, aip->ai_addr, aip->ai_addrlen) == -1) s = -1; + + } + + } + +#ifdef USE_DEFLATE + struct libdeflate_compressor *compressor; + compressor = libdeflate_alloc_compressor(1); + struct libdeflate_decompressor *decompressor; + decompressor = libdeflate_alloc_decompressor(); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + if (s == -1) + FATAL("could not connect to target tcp://%s:%s", argv[1], argv[2]); + else + fprintf(stderr, "Connected to target tcp://%s:%s\n", argv[1], argv[2]); + + /* we initialize the shared memory map and start the forkserver */ + __afl_map_shm(); + __afl_start_forkserver(); + + int i = 1, j, status, ret, received; + + // fprintf(stderr, "Waiting for first testcase\n"); + while ((*lenptr = __afl_next_testcase(buf + 4, max_len)) > 0) { + + // fprintf(stderr, "Sending testcase with len %u\n", *lenptr); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // we only compress the testcase if it does not fit in the TCP packet + if (*lenptr > 1500 - 20 - 32 - 4) { + + // set highest byte to signify compression + *lenptr1 = (*lenptr | 0xff000000); + *lenptr2 = (u32)libdeflate_deflate_compress(compressor, buf + 4, *lenptr, + buf2 + 8, buf2_len); + if (send(s, buf2, *lenptr2 + 8, 0) != *lenptr2 + 8) + PFATAL("sending test data failed"); + // fprintf(stderr, "COMPRESS (%u->%u):\n", *lenptr, *lenptr2); + // for (u32 i = 0; i < *lenptr; i++) + // fprintf(stderr, "%02x", buf[i + 4]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < *lenptr2; i++) + // fprintf(stderr, "%02x", buf2[i + 8]); + // fprintf(stderr, "\n"); + + } else { + + #endif +#endif + if (send(s, buf, *lenptr + 4, 0) != *lenptr + 4) + PFATAL("sending test data failed"); +#ifdef USE_DEFLATE + #ifdef COMPRESS_TESTCASES + // fprintf(stderr, "unCOMPRESS (%u)\n", *lenptr); + + } + + #endif +#endif + + received = 0; + while (received < 4 && + (ret = recv(s, &status + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive waitpid data (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; +#ifdef USE_DEFLATE + while (received < 4 && + (ret = recv(s, &compress_len + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) + FATAL("did not receive compress_len (%d, %d)", received, ret); + // fprintf(stderr, "Received status\n"); + + received = 0; + while (received < compress_len && + (ret = recv(s, buf2 + received, buf2_len - received, 0)) > 0) + received += ret; + if (received != compress_len) + FATAL("did not receive coverage data (%d, %d)", received, ret); + + if (libdeflate_deflate_decompress(decompressor, buf2, compress_len, + __afl_area_ptr, __afl_map_size, + &decompress_len) != LIBDEFLATE_SUCCESS || + decompress_len != __afl_map_size) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u): ", compress_len, decompress_len); + // for (u32 i = 0; i < __afl_map_size; i++) fprintf(stderr, "%02x", + // __afl_area_ptr[i]); fprintf(stderr, "\n"); +#else + while (received < __afl_map_size && + (ret = recv(s, __afl_area_ptr + received, __afl_map_size - received, + 0)) > 0) + received += ret; + if (received != __afl_map_size) + FATAL("did not receive coverage data (%d, %d)", received, ret); +#endif + // fprintf(stderr, "Received coverage\n"); + + /* report the test case is done and wait for the next */ + __afl_end_testcase(status); + // fprintf(stderr, "Waiting for next testcase %d\n", ++i); + + } + +#ifdef USE_DEFLATE + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + return 0; + +} + diff --git a/utils/afl_network_proxy/afl-network-server.c b/utils/afl_network_proxy/afl-network-server.c new file mode 100644 index 00000000..513dc8f2 --- /dev/null +++ b/utils/afl_network_proxy/afl-network-server.c @@ -0,0 +1,720 @@ +/* + american fuzzy lop++ - network proxy server + ------------------------------------------- + + Originally written by Michal Zalewski + + Forkserver design by Jann Horn + + Now maintained by Marc Heuse , + Heiko Eißfeldt and + Andrea Fioraldi and + Dominik Maier + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "hash.h" +#include "forkserver.h" +#include "sharedmem.h" +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_DEFLATE + #include +struct libdeflate_compressor * compressor; +struct libdeflate_decompressor *decompressor; +#endif + +static u8 *in_file, /* Minimizer input test case */ + *out_file; + +static u8 *in_data; /* Input data for trimming */ +static u8 *buf2; + +static s32 in_len; +static s32 buf2_len; +static u32 map_size = MAP_SIZE; + +static volatile u8 stop_soon; /* Ctrl-C pressed? */ + +/* See if any bytes are set in the bitmap. */ + +static inline u8 anything_set(afl_forkserver_t *fsrv) { + + u32 *ptr = (u32 *)fsrv->trace_bits; + u32 i = (map_size >> 2); + + while (i--) { + + if (*(ptr++)) { return 1; } + + } + + return 0; + +} + +static void at_exit_handler(void) { + + afl_fsrv_killall(); + +} + +/* Write output file. */ + +static s32 write_to_file(u8 *path, u8 *mem, u32 len) { + + s32 ret; + + unlink(path); /* Ignore errors */ + + ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (ret < 0) { PFATAL("Unable to create '%s'", path); } + + ck_write(ret, mem, len, path); + + lseek(ret, 0, SEEK_SET); + + return ret; + +} + +/* Execute target application. Returns 0 if the changes are a dud, or + 1 if they should be kept. */ + +static u8 run_target(afl_forkserver_t *fsrv, char **argv, u8 *mem, u32 len, + u8 first_run) { + + afl_fsrv_write_to_testcase(fsrv, mem, len); + + fsrv_run_result_t ret = + afl_fsrv_run_target(fsrv, fsrv->exec_tmout, &stop_soon); + + if (ret == FSRV_RUN_ERROR) { FATAL("Couldn't run child"); } + + if (stop_soon) { + + SAYF(cRST cLRD "\n+++ aborted by user +++\n" cRST); + exit(1); + + } + + return ret; + +} + +/* Handle Ctrl-C and the like. */ + +static void handle_stop_sig(int sig) { + + stop_soon = 1; + afl_fsrv_killall(); + +} + +/* Do basic preparations - persistent fds, filenames, etc. */ + +static void set_up_environment(afl_forkserver_t *fsrv) { + + u8 *x; + + fsrv->dev_null_fd = open("/dev/null", O_RDWR); + if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); } + + if (!out_file) { + + u8 *use_dir = "."; + + if (access(use_dir, R_OK | W_OK | X_OK)) { + + use_dir = get_afl_env("TMPDIR"); + if (!use_dir) { use_dir = "/tmp"; } + + } + + out_file = alloc_printf("%s/.afl-input-temp-%u", use_dir, getpid()); + + } + + unlink(out_file); + + fsrv->out_fd = open(out_file, O_RDWR | O_CREAT | O_EXCL, 0600); + + if (fsrv->out_fd < 0) { PFATAL("Unable to create '%s'", out_file); } + + /* Set sane defaults... */ + + x = get_afl_env("ASAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "abort_on_error=1")) { + + FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + x = get_afl_env("MSAN_OPTIONS"); + + if (x) { + + if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) { + + FATAL("Custom MSAN_OPTIONS set without exit_code=" STRINGIFY( + MSAN_ERROR) " - please fix!"); + + } + + if (!strstr(x, "symbolize=0")) { + + FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); + + } + + } + + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); + + setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" + "symbolize=0:" + "abort_on_error=1:" + "allocator_may_return_null=1:" + "msan_track_origins=0", 0); + + if (get_afl_env("AFL_PRELOAD")) { + + if (fsrv->qemu_mode) { + + u8 *qemu_preload = getenv("QEMU_SET_ENV"); + u8 *afl_preload = getenv("AFL_PRELOAD"); + u8 *buf; + + s32 i, afl_preload_size = strlen(afl_preload); + for (i = 0; i < afl_preload_size; ++i) { + + if (afl_preload[i] == ',') { + + PFATAL( + "Comma (',') is not allowed in AFL_PRELOAD when -Q is " + "specified!"); + + } + + } + + if (qemu_preload) { + + buf = alloc_printf("%s,LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + qemu_preload, afl_preload, afl_preload); + + } else { + + buf = alloc_printf("LD_PRELOAD=%s,DYLD_INSERT_LIBRARIES=%s", + afl_preload, afl_preload); + + } + + setenv("QEMU_SET_ENV", buf, 1); + + afl_free(buf); + + } else { + + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); + setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + + } + + } + +} + +/* Setup signal handlers, duh. */ + +static void setup_signal_handlers(void) { + + struct sigaction sa; + + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; + sa.sa_sigaction = NULL; + + sigemptyset(&sa.sa_mask); + + /* Various ways of saying "stop". */ + + sa.sa_handler = handle_stop_sig; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + +} + +/* Display usage hints. */ + +static void usage(u8 *argv0) { + + SAYF( + "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + + "Required parameters:\n" + + " -i port - the port to listen for the client to connect to\n\n" + + "Execution control settings:\n" + + " -f file - input file read by the tested program (stdin)\n" + " -t msec - timeout for each run (%d ms)\n" + " -m megs - memory limit for child process (%d MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n" + " -U - use unicorn-based instrumentation (Unicorn mode)\n" + " -W - use qemu-based instrumentation with Wine (Wine " + "mode)\n\n" + + "Environment variables used:\n" + "TMPDIR: directory to use for temporary input files\n" + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" + " the target was compiled for\n" + "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" + + , argv0, EXEC_TIMEOUT, MEM_LIMIT); + + exit(1); + +} + +int recv_testcase(int s, void **buf) { + + u32 size; + s32 ret; + size_t received; + + received = 0; + while (received < 4 && (ret = recv(s, &size + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive size information"); + if (size == 0) FATAL("did not receive valid size information"); + // fprintf(stderr, "received size information of %d\n", size); + + if ((size & 0xff000000) != 0xff000000) { + + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + // fprintf(stderr, "unCOMPRESS (%u)\n", size); + while (received < size && + (ret = recv(s, ((char *)*buf) + received, size - received, 0)) > 0) + received += ret; + + } else { + +#ifdef USE_DEFLATE + u32 clen; + size -= 0xff000000; + *buf = afl_realloc(buf, size); + if (unlikely(!*buf)) { PFATAL("Alloc"); } + received = 0; + while (received < 4 && + (ret = recv(s, &clen + received, 4 - received, 0)) > 0) + received += ret; + if (received != 4) FATAL("did not receive clen1 information"); + // fprintf(stderr, "received clen information of %d\n", clen); + if (clen < 1) + FATAL("did not receive valid compressed len information: %u", clen); + buf2 = afl_realloc((void **)&buf2, clen); + buf2_len = clen; + if (unlikely(!buf2)) { PFATAL("Alloc"); } + received = 0; + while (received < clen && + (ret = recv(s, buf2 + received, clen - received, 0)) > 0) + received += ret; + if (received != clen) FATAL("did not receive compressed information"); + if (libdeflate_deflate_decompress(decompressor, buf2, clen, (char *)*buf, + size, &received) != LIBDEFLATE_SUCCESS) + FATAL("decompression failed"); + // fprintf(stderr, "DECOMPRESS (%u->%u):\n", clen, received); + // for (u32 i = 0; i < clen; i++) fprintf(stderr, "%02x", buf2[i]); + // fprintf(stderr, "\n"); + // for (u32 i = 0; i < received; i++) fprintf(stderr, "%02x", + // ((u8*)(*buf))[i]); fprintf(stderr, "\n"); +#else + FATAL("Received compressed data but not compiled with compression support"); +#endif + + } + + // fprintf(stderr, "receiving testcase %p %p max %u\n", buf, *buf, *max_len); + if (received != size) + FATAL("did not receive testcase data %lu != %u, %d", received, size, ret); + // fprintf(stderr, "received testcase\n"); + return size; + +} + +/* Main entry point */ + +int main(int argc, char **argv_orig, char **envp) { + + s32 opt, s, sock, on = 1, port = -1; + u8 mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0; + char **use_argv; + struct sockaddr_in6 serveraddr, clientaddr; + int addrlen = sizeof(clientaddr); + char str[INET6_ADDRSTRLEN]; + char ** argv = argv_cpy_dup(argc, argv_orig); + u8 * send_buf; +#ifdef USE_DEFLATE + u32 *lenptr; +#endif + + afl_forkserver_t fsrv_var = {0}; + afl_forkserver_t *fsrv = &fsrv_var; + afl_fsrv_init(fsrv); + map_size = get_map_size(); + fsrv->map_size = map_size; + + if ((send_buf = malloc(map_size + 4)) == NULL) PFATAL("malloc"); + + while ((opt = getopt(argc, argv, "+i:f:m:t:QUWh")) > 0) { + + switch (opt) { + + case 'i': + + if (port > 0) { FATAL("Multiple -i options not supported"); } + port = atoi(optarg); + if (port < 1 || port > 65535) + FATAL("invalid port definition, must be between 1-65535: %s", optarg); + break; + + case 'f': + + if (out_file) { FATAL("Multiple -f options not supported"); } + fsrv->use_stdin = 0; + out_file = optarg; + break; + + case 'm': { + + u8 suffix = 'M'; + + if (mem_limit_given) { FATAL("Multiple -m options not supported"); } + mem_limit_given = 1; + + if (!optarg) { FATAL("Wrong usage of -m"); } + + if (!strcmp(optarg, "none")) { + + fsrv->mem_limit = 0; + break; + + } + + if (sscanf(optarg, "%llu%c", &fsrv->mem_limit, &suffix) < 1 || + optarg[0] == '-') { + + FATAL("Bad syntax used for -m"); + + } + + switch (suffix) { + + case 'T': + fsrv->mem_limit *= 1024 * 1024; + break; + case 'G': + fsrv->mem_limit *= 1024; + break; + case 'k': + fsrv->mem_limit /= 1024; + break; + case 'M': + break; + + default: + FATAL("Unsupported suffix or bad syntax for -m"); + + } + + if (fsrv->mem_limit < 5) { FATAL("Dangerously low value of -m"); } + + if (sizeof(rlim_t) == 4 && fsrv->mem_limit > 2000) { + + FATAL("Value of -m out of range on 32-bit systems"); + + } + + } + + break; + + case 't': + + if (timeout_given) { FATAL("Multiple -t options not supported"); } + timeout_given = 1; + + if (!optarg) { FATAL("Wrong usage of -t"); } + + fsrv->exec_tmout = atoi(optarg); + + if (fsrv->exec_tmout < 10 || optarg[0] == '-') { + + FATAL("Dangerously low value of -t"); + + } + + break; + + case 'Q': + + if (fsrv->qemu_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; } + + fsrv->qemu_mode = 1; + break; + + case 'U': + + if (unicorn_mode) { FATAL("Multiple -Q options not supported"); } + if (!mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; } + + unicorn_mode = 1; + break; + + case 'W': /* Wine+QEMU mode */ + + if (use_wine) { FATAL("Multiple -W options not supported"); } + fsrv->qemu_mode = 1; + use_wine = 1; + + if (!mem_limit_given) { fsrv->mem_limit = 0; } + + break; + + case 'h': + usage(argv[0]); + return -1; + break; + + default: + usage(argv[0]); + + } + + } + + if (optind == argc || port < 1) { usage(argv[0]); } + + check_environment_vars(envp); + + sharedmem_t shm = {0}; + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); + + in_data = afl_realloc((void **)&in_data, 65536); + if (unlikely(!in_data)) { PFATAL("Alloc"); } + + atexit(at_exit_handler); + setup_signal_handlers(); + + set_up_environment(fsrv); + + fsrv->target_path = find_binary(argv[optind]); + detect_file_args(argv + optind, out_file, &fsrv->use_stdin); + + if (fsrv->qemu_mode) { + + if (use_wine) { + + use_argv = get_wine_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } else { + + use_argv = get_qemu_argv(argv[0], &fsrv->target_path, argc - optind, + argv + optind); + + } + + } else { + + use_argv = argv + optind; + + } + + if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) PFATAL("socket() failed"); + +#ifdef SO_REUSEADDR + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { + + WARNF("setsockopt(SO_REUSEADDR) failed"); + + } + +#endif + +#ifdef SO_PRIORITY + int priority = 7; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) { + + priority = 6; + if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < + 0) + WARNF("could not set priority on socket"); + + } + +#endif + + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin6_family = AF_INET6; + serveraddr.sin6_port = htons(port); + serveraddr.sin6_addr = in6addr_any; + + if (bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) + PFATAL("bind() failed"); + + if (listen(sock, 1) < 0) { PFATAL("listen() failed"); } + + afl_fsrv_start( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + +#ifdef USE_DEFLATE + compressor = libdeflate_alloc_compressor(1); + decompressor = libdeflate_alloc_decompressor(); + buf2 = afl_realloc((void **)&buf2, map_size + 16); + buf2_len = map_size + 16; + if (unlikely(!buf2)) { PFATAL("alloc"); } + lenptr = (u32 *)(buf2 + 4); + fprintf(stderr, "Compiled with compression support\n"); +#endif + + fprintf(stderr, + "Waiting for incoming connection from afl-network-client on port %d " + "...\n", + port); + + if ((s = accept(sock, NULL, NULL)) < 0) { PFATAL("accept() failed"); } + fprintf(stderr, "Received connection, starting ...\n"); + +#ifdef SO_PRIORITY + priority = 7; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) { + + priority = 6; + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)) < 0) + WARNF("could not set priority on socket"); + + } + +#endif + + while ((in_len = recv_testcase(s, (void **)&in_data)) > 0) { + + // fprintf(stderr, "received %u\n", in_len); + (void)run_target(fsrv, use_argv, in_data, in_len, 1); + + memcpy(send_buf + 4, fsrv->trace_bits, fsrv->map_size); + +#ifdef USE_DEFLATE + memcpy(buf2, &fsrv->child_status, 4); + *lenptr = (u32)libdeflate_deflate_compress( + compressor, send_buf + 4, fsrv->map_size, buf2 + 8, buf2_len - 8); + // fprintf(stderr, "COMPRESS (%u->%u): ", fsrv->map_size, *lenptr); + // for (u32 i = 0; i < fsrv->map_size; i++) fprintf(stderr, "%02x", + // fsrv->trace_bits[i]); fprintf(stderr, "\n"); + if (send(s, buf2, *lenptr + 8, 0) != 8 + *lenptr) + FATAL("could not send data"); +#else + memcpy(send_buf, &fsrv->child_status, 4); + if (send(s, send_buf, fsrv->map_size + 4, 0) != 4 + fsrv->map_size) + FATAL("could not send data"); +#endif + + // fprintf(stderr, "sent result\n"); + + } + + unlink(out_file); + if (out_file) { ck_free(out_file); } + out_file = NULL; + + afl_shm_deinit(&shm); + afl_fsrv_deinit(fsrv); + if (fsrv->target_path) { ck_free(fsrv->target_path); } + afl_free(in_data); +#if USE_DEFLATE + afl_free(buf2); + libdeflate_free_compressor(compressor); + libdeflate_free_decompressor(decompressor); +#endif + + argv_cpy_free(argv); + + exit(0); + +} + diff --git a/utils/afl_proxy/Makefile b/utils/afl_proxy/Makefile new file mode 100644 index 00000000..4b368f8d --- /dev/null +++ b/utils/afl_proxy/Makefile @@ -0,0 +1,7 @@ +all: afl-proxy + +afl-proxy: afl-proxy.c + $(CC) -I../../include -o afl-proxy afl-proxy.c + +clean: + rm -f afl-proxy *~ core diff --git a/utils/afl_proxy/README.md b/utils/afl_proxy/README.md new file mode 100644 index 00000000..3c768a19 --- /dev/null +++ b/utils/afl_proxy/README.md @@ -0,0 +1,9 @@ +# afl-proxy + +afl-proxy is an example skeleton file which can easily be used to fuzz +and instrument non-standard things. + +You only need to change the while() loop of the main() to send the +data of buf[] with length len to the target and write the coverage +information to __afl_area_ptr[__afl_map_size] + diff --git a/utils/afl_proxy/afl-proxy.c b/utils/afl_proxy/afl-proxy.c new file mode 100644 index 00000000..f2dfeac1 --- /dev/null +++ b/utils/afl_proxy/afl-proxy.c @@ -0,0 +1,238 @@ +/* + american fuzzy lop++ - afl-proxy skeleton example + --------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + + HOW-TO + ====== + + You only need to change the while() loop of the main() to send the + data of buf[] with length len to the target and write the coverage + information to __afl_area_ptr[__afl_map_size] + + +*/ + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +u8 *__afl_area_ptr; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +#else +__thread u32 __afl_map_size = MAP_SIZE; +#endif + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ + +static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + +} + +static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status, res = 0xffffff; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) return 0; + + /* we have a testcase - read it */ + status = read(0, buf, max_len); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &res, 4) != 4) return 0; + + if (status < 1) + return 0; + else + return status; + +} + +static void __afl_end_testcase(void) { + + int status = 0xffffff; + + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(1); + +} + +/* you just need to modify the while() loop in this main() */ + +int main(int argc, char *argv[]) { + + /* This is were the testcase data is written into */ + u8 buf[1024]; // this is the maximum size for a test case! set it! + u32 len; + + /* here you specify the map size you need that you are reporting to + afl-fuzz. */ + __afl_map_size = MAP_SIZE; // default is 65536 + + /* then we initialize the shared memory map and start the forkserver */ + __afl_map_shm(); + __afl_start_forkserver(); + + while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { + + /* here you have to create the magic that feeds the buf/len to the + target and write the coverage to __afl_area_ptr */ + + // ... the magic ... + + /* report the test case is done and wait for the next */ + __afl_end_testcase(); + + } + + return 0; + +} + diff --git a/utils/afl_untracer/Makefile b/utils/afl_untracer/Makefile new file mode 100644 index 00000000..14a09b41 --- /dev/null +++ b/utils/afl_untracer/Makefile @@ -0,0 +1,16 @@ +ifdef DEBUG + OPT=-O0 +else + OPT=-O3 +endif + +all: afl-untracer libtestinstr.so + +afl-untracer: afl-untracer.c + $(CC) $(OPT) -I../../include -g -o afl-untracer afl-untracer.c -ldl + +libtestinstr.so: libtestinstr.c + $(CC) -g -O0 -fPIC -o libtestinstr.so -shared libtestinstr.c + +clean: + rm -f afl-untracer libtestinstr.so *~ core diff --git a/utils/afl_untracer/README.md b/utils/afl_untracer/README.md new file mode 100644 index 00000000..ada0c916 --- /dev/null +++ b/utils/afl_untracer/README.md @@ -0,0 +1,60 @@ +# afl-untracer - fast fuzzing of binary-only libraries + +## Introduction + +afl-untracer is an example skeleton file which can easily be used to fuzz +a closed source library. + +It requires less memory and is x3-5 faster than qemu_mode however it is way +more course grained and does not provide interesting features like compcov +or cmplog. + +Supported is so far Intel (i386/x86_64) and AARCH64. + +## How-to + +### Modify afl-untracer.c + +Read and modify afl-untracer.c then `make`. +To adapt afl-untracer.c to your needs, read the header of the file and then +search and edit the `STEP 1`, `STEP 2` and `STEP 3` locations. + +### Generate patches.txt file + +To generate the `patches.txt` file for your target library use the +`ida_get_patchpoints.py` script for IDA Pro or +`ghidra_get_patchpoints.java` for Ghidra. + +The patches.txt file has to be pointed to by `AFL_UNTRACER_FILE`. + +To easily run the scripts without needing to run the GUI with Ghidra: +``` +/opt/ghidra/support/analyzeHeadless /tmp/ tmp$$ -import libtestinstr.so -postscript ./ghidra_get_patchpoints.java +rm -rf /tmp/tmp$$ +``` +The file is created at `~/Desktop/patches.txt` + +### Fuzzing + +Example (after modifying afl-untracer.c to your needs, compiling and creating +patches.txt): +``` +LD_LIBRARY_PATH=/path/to/target/library AFL_UNTRACER_FILE=./patches.txt afl-fuzz -i in -o out -- ./afl-untracer +``` +(or even remote via afl-network-proxy). + +### Testing and debugging + +For testing/debugging you can try: +``` +make DEBUG=1 +AFL_UNTRACER_FILE=./patches.txt AFL_DEBUG=1 gdb ./afl-untracer +``` +and then you can easily set breakpoints to "breakpoint" and "fuzz". + +# Background + +This idea is based on [UnTracer](https://github.com/FoRTE-Research/UnTracer-AFL) +and modified by [Trapfuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz). +This implementation is slower because the traps are not patched out with each +run, but on the other hand gives much better coverage information. diff --git a/utils/afl_untracer/TODO b/utils/afl_untracer/TODO new file mode 100644 index 00000000..fffffacf --- /dev/null +++ b/utils/afl_untracer/TODO @@ -0,0 +1,2 @@ + * add shmem fuzzing + * add snapshot feature? diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c new file mode 100644 index 00000000..cb6f948c --- /dev/null +++ b/utils/afl_untracer/afl-untracer.c @@ -0,0 +1,768 @@ +/* + american fuzzy lop++ - afl-untracer skeleton example + --------------------------------------------------- + + Written by Marc Heuse + + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + + HOW-TO + ====== + + You only need to change the following: + + 1. decide if you want to receive data from stdin [DEFAULT] or file(name) + -> use_stdin = 0 if via file, and what the maximum input size is + 2. dl load the library you want to fuzz, lookup the functions you need + and setup the calls to these + 3. in the while loop you call the functions in the necessary order - + incl the cleanup. the cleanup is important! + + Just look these steps up in the code, look for "// STEP x:" + + +*/ + +#define __USE_GNU +#define _GNU_SOURCE + +#ifdef __ANDROID__ + #include "android-ashmem.h" +#endif +#include "config.h" +#include "types.h" +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__linux__) + #include +#elif defined(__APPLE__) && defined(__LP64__) + #include +#elif defined(__FreeBSD__) + #include + #include +#else + #error "Unsupported platform" +#endif + +#define MEMORY_MAP_DECREMENT 0x200000000000 +#define MAX_LIB_COUNT 128 + +// STEP 1: + +/* here you need to specify the parameter for the target function */ +static void *(*o_function)(u8 *buf, int len); + +/* use stdin (1) or a file on the commandline (0) */ +static u32 use_stdin = 1; + +/* This is were the testcase data is written into */ +static u8 buf[10000]; // this is the maximum size for a test case! set it! + +/* If you want to have debug output set this to 1, can also be set with + AFL_DEBUG */ +static u32 debug = 0; + +// END STEP 1 + +typedef struct library_list { + + u8 *name; + u64 addr_start, addr_end; + +} library_list_t; + +#ifdef __ANDROID__ +u32 __afl_map_size = MAP_SIZE; +u32 do_exit; +#else +__thread u32 __afl_map_size = MAP_SIZE; +__thread u32 do_exit; +#endif + +static pid_t pid = 65537; +static pthread_t __afl_thread; +static u8 __afl_dummy[MAP_SIZE]; +static u8 * __afl_area_ptr = __afl_dummy; +static u8 * inputfile; // this will point to argv[1] +static u32 len; + +static library_list_t liblist[MAX_LIB_COUNT]; +static u32 liblist_cnt; + +static void sigtrap_handler(int signum, siginfo_t *si, void *context); +static void fuzz(void); + +/* read the library information */ +void read_library_information(void) { + +#if defined(__linux__) + FILE *f; + u8 buf[1024], *b, *m, *e, *n; + + if ((f = fopen("/proc/self/maps", "r")) == NULL) + FATAL("cannot open /proc/self/maps"); + + if (debug) fprintf(stderr, "Library list:\n"); + while (fgets(buf, sizeof(buf), f)) { + + if (strstr(buf, " r-x")) { + + if (liblist_cnt >= MAX_LIB_COUNT) { + + WARNF("too many libraries to old, maximum count of %d reached", + liblist_cnt); + return; + + } + + b = buf; + m = index(buf, '-'); + e = index(buf, ' '); + if ((n = rindex(buf, '/')) == NULL) n = rindex(buf, ' '); + if (n && + ((*n >= '0' && *n <= '9') || *n == '[' || *n == '{' || *n == '(')) + n = NULL; + else + n++; + if (b && m && e && n && *n) { + + *m++ = 0; + *e = 0; + if (n[strlen(n) - 1] == '\n') n[strlen(n) - 1] = 0; + + liblist[liblist_cnt].name = strdup(n); + liblist[liblist_cnt].addr_start = strtoull(b, NULL, 16); + liblist[liblist_cnt].addr_end = strtoull(m, NULL, 16); + if (debug) + fprintf( + stderr, "%s:%llx (%llx-%llx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + liblist_cnt++; + + } + + } + + } + + if (debug) fprintf(stderr, "\n"); + +#elif defined(__FreeBSD__) + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + char * buf, *start, *end; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + size_t len; + + if (debug) fprintf(stderr, "Library list:\n"); + if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) { return; } + + len = len * 4 / 3; + + buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) { return; } + + if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { + + munmap(buf, len); + return; + + } + + start = buf; + end = buf + len; + + while (start < end) { + + struct kinfo_vmentry *region = (struct kinfo_vmentry *)start; + size_t size = region->kve_structsize; + + if (size == 0) { break; } + + if ((region->kve_protection & KVME_PROT_READ) && + !(region->kve_protection & KVME_PROT_EXEC)) { + + liblist[liblist_cnt].name = + region->kve_path[0] != '\0' ? strdup(region->kve_path) : 0; + liblist[liblist_cnt].addr_start = region->kve_start; + liblist[liblist_cnt].addr_end = region->kve_end; + + if (debug) { + + fprintf(stderr, "%s:%x (%lx-%lx)\n", liblist[liblist_cnt].name, + liblist[liblist_cnt].addr_end - liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_start, + liblist[liblist_cnt].addr_end - 1); + + } + + liblist_cnt++; + + } + + start += size; + + } + +#endif + +} + +library_list_t *find_library(char *name) { + +#if defined(__linux__) + u32 i; + + for (i = 0; i < liblist_cnt; i++) + if (strncmp(liblist[i].name, name, strlen(name)) == 0) return &liblist[i]; +#elif defined(__APPLE__) && defined(__LP64__) + kern_return_t err; + static library_list_t lib; + + // get the list of all loaded modules from dyld + // the task_info mach API will get the address of the dyld all_image_info + // struct for the given task from which we can get the names and load + // addresses of all modules + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + err = task_info(mach_task_self(), TASK_DYLD_INFO, + (task_info_t)&task_dyld_info, &count); + + const struct dyld_all_image_infos *all_image_infos = + (const struct dyld_all_image_infos *)task_dyld_info.all_image_info_addr; + const struct dyld_image_info *image_infos = all_image_infos->infoArray; + + for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { + + const char * image_name = image_infos[i].imageFilePath; + mach_vm_address_t image_load_address = + (mach_vm_address_t)image_infos[i].imageLoadAddress; + if (strstr(image_name, name)) { + + lib.name = name; + lib.addr_start = (u64)image_load_address; + lib.addr_end = 0; + return &lib; + + } + + } + +#endif + + return NULL; + +} + +/* for having an easy breakpoint location after loading the shared library */ +// this seems to work for clang too. nice :) requires gcc 4.4+ +#pragma GCC push_options +#pragma GCC optimize("O0") +void breakpoint(void) { + + if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); + +} + +#pragma GCC pop_options + +/* Error reporting to forkserver controller */ + +void send_forkserver_error(int error) { + + u32 status; + if (!error || error > 0xffff) return; + status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); + if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) return; + +} + +/* SHM setup. */ + +static void __afl_map_shm(void) { + + char *id_str = getenv(SHM_ENV_VAR); + char *ptr; + + if ((ptr = getenv("AFL_MAP_SIZE")) != NULL) { + + u32 val = atoi(ptr); + if (val > 0) __afl_map_size = val; + + } + + if (__afl_map_size > MAP_SIZE) { + + if (__afl_map_size > FS_OPT_MAX_MAPSIZE) { + + fprintf(stderr, + "Error: AFL++ tools *require* to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + if (id_str) { + + send_forkserver_error(FS_ERROR_MAP_SIZE); + exit(-1); + + } + + } else { + + fprintf(stderr, + "Warning: AFL++ tools will need to set AFL_MAP_SIZE to %u to " + "be able to run this instrumented program!\n", + __afl_map_size); + + } + + } + + if (id_str) { + +#ifdef USEMMAP + const char * shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, 0600); + if (shm_fd == -1) { + + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + + } + + /* map the shared memory segment to the address space of the process */ + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + if (shm_base == MAP_FAILED) { + + close(shm_fd); + shm_fd = -1; + + fprintf(stderr, "mmap() failed\n"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + + } + + __afl_area_ptr = shm_base; +#else + u32 shm_id = atoi(id_str); + + __afl_area_ptr = shmat(shm_id, 0, 0); + +#endif + + if (__afl_area_ptr == (void *)-1) { + + send_forkserver_error(FS_ERROR_SHMAT); + exit(1); + + } + + /* Write something into the bitmap so that the parent doesn't give up */ + + __afl_area_ptr[0] = 1; + + } + +} + +/* Fork server logic. */ +inline static void __afl_start_forkserver(void) { + + u8 tmp[4] = {0, 0, 0, 0}; + u32 status = 0; + + if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) + status |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); + if (status) status |= (FS_OPT_ENABLED); + memcpy(tmp, &status, 4); + + /* Phone home and tell the parent that we're OK. */ + if (write(FORKSRV_FD + 1, tmp, 4) != 4) do_exit = 1; + // fprintf(stderr, "write0 %d\n", do_exit); + +} + +inline static u32 __afl_next_testcase(u8 *buf, u32 max_len) { + + s32 status; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + if (read(FORKSRV_FD, &status, 4) != 4) do_exit = 1; + // fprintf(stderr, "read %d\n", do_exit); + + /* we have a testcase - read it if we read from stdin */ + if (use_stdin) { + + if ((status = read(0, buf, max_len)) <= 0) exit(-1); + + } else + + status = 1; + // fprintf(stderr, "stdin: %d %d\n", use_stdin, status); + + /* report that we are starting the target */ + if (write(FORKSRV_FD + 1, &pid, 4) != 4) do_exit = 1; + // fprintf(stderr, "write1 %d\n", do_exit); + + __afl_area_ptr[0] = 1; // put something in the map + + return status; + +} + +inline static void __afl_end_testcase(int status) { + + if (write(FORKSRV_FD + 1, &status, 4) != 4) do_exit = 1; + // fprintf(stderr, "write2 %d\n", do_exit); + if (do_exit) exit(0); + +} + +#ifdef __aarch64__ + #define SHADOW(addr) \ + ((uint64_t *)(((uintptr_t)addr & 0xfffffffffffffff8) - \ + MEMORY_MAP_DECREMENT - \ + ((uintptr_t)addr & 0x7) * 0x10000000000)) +#else + #define SHADOW(addr) \ + ((uint32_t *)(((uintptr_t)addr & 0xfffffffffffffffc) - \ + MEMORY_MAP_DECREMENT - \ + ((uintptr_t)addr & 0x3) * 0x10000000000)) +#endif + +void setup_trap_instrumentation(void) { + + library_list_t *lib_base = NULL; + size_t lib_size = 0; + u8 * lib_addr; + char * line = NULL; + size_t nread, len = 0; + char * filename = getenv("AFL_UNTRACER_FILE"); + if (!filename) filename = getenv("TRAPFUZZ_FILE"); + if (!filename) FATAL("AFL_UNTRACER_FILE environment variable not set"); + + FILE *patches = fopen(filename, "r"); + if (!patches) FATAL("Couldn't open AFL_UNTRACER_FILE file %s", filename); + + // Index into the coverage bitmap for the current trap instruction. +#ifdef __aarch64__ + uint64_t bitmap_index = 0; +#else + uint32_t bitmap_index = 0; +#endif + + while ((nread = getline(&line, &len, patches)) != -1) { + + char *end = line + len; + + char *col = strchr(line, ':'); + if (col) { + + // It's a library:size pair + *col++ = 0; + + lib_base = find_library(line); + if (!lib_base) FATAL("Library %s does not appear to be loaded", line); + + // we ignore the defined lib_size + lib_size = strtoul(col, NULL, 16); +#if (__linux__) + if (lib_size < lib_base->addr_end - lib_base->addr_start) + lib_size = lib_base->addr_end - lib_base->addr_start; +#endif + if (lib_size % 0x1000 != 0) + WARNF("Invalid library size 0x%zx. Must be multiple of 0x1000", + lib_size); + + lib_addr = (u8 *)lib_base->addr_start; + + // Make library code writable. + if (mprotect((void *)lib_addr, lib_size, + PROT_READ | PROT_WRITE | PROT_EXEC) != 0) + FATAL("Failed to mprotect library %s writable", line); + + // Create shadow memory. +#ifdef __aarch64__ + for (int i = 0; i < 8; i++) { + +#else + for (int i = 0; i < 4; i++) { + +#endif + + void *shadow_addr = SHADOW(lib_addr + i); + void *shadow = mmap(shadow_addr, lib_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); + if (debug) + fprintf(stderr, "Shadow: %s %d = %p-%p for %p\n", line, i, shadow, + shadow + lib_size - 1, lib_addr); + if (shadow == MAP_FAILED) FATAL("Failed to mmap shadow memory"); + + } + + // Done, continue with next line. + continue; + + } + + // It's an offset, parse it and do the patching. + unsigned long offset = strtoul(line, NULL, 16); + + if (offset > lib_size) + FATAL("Invalid offset: 0x%lx. Current library is 0x%zx bytes large", + offset, lib_size); + + if (bitmap_index >= __afl_map_size) + FATAL("Too many basic blocks to instrument"); + +#ifdef __arch64__ + uint64_t +#else + uint32_t +#endif + *shadow = SHADOW(lib_addr + offset); + if (*shadow != 0) continue; // skip duplicates + + // Make lookup entry in shadow memory. + +#if ((defined(__APPLE__) && defined(__LP64__)) || defined(__x86_64__) || \ + defined(__i386__)) + + // this is for Intel x64 + + uint8_t orig_byte = lib_addr[offset]; + *shadow = (bitmap_index << 8) | orig_byte; + lib_addr[offset] = 0xcc; // replace instruction with debug trap + if (debug) + fprintf(stderr, + "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %08x\n", + lib_addr, offset, lib_addr + offset, orig_byte, shadow, + bitmap_index, *shadow); + +#elif defined(__aarch64__) + + // this is for aarch64 + + uint32_t *patch_bytes = (uint32_t *)(lib_addr + offset); + uint32_t orig_bytes = *patch_bytes; + *shadow = (bitmap_index << 32) | orig_bytes; + *patch_bytes = 0xd4200000; // replace instruction with debug trap + if (debug) + fprintf(stderr, + "Patch entry: %p[%x] = %p = %02x -> SHADOW(%p) #%d -> %016x\n", + lib_addr, offset, lib_addr + offset, orig_bytes, shadow, + bitmap_index, *shadow); + +#else + // this will be ARM and AARCH64 + // for ARM we will need to identify if the code is in thumb or ARM + #error "non x86_64/aarch64 not supported yet" + //__arm__: + // linux thumb: 0xde01 + // linux arm: 0xe7f001f0 + //__aarch64__: + // linux aarch64: 0xd4200000 +#endif + + bitmap_index++; + + } + + free(line); + fclose(patches); + + // Install signal handler for SIGTRAP. + struct sigaction s; + s.sa_flags = SA_SIGINFO; + s.sa_sigaction = sigtrap_handler; + sigemptyset(&s.sa_mask); + sigaction(SIGTRAP, &s, 0); + + if (debug) fprintf(stderr, "Patched %u locations.\n", bitmap_index); + __afl_map_size = bitmap_index; + if (__afl_map_size % 8) __afl_map_size = (((__afl_map_size + 7) >> 3) << 3); + +} + +/* the signal handler for the traps / debugging interrupts + No debug output here because this would cost speed */ +static void sigtrap_handler(int signum, siginfo_t *si, void *context) { + + uint64_t addr; + // Must re-execute the instruction, so decrement PC by one instruction. + ucontext_t *ctx = (ucontext_t *)context; +#if defined(__APPLE__) && defined(__LP64__) + ctx->uc_mcontext->__ss.__rip -= 1; + addr = ctx->uc_mcontext->__ss.__rip; +#elif defined(__linux__) + #if defined(__x86_64__) || defined(__i386__) + ctx->uc_mcontext.gregs[REG_RIP] -= 1; + addr = ctx->uc_mcontext.gregs[REG_RIP]; + #elif defined(__aarch64__) + ctx->uc_mcontext.pc -= 4; + addr = ctx->uc_mcontext.pc; + #else + #error "Unsupported processor" + #endif +#elif defined(__FreeBSD__) && defined(__LP64__) + ctx->uc_mcontext.mc_rip -= 1; + addr = ctx->uc_mcontext.mc_rip; +#else + #error "Unsupported platform" +#endif + + // fprintf(stderr, "TRAP at context addr = %lx, fault addr = %lx\n", addr, + // si->si_addr); + + // If the trap didn't come from our instrumentation, then we probably will + // just segfault here + uint8_t *faultaddr; + if (unlikely(si->si_addr)) + faultaddr = (u8 *)si->si_addr - 1; + else + faultaddr = (u8 *)addr; + // if (debug) fprintf(stderr, "Shadow location: %p\n", SHADOW(faultaddr)); + uint32_t shadow = *SHADOW(faultaddr); + uint8_t orig_byte = shadow & 0xff; + uint32_t index = shadow >> 8; + + // if (debug) fprintf(stderr, "shadow data: %x, orig_byte %02x, index %d\n", + // shadow, orig_byte, index); + + // Index zero is invalid so that it is still possible to catch actual trap + // instructions in instrumented libraries. + if (unlikely(index == 0)) abort(); + + // Restore original instruction + *faultaddr = orig_byte; + + __afl_area_ptr[index] = 128; + +} + +/* the MAIN function */ +int main(int argc, char *argv[]) { + + (void)personality(ADDR_NO_RANDOMIZE); // disable ASLR + + pid = getpid(); + if (getenv("AFL_DEBUG")) debug = 1; + + /* by default we use stdin, but also a filename can be passed, in this + case the input is argv[1] and we have to disable stdin */ + if (argc > 1) { + + use_stdin = 0; + inputfile = argv[1]; + + } + + // STEP 2: load the library you want to fuzz and lookup the functions, + // inclusive of the cleanup functions + // NOTE: above the main() you have to define the functions! + + void *dl = dlopen("./libtestinstr.so", RTLD_LAZY); + if (!dl) FATAL("could not find target library"); + o_function = dlsym(dl, "testinstr"); + if (!o_function) FATAL("could not resolve target function from library"); + if (debug) fprintf(stderr, "Function address: %p\n", o_function); + + // END STEP 2 + + /* setup instrumentation, shared memory and forkserver */ + breakpoint(); + read_library_information(); + setup_trap_instrumentation(); + __afl_map_shm(); + __afl_start_forkserver(); + + while (1) { + + // instead of fork() we could also use the snapshot lkm or do our own mini + // snapshot feature like in https://github.com/marcinguy/fuzzer + // -> snapshot.c + if ((pid = fork()) == -1) PFATAL("fork failed"); + + if (pid) { + + u32 status; + if (waitpid(pid, &status, 0) < 0) exit(1); + /* report the test case is done and wait for the next */ + __afl_end_testcase(status); + + } else { + + pid = getpid(); + while ((len = __afl_next_testcase(buf, sizeof(buf))) > 0) { + + // in this function the fuzz magic happens, this is STEP 3 + fuzz(); + + // we can use _exit which is faster because our target library + // was loaded via dlopen and therefore cannot have deconstructors + // registered. + _exit(0); + + } + + } + + } + + return 0; + +} + +#ifndef _DEBUG +inline +#endif + static void + fuzz(void) { + + // STEP 3: call the function to fuzz, also the functions you might + // need to call to prepare the function and - important! - + // to clean everything up + + // in this example we use the input file, not stdin! + (*o_function)(buf, len); + + // normally you also need to cleanup + //(*o_LibFree)(foo); + + // END STEP 3 + +} + diff --git a/utils/afl_untracer/ghidra_get_patchpoints.java b/utils/afl_untracer/ghidra_get_patchpoints.java new file mode 100644 index 00000000..2a93642b --- /dev/null +++ b/utils/afl_untracer/ghidra_get_patchpoints.java @@ -0,0 +1,84 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Find patch points for untracer tools (e.g. afl++ utils/afl_untracer) +// +// Copy to ..../Ghidra/Features/Search/ghidra_scripts/ +// Writes the results to ~/Desktop/patches.txt +// +// This is my very first Ghidra script. I am sure this could be done better. +// +//@category Search + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.*; +import ghidra.program.model.block.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.*; +import ghidra.program.model.mem.*; + +import java.io.*; + +public class ghidra_get_patchpoints extends GhidraScript { + + @Override + public void run() throws Exception { + + long segment_start = 0; + Memory memory = currentProgram.getMemory(); + MultEntSubModel model = new MultEntSubModel(currentProgram); + CodeBlockIterator subIter = model.getCodeBlocks(monitor); + BufferedWriter out = new BufferedWriter(new FileWriter(System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "patches.txt")); + + while (subIter.hasNext()) { + + CodeBlock multiEntryBlock = subIter.next(); + SimpleBlockModel basicBlockModel = new SimpleBlockModel(currentProgram); + CodeBlockIterator bbIter = basicBlockModel.getCodeBlocksContaining(multiEntryBlock, monitor); + + while (bbIter.hasNext()) { + + CodeBlock basicBlock = bbIter.next(); + + if (segment_start == 0) { + + Address firstAddr = basicBlock.getFirstStartAddress(); + long firstBlockAddr = firstAddr.getAddressableWordOffset(); + MemoryBlock mb = memory.getBlock(firstAddr); + Address startAddr = mb.getStart(); + Address endAddr = mb.getEnd(); + segment_start = startAddr.getAddressableWordOffset(); + if ((firstBlockAddr - segment_start) >= 0x1000) + segment_start += 0x1000; + long segment_end = endAddr.getAddressableWordOffset(); + long segment_size = segment_end - segment_start; + if ((segment_size % 0x1000) > 0) + segment_size = (((segment_size / 0x1000) + 1) * 0x1000); + out.write(currentProgram.getName() + ":0x" + Long.toHexString(segment_size) + "\n"); + //println("Start: " + Long.toHexString(segment_start)); + //println("End: " + Long.toHexString(segment_end)); + + } + + if (basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start > 0) + out.write("0x" + Long.toHexString(basicBlock.getFirstStartAddress().getAddressableWordOffset() - segment_start) + "\n"); + + } + } + + out.close(); + + } +} diff --git a/utils/afl_untracer/ida_get_patchpoints.py b/utils/afl_untracer/ida_get_patchpoints.py new file mode 100644 index 00000000..43cf6d89 --- /dev/null +++ b/utils/afl_untracer/ida_get_patchpoints.py @@ -0,0 +1,62 @@ +# +# IDAPython script for IDA Pro +# Slightly modified from https://github.com/googleprojectzero/p0tools/blob/master/TrapFuzz/findPatchPoints.py +# + +import idautils +import idaapi +import ida_nalt +import idc + +# See https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml + +from os.path import expanduser +home = expanduser("~") + +patchpoints = set() + +max_offset = 0 +for seg_ea in idautils.Segments(): + name = idc.get_segm_name(seg_ea) + #print("Segment: " + name) + if name != "__text" and name != ".text": + continue + + start = idc.get_segm_start(seg_ea) + end = idc.get_segm_end(seg_ea) + first = 0 + subtract_addr = 0 + #print("Start: " + hex(start) + " End: " + hex(end)) + for func_ea in idautils.Functions(start, end): + f = idaapi.get_func(func_ea) + if not f: + continue + for block in idaapi.FlowChart(f): + if start <= block.start_ea < end: + if first == 0: + if block.start_ea >= 0x1000: + subtract_addr = 0x1000 + first = 1 + + max_offset = max(max_offset, block.start_ea) + patchpoints.add(block.start_ea - subtract_addr) + #else: + # print("Warning: broken CFG?") + +# Round up max_offset to page size +size = max_offset +rem = size % 0x1000 +if rem != 0: + size += 0x1000 - rem + +print("Writing to " + home + "/Desktop/patches.txt") + +with open(home + "/Desktop/patches.txt", "w") as f: + f.write(ida_nalt.get_root_filename() + ':' + hex(size) + '\n') + f.write('\n'.join(map(hex, sorted(patchpoints)))) + f.write('\n') + +print("Done, found {} patchpoints".format(len(patchpoints))) + +# For headless script running remove the comment from the next line +#ida_pro.qexit() diff --git a/utils/afl_untracer/libtestinstr.c b/utils/afl_untracer/libtestinstr.c new file mode 100644 index 00000000..96b1cf21 --- /dev/null +++ b/utils/afl_untracer/libtestinstr.c @@ -0,0 +1,35 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +void testinstr(char *buf, int len) { + + if (len < 1) return; + buf[len] = 0; + + // we support three input cases + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + +} + diff --git a/utils/afl_untracer/patches.txt b/utils/afl_untracer/patches.txt new file mode 100644 index 00000000..7e964249 --- /dev/null +++ b/utils/afl_untracer/patches.txt @@ -0,0 +1,34 @@ +libtestinstr.so:0x1000 +0x10 +0x12 +0x20 +0x36 +0x30 +0x40 +0x50 +0x63 +0x6f +0x78 +0x80 +0xa4 +0xb0 +0xb8 +0x100 +0xc0 +0xc9 +0xd7 +0xe3 +0xe8 +0xf8 +0x105 +0x11a +0x135 +0x141 +0x143 +0x14e +0x15a +0x15c +0x168 +0x16a +0x16b +0x170 diff --git a/utils/aflpp_driver/GNUmakefile b/utils/aflpp_driver/GNUmakefile new file mode 100644 index 00000000..c1a087d7 --- /dev/null +++ b/utils/aflpp_driver/GNUmakefile @@ -0,0 +1,46 @@ +ifeq "" "$(LLVM_CONFIG)" + LLVM_CONFIG=llvm-config +endif + +LLVM_BINDIR = $(shell $(LLVM_CONFIG) --bindir 2>/dev/null) +ifneq "" "$(LLVM_BINDIR)" + LLVM_BINDIR := $(LLVM_BINDIR)/ +endif + +CFLAGS := -O3 -funroll-loops -g + +all: libAFLDriver.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so + +aflpp_driver.o: aflpp_driver.c + -$(LLVM_BINDIR)clang -I. -I../../include $(CFLAGS) -c aflpp_driver.c + +libAFLDriver.a: aflpp_driver.o + ar ru libAFLDriver.a aflpp_driver.o + cp -vf libAFLDriver.a ../../ + +debug: + $(LLVM_BINDIR)clang -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.o ../../src/afl-performance.c + $(LLVM_BINDIR)clang -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + #$(LLVM_BINDIR)clang -S -emit-llvm -Wno-deprecated -I../../include $(CFLAGS) -D_DEBUG=\"1\" -c -o afl-performance.ll ../../src/afl-performance.c + #$(LLVM_BINDIR)clang -S -emit-llvm -I../../include -D_DEBUG=\"1\" -g -funroll-loops -c aflpp_driver.c + ar ru libAFLDriver.a afl-performance.o aflpp_driver.o + +aflpp_qemu_driver.o: aflpp_qemu_driver.c + $(LLVM_BINDIR)clang $(CFLAGS) -O0 -funroll-loops -c aflpp_qemu_driver.c + +libAFLQemuDriver.a: aflpp_qemu_driver.o + ar ru libAFLQemuDriver.a aflpp_qemu_driver.o + cp -vf libAFLQemuDriver.a ../../ + +aflpp_qemu_driver_hook.so: aflpp_qemu_driver_hook.o + $(LLVM_BINDIR)clang -shared aflpp_qemu_driver_hook.o -o aflpp_qemu_driver_hook.so + +aflpp_qemu_driver_hook.o: aflpp_qemu_driver_hook.c + $(LLVM_BINDIR)clang -fPIC $(CFLAGS) -funroll-loops -c aflpp_qemu_driver_hook.c + +test: debug + #clang -S -emit-llvm -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test.ll aflpp_driver_test.c + afl-clang-fast -D_DEBUG=\"1\" -I../../include -Wl,--allow-multiple-definition -funroll-loops -o aflpp_driver_test aflpp_driver_test.c libAFLDriver.a afl-performance.o + +clean: + rm -f *.o libAFLDriver*.a libAFLQemuDriver.a aflpp_qemu_driver_hook.so *~ core aflpp_driver_test diff --git a/utils/aflpp_driver/Makefile b/utils/aflpp_driver/Makefile new file mode 100644 index 00000000..3666a74d --- /dev/null +++ b/utils/aflpp_driver/Makefile @@ -0,0 +1,2 @@ +all: + @gmake all || echo please install GNUmake diff --git a/utils/aflpp_driver/aflpp_driver.c b/utils/aflpp_driver/aflpp_driver.c new file mode 100644 index 00000000..017aa72b --- /dev/null +++ b/utils/aflpp_driver/aflpp_driver.c @@ -0,0 +1,326 @@ +//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +//===----------------------------------------------------------------------===// + +/* This file allows to fuzz libFuzzer-style target functions + (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode. + +Usage: +################################################################################ +cat << EOF > test_fuzzer.cc +#include +#include +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; + +} + +EOF +# Build your target with -fsanitize-coverage=trace-pc-guard using fresh clang. +clang -g -fsanitize-coverage=trace-pc-guard test_fuzzer.cc -c +# Build afl-llvm-rt.o.c from the AFL distribution. +clang -c -w $AFL_HOME/instrumentation/afl-llvm-rt.o.c +# Build this file, link it with afl-llvm-rt.o.o and the target code. +clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o +# Run AFL: +rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; +$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out +################################################################################ +AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file +specified. If the file does not exist, it is created. This is useful for getting +stack traces (when using ASAN for example) or original error messages on hard +to reproduce bugs. Note that any content written to stderr will be written to +this file instead of stderr's usual location. + +AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. +If 1, close stdout at startup. If 2 close stderr; if 3 close both. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "cmplog.h" + +#ifdef _DEBUG + #include "hash.h" +#endif + +#ifndef MAP_FIXED_NOREPLACE + #define MAP_FIXED_NOREPLACE 0x100000 +#endif + +#define MAX_DUMMY_SIZE 256000 + +// Platform detection. Copied from FuzzerInternal.h +#ifdef __linux__ + #define LIBFUZZER_LINUX 1 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __APPLE__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 1 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __NetBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 1 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 0 +#elif __FreeBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 1 + #define LIBFUZZER_OPENBSD 0 +#elif __OpenBSD__ + #define LIBFUZZER_LINUX 0 + #define LIBFUZZER_APPLE 0 + #define LIBFUZZER_NETBSD 0 + #define LIBFUZZER_FREEBSD 0 + #define LIBFUZZER_OPENBSD 1 +#else + #error "Support for your platform has not been implemented" +#endif + +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); + +// Notify AFL about persistent mode. +static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##"; +int __afl_persistent_loop(unsigned int); + +// Notify AFL about deferred forkserver. +static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; +void __afl_manual_init(); + +// Use this optionally defined function to output sanitizer messages even if +// user asks to close stderr. +__attribute__((weak)) void __sanitizer_set_report_fd(void *); + +// Keep track of where stderr content is being written to, so that +// dup_and_close_stderr can use the correct one. +static FILE *output_file; + +// Experimental feature to use afl_driver without AFL's deferred mode. +// Needs to run before __afl_auto_init. +__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) { + + if (getenv("AFL_DRIVER_DONT_DEFER")) { + + if (unsetenv("__AFL_DEFER_FORKSRV")) { + + perror("Failed to unset __AFL_DEFER_FORKSRV"); + abort(); + + } + + } + +} + +// If the user asks us to duplicate stderr, then do it. +static void maybe_duplicate_stderr() { + + char *stderr_duplicate_filename = + getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + + if (!stderr_duplicate_filename) return; + + FILE *stderr_duplicate_stream = + freopen(stderr_duplicate_filename, "a+", stderr); + + if (!stderr_duplicate_stream) { + + fprintf( + stderr, + "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + abort(); + + } + + output_file = stderr_duplicate_stream; + +} + +// Most of these I/O functions were inspired by/copied from libFuzzer's code. +static void discard_output(int fd) { + + FILE *temp = fopen("/dev/null", "w"); + if (!temp) abort(); + dup2(fileno(temp), fd); + fclose(temp); + +} + +static void close_stdout() { + + discard_output(STDOUT_FILENO); + +} + +// Prevent the targeted code from writing to "stderr" but allow sanitizers and +// this driver to do so. +static void dup_and_close_stderr() { + + int output_fileno = fileno(output_file); + int output_fd = dup(output_fileno); + if (output_fd <= 0) abort(); + FILE *new_output_file = fdopen(output_fd, "w"); + if (!new_output_file) abort(); + if (!__sanitizer_set_report_fd) return; + __sanitizer_set_report_fd((void *)(long int)output_fd); + discard_output(output_fileno); + +} + +// Close stdout and/or stderr if user asks for it. +static void maybe_close_fd_mask() { + + char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); + if (!fd_mask_str) return; + int fd_mask = atoi(fd_mask_str); + if (fd_mask & 2) dup_and_close_stderr(); + if (fd_mask & 1) close_stdout(); + +} + +// Define LLVMFuzzerMutate to avoid link failures for targets that use it +// with libFuzzer's LLVMFuzzerCustomMutator. +size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) { + + // assert(false && "LLVMFuzzerMutate should not be called from afl_driver"); + return 0; + +} + +// Execute any files provided as parameters. +static int ExecuteFilesOnyByOne(int argc, char **argv) { + + unsigned char *buf = malloc(MAX_FILE); + for (int i = 1; i < argc; i++) { + + int fd = open(argv[i], O_RDONLY); + if (fd == -1) continue; + ssize_t length = read(fd, buf, MAX_FILE); + if (length > 0) { + + printf("Reading %zu bytes from %s\n", length, argv[i]); + LLVMFuzzerTestOneInput(buf, length); + printf("Execution successful.\n"); + + } + + } + + free(buf); + return 0; + +} + +int main(int argc, char **argv) { + + printf( + "======================= INFO =========================\n" + "This binary is built for afl++.\n" + "To run the target function on individual input(s) execute this:\n" + " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" + "To fuzz with afl-fuzz execute this:\n" + " afl-fuzz [afl-flags] -- %s [-N]\n" + "afl-fuzz will run N iterations before re-spawning the process (default: " + "1000)\n" + "======================================================\n", + argv[0], argv[0]); + + output_file = stderr; + maybe_duplicate_stderr(); + maybe_close_fd_mask(); + if (LLVMFuzzerInitialize) { + + fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); + LLVMFuzzerInitialize(&argc, &argv); + fprintf(stderr, "continue...\n"); + + } + + // Do any other expensive one-time initialization here. + + uint8_t dummy_input[64] = {0}; + memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT)); + memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR, + sizeof(AFL_DEFER_FORKSVR)); + int N = INT_MAX; + if (argc == 2 && argv[1][0] == '-') + N = atoi(argv[1] + 1); + else if (argc == 2 && (N = atoi(argv[1])) > 0) + printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); + else if (argc > 1) { + + __afl_sharedmem_fuzzing = 0; + __afl_manual_init(); + return ExecuteFilesOnyByOne(argc, argv); + + } + + assert(N > 0); + + // if (!getenv("AFL_DRIVER_DONT_DEFER")) + __afl_manual_init(); + + // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization + // on the first execution of LLVMFuzzerTestOneInput is ignored. + LLVMFuzzerTestOneInput(dummy_input, 1); + + int num_runs = 0; + while (__afl_persistent_loop(N)) { + +#ifdef _DEBUG + fprintf(stderr, "CLIENT crc: %016llx len: %u\n", + hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705), + *__afl_fuzz_len); + fprintf(stderr, "RECV:"); + for (int i = 0; i < *__afl_fuzz_len; i++) + fprintf(stderr, "%02x", __afl_fuzz_ptr[i]); + fprintf(stderr, "\n"); +#endif + if (*__afl_fuzz_len) { + + num_runs++; + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + + } + + printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); + +} + diff --git a/utils/aflpp_driver/aflpp_driver_test.c b/utils/aflpp_driver/aflpp_driver_test.c new file mode 100644 index 00000000..b4ff6bc6 --- /dev/null +++ b/utils/aflpp_driver/aflpp_driver_test.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "hash.h" + +void __attribute__((noinline)) crashme(const uint8_t *Data, size_t Size) { + + if (Size < 5) return; + + if (Data[0] == 'F') + if (Data[1] == 'A') + if (Data[2] == '$') + if (Data[3] == '$') + if (Data[4] == '$') abort(); + +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + + if (Size) + fprintf(stderr, "FUNC crc: %016llx len: %lu\n", + hash64((u8 *)Data, (unsigned int)Size, + (unsigned long long int)0xa5b35705), + Size); + + crashme(Data, Size); + + return 0; + +} + diff --git a/utils/aflpp_driver/aflpp_qemu_driver.c b/utils/aflpp_driver/aflpp_qemu_driver.c new file mode 100644 index 00000000..4f3e5f71 --- /dev/null +++ b/utils/aflpp_driver/aflpp_qemu_driver.c @@ -0,0 +1,38 @@ +#include +#include +#include + +// libFuzzer interface is thin, so we don't include any libFuzzer headers. +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv); + +static const size_t kMaxAflInputSize = 1 * 1024 * 1024; +static uint8_t AflInputBuf[kMaxAflInputSize]; + +void __attribute__((noinline)) afl_qemu_driver_stdin_input(void) { + + size_t l = read(0, AflInputBuf, kMaxAflInputSize); + LLVMFuzzerTestOneInput(AflInputBuf, l); + +} + +int main(int argc, char **argv) { + + if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); + // Do any other expensive one-time initialization here. + + if (getenv("AFL_QEMU_DRIVER_NO_HOOK")) { + + afl_qemu_driver_stdin_input(); + + } else { + + uint8_t dummy_input[1024000] = {0}; + LLVMFuzzerTestOneInput(dummy_input, 1); + + } + + return 0; + +} + diff --git a/utils/aflpp_driver/aflpp_qemu_driver_hook.c b/utils/aflpp_driver/aflpp_qemu_driver_hook.c new file mode 100644 index 00000000..823cc42d --- /dev/null +++ b/utils/aflpp_driver/aflpp_qemu_driver_hook.c @@ -0,0 +1,22 @@ +#include +#include + +#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) + +#define REGS_RDI 7 +#define REGS_RSI 6 + +void afl_persistent_hook(uint64_t *regs, uint64_t guest_base, + uint8_t *input_buf, uint32_t input_len) { + + memcpy(g2h(regs[REGS_RDI]), input_buf, input_len); + regs[REGS_RSI] = input_len; + +} + +int afl_persistent_hook_init(void) { + + return 1; + +} + diff --git a/utils/analysis_scripts/queue2csv.sh b/utils/analysis_scripts/queue2csv.sh new file mode 100755 index 00000000..2528b438 --- /dev/null +++ b/utils/analysis_scripts/queue2csv.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +test -z "$1" -o -z "$2" -o "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" -o '!' -d "$1" && { + echo "Syntax: [-n] $0 out-directory file.csv [\"tools/target --opt @@\"]" + echo Option -n will suppress the CSV header. + echo If the target execution command is supplied then also edge coverage is gathered. + exit 1 +} + +function getval() { + VAL="" + if [ "$file" != "${file/$1/}" ]; then + TMP="${file/*$1:/}" + VAL="${TMP/,*/}" + fi +} + +SKIP= +if [ "$1" = "-n" ]; then + SKIP=1 + shift +fi + +test -n "$4" && { echo "Error: too many commandline options. Target command and options including @@ have to be passed within \"\"!"; exit 1; } + +test -d "$1"/queue && OUT="$1/queue" || OUT="$1" + +OK=`ls $OUT/id:000000,time:0,orig:* 2> /dev/null` +if [ -n "$OK" ]; then + LISTCMD="ls $OUT/id:"* +else + LISTCMD="ls -tr $OUT/" +fi + +ID=;SRC=;TIME=;OP=;POS=;REP=;EDGES=;EDGES_TOTAL=; +DIR="$OUT/../stats" +rm -rf "$DIR" +> "$2" || exit 1 +mkdir "$DIR" || exit 1 +> "$DIR/../edges.txt" || exit 1 + +{ + + if [ -z "$SKIP" ]; then + echo "time;\"filename\";id;src;new_cov;edges;total_edges;\"op\";pos;rep;unique_edges" + fi + + $LISTCMD | grep -v ,sync: | sed 's/.*id:/id:/g' | while read file; do + + if [ -n "$3" ]; then + + TMP=${3/@@/$OUT/$file} + + if [ "$TMP" = "$3" ]; then + + cat "$OUT/$file" | afl-showmap -o "$DIR/$file" -q -- $3 >/dev/null 2>&1 + + else + + afl-showmap -o "$DIR/$file" -q -- $TMP >/dev/null 2>&1 + + fi + + { cat "$DIR/$file" | sed 's/:.*//' ; cat "$DIR/../edges.txt" ; } | sort -nu > $DIR/../edges.txt.tmp + mv $DIR/../edges.txt.tmp $DIR/../edges.txt + EDGES=$(cat "$DIR/$file" | wc -l) + EDGES_TOTAL=$(cat "$DIR/../edges.txt" | wc -l) + + fi + + getval id; ID="$VAL" + getval src; SRC="$VAL" + getval time; TIME="$VAL" + getval op; OP="$VAL" + getval pos; POS="$VAL" + getval rep; REP="$VAL" + if [ "$file" != "${file/+cov/}" ]; then + COV=1 + else + COV="" + fi + + if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then + echo "$TIME;\"$file\";$ID;$SRC;$COV;$EDGES;$EDGES_TOTAL;\"$OP\";$POS;$REP;UNIQUE$file" + else + echo "$TIME;\"$file\";$ID;$SRC;$COV;;;\"$OP\";$POS;$REP;" + fi + + done + +} | tee "$DIR/../queue.csv" > "$2" || exit 1 + +if [ -n "$3" -a -s "$DIR/../edges.txt" ]; then + + cat "$DIR/"* | sed 's/:.*//' | sort -n | uniq -c | egrep '^[ \t]*1 ' | awk '{print$2}' > $DIR/../unique.txt + + if [ -s "$DIR/../unique.txt" ]; then + + ls "$DIR/id:"* | grep -v ",sync:" |sed 's/.*\/id:/id:/g' | while read file; do + + CNT=$(sed 's/:.*//' "$DIR/$file" | tee "$DIR/../tmp.txt" | wc -l) + DIFF=$(diff -u "$DIR/../tmp.txt" "$DIR/../unique.txt" | egrep '^-[0-9]' | wc -l) + UNIQUE=$(($CNT - $DIFF)) + sed -i "s/;UNIQUE$file/;$UNIQUE/" "$DIR/../queue.csv" "$2" + + done + + rm -f "$DIR/../tmp.txt" + + else + + sed -i 's/;UNIQUE.*/;/' "$DIR/../queue.csv" "$2" + + fi + +fi + +mv "$DIR/../queue.csv" "$DIR/queue.csv" +if [ -e "$DIR/../edges.txt" ]; then mv "$DIR/../edges.txt" "$DIR/edges.txt"; fi +if [ -e "$DIR/../unique.txt" ]; then mv "$DIR/../unique.txt" "$DIR/unique.txt"; fi + +echo "Created $2" diff --git a/utils/argv_fuzzing/Makefile b/utils/argv_fuzzing/Makefile new file mode 100644 index 00000000..5a0ac6e6 --- /dev/null +++ b/utils/argv_fuzzing/Makefile @@ -0,0 +1,58 @@ +# +# american fuzzy lop++ - argvfuzz +# -------------------------------- +# +# Copyright 2019-2020 Kjell Braden +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) + +all: argvfuzz32.so argvfuzz64.so + +argvfuzz32.so: argvfuzz.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz32 build failure (that's fine)" + +argvfuzz64.so: argvfuzz.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "argvfuzz64 build failure (that's fine)" + +install: argvfuzz32.so argvfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f argvfuzz64.so ]; then set -e; install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi + +clean: + rm -f argvfuzz32.so argvfuzz64.so diff --git a/utils/argv_fuzzing/README.md b/utils/argv_fuzzing/README.md new file mode 100644 index 00000000..fa8cad80 --- /dev/null +++ b/utils/argv_fuzzing/README.md @@ -0,0 +1,16 @@ +# argvfuzz + +afl supports fuzzing file inputs or stdin. When source is available, +`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. + +`argvfuzz` tries to provide the same functionality for binaries. When loaded +using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace +argv using the same logic of `argv-fuzz-inl.h`. + +A few conditions need to be fulfilled for this mechanism to work correctly: + +1. As it relies on hooking the loader, it cannot work on static binaries. +2. If the target binary does not use the default libc's `_start` implementation + (crt1.o), the hook may not run. +3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the + target binary expects argv to be living on the stack, things may go wrong. diff --git a/utils/argv_fuzzing/argv-fuzz-inl.h b/utils/argv_fuzzing/argv-fuzz-inl.h new file mode 100644 index 00000000..c15c0271 --- /dev/null +++ b/utils/argv_fuzzing/argv-fuzz-inl.h @@ -0,0 +1,90 @@ +/* + american fuzzy lop++ - sample argv fuzzing wrapper + ------------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file shows a simple way to fuzz command-line parameters with stock + afl-fuzz. To use, add: + + #include "/path/to/argv-fuzz-inl.h" + + ...to the file containing main(), ideally placing it after all the + standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of + main(). + + This will cause the program to read NUL-delimited input from stdin and + put it in argv[]. Two subsequent NULs terminate the array. Empty + params are encoded as a lone 0x02. Lone 0x02 can't be generated, but + that shouldn't matter in real life. + + If you would like to always preserve argv[0], use this instead: + AFL_INIT_SET0("prog_name"); + +*/ + +#ifndef _HAVE_ARGV_FUZZ_INL +#define _HAVE_ARGV_FUZZ_INL + +#include + +#define AFL_INIT_ARGV() \ + do { \ + \ + argv = afl_init_argv(&argc); \ + \ + } while (0) + +#define AFL_INIT_SET0(_p) \ + do { \ + \ + argv = afl_init_argv(&argc); \ + argv[0] = (_p); \ + if (!argc) argc = 1; \ + \ + } while (0) + +#define MAX_CMDLINE_LEN 100000 +#define MAX_CMDLINE_PAR 50000 + +static char **afl_init_argv(int *argc) { + + static char in_buf[MAX_CMDLINE_LEN]; + static char *ret[MAX_CMDLINE_PAR]; + + char *ptr = in_buf; + int rc = 0; + + if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} + + while (*ptr && rc < MAX_CMDLINE_PAR) { + + ret[rc] = ptr; + if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; + rc++; + + while (*ptr) + ptr++; + ptr++; + + } + + *argc = rc; + + return ret; + +} + +#undef MAX_CMDLINE_LEN +#undef MAX_CMDLINE_PAR + +#endif /* !_HAVE_ARGV_FUZZ_INL */ + diff --git a/utils/argv_fuzzing/argvfuzz.c b/utils/argv_fuzzing/argvfuzz.c new file mode 100644 index 00000000..4251ca4c --- /dev/null +++ b/utils/argv_fuzzing/argvfuzz.c @@ -0,0 +1,49 @@ +/* + american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries + ------------------------------------------------------------ + + Copyright 2019-2020 Kjell Braden + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define _GNU_SOURCE /* for RTLD_NEXT */ +#include +#include +#include +#include +#include "argv-fuzz-inl.h" + +int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), + void (*rtld_fini)(void), void *stack_end) { + + int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), + void *stack_end); + int sub_argc; + char **sub_argv; + + (void)argc; + (void)argv; + + orig = dlsym(RTLD_NEXT, __func__); + + if (!orig) { + + fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); + exit(EXIT_FAILURE); + + } + + sub_argv = afl_init_argv(&sub_argc); + + return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); + +} + diff --git a/utils/asan_cgroups/limit_memory.sh b/utils/asan_cgroups/limit_memory.sh new file mode 100755 index 00000000..1f0f04ad --- /dev/null +++ b/utils/asan_cgroups/limit_memory.sh @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# +# american fuzzy lop++ - limit memory using cgroups +# ----------------------------------------------- +# +# Written by Samir Khakimov and +# David A. Wheeler +# +# Edits to bring the script in line with afl-cmin and other companion scripts +# by Michal Zalewski. All bugs are my fault. +# +# Copyright 2015 Institute for Defense Analyses. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This tool allows the amount of actual memory allocated to a program +# to be limited on Linux systems using cgroups, instead of the traditional +# setrlimit() API. This helps avoid the address space problems discussed in +# docs/notes_for_asan.md. +# +# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some +# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed +# task. +# + +echo "cgroup tool for afl-fuzz by and " +echo + +unset NEW_USER +MEM_LIMIT="50" + +while getopts "+u:m:" opt; do + + case "$opt" in + + "u") + NEW_USER="$OPTARG" + ;; + + "m") + MEM_LIMIT="$[OPTARG]" + ;; + + "?") + exit 1 + ;; + + esac + +done + +if [ "$MEM_LIMIT" -lt "5" ]; then + echo "[-] Error: malformed or dangerously low value of -m." 1>&2 + exit 1 +fi + +shift $((OPTIND-1)) + +TARGET_BIN="$1" + +if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then + + cat 1>&2 <<_EOF_ +Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] + +Required parameters: + + -u user - run the fuzzer as a specific user after setting up limits + +Optional parameters: + + -m megs - set memory limit to a specified value ($MEM_LIMIT MB) + +This tool configures cgroups-based memory limits for a fuzzing job to simplify +the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in +conjunction with '-m none' passed to the afl-fuzz binary itself, say: + + $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target + +_EOF_ + + exit 1 + +fi + +# Basic sanity checks + +if [ ! "`uname -s`" = "Linux" ]; then + echo "[-] Error: this tool does not support non-Linux systems." 1>&2 + exit 1 +fi + +if [ ! "`id -u`" = "0" ]; then + echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 + exit 1 +fi + +if ! type cgcreate 2>/dev/null 1>&2; then + + echo "[-] Error: you need to install cgroup tools first." 1>&2 + + if type apt-get 2>/dev/null 1>&2; then + echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 + elif type yum 2>/dev/null 1>&2; then + echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 + fi + + exit 1 + +fi + +if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then + echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 + exit 1 +fi + +# Create a new cgroup path if necessary... We used PID-keyed groups to keep +# parallel afl-fuzz tasks separate from each other. + +CID="afl-$NEW_USER-$$" + +CPATH="/sys/fs/cgroup/memory/$CID" + +if [ ! -d "$CPATH" ]; then + + cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 + +fi + +# Set the appropriate limit... + +if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null + echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +elif grep -qE 'partition|file' /proc/swaps; then + + echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 + exit 1 + +else + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +fi + +# All right. At this point, we can just run the command. + +cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" + +cgdelete -g "memory:$CID" diff --git a/utils/bash_shellshock/shellshock-fuzz.diff b/utils/bash_shellshock/shellshock-fuzz.diff new file mode 100644 index 00000000..3fa05bf8 --- /dev/null +++ b/utils/bash_shellshock/shellshock-fuzz.diff @@ -0,0 +1,59 @@ +This patch shows a very simple way to find post-Shellshock bugs in bash, as +discussed here: + + http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html + +In essence, it shows a way to fuzz environmental variables. Instructions: + +1) Download bash 4.3, apply this patch, compile with: + + CC=/path/to/afl-gcc ./configure + make clean all + + Note that the harness puts the fuzzed output in $TEST_VARIABLE. With + Florian's Shellshock patch (bash43-028), this is no longer passed down + to the parser. + +2) Create and cd to an empty directory, put the compiled bash binary in + there, and run these commands: + + mkdir in_dir + echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt + +3) Run the fuzzer with: + + /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : + + The -d parameter is advisable only if the tested shell is fairly slow + or if you are in a hurry; will cover more ground faster, but + less systematically. + +4) Watch for crashes in out_dir/crashes/. Also watch for any new files + created in cwd if you're interested in non-crash RCEs (files will be + created whenever the shell executes "foo>bar" or something like + that). You can correlate their creation date with new entries in + out_dir/queue/. + + You can also modify the bash binary to directly check for more subtle + fault conditions, or use the synthesized entries in out_dir/queue/ + as a seed for other, possibly slower or more involved testing regimes. + + Expect several hours to get decent coverage. + +--- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 ++++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 +@@ -371,6 +371,14 @@ + env = environ; + #endif /* __OPENNT */ + ++ { ++ ++ static char val[1024 * 16]; ++ read(0, val, sizeof(val) - 1); ++ setenv("TEST_VARIABLE", val, 1); ++ ++ } ++ + USE_VAR(argc); + USE_VAR(argv); + USE_VAR(env); diff --git a/utils/canvas_harness/canvas_harness.html b/utils/canvas_harness/canvas_harness.html new file mode 100644 index 00000000..a37b6937 --- /dev/null +++ b/utils/canvas_harness/canvas_harness.html @@ -0,0 +1,170 @@ + + + + + +
    + +
    + + + +

    Results

    + +
      + + + + diff --git a/utils/clang_asm_normalize/as b/utils/clang_asm_normalize/as new file mode 100755 index 00000000..45537cae --- /dev/null +++ b/utils/clang_asm_normalize/as @@ -0,0 +1,75 @@ +#!/bin/sh +# +# american fuzzy lop++ - clang assembly normalizer +# ---------------------------------------------- +# +# Originally written by Michal Zalewski +# The idea for this wrapper comes from Ryan Govostes. +# +# Copyright 2013, 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This 'as' wrapper should allow you to instrument unruly, hand-written +# assembly with afl-as. +# +# Usage: +# +# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ +# AFL_PATH=/path/to/this/directory/ make clean all + +if [ "$#" -lt "2" ]; then + echo "[-] Error: this utility can't be called directly." 1>&2 + exit 1 +fi + +if [ "$AFL_REAL_PATH" = "" ]; then + echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 + exit 1 +fi + +if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then + echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 + exit 1 +fi + +unset __AFL_AS_CMDLINE __AFL_FNAME + +while [ ! "$#" = "0" ]; do + + if [ "$#" = "1" ]; then + __AFL_FNAME="$1" + else + __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" + fi + + shift + +done + +test "$TMPDIR" = "" && TMPDIR=/tmp + +TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` + +test "$TMPFILE" = "" && exit 1 + +clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" + +ERR="$?" + +if [ ! "$ERR" = "0" ]; then + rm -f "$TMPFILE" + exit $ERR +fi + +"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" + +ERR="$?" + +rm -f "$TMPFILE" + +exit "$ERR" diff --git a/utils/crash_triage/triage_crashes.sh b/utils/crash_triage/triage_crashes.sh new file mode 100755 index 00000000..bf763cba --- /dev/null +++ b/utils/crash_triage/triage_crashes.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# +# american fuzzy lop++ - crash triage utility +# ----------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Note that this assumes that the targeted application reads from stdin +# and requires no other cmdline parameters. Modify as needed if this is +# not the case. +# +# Note that on OpenBSD, you may need to install a newer version of gdb +# (e.g., from ports). You can set GDB=/some/path to point to it if +# necessary. +# + +echo "crash triage utility for afl-fuzz by Michal Zalewski" +echo + +ulimit -v 100000 2>/dev/null +ulimit -d 100000 2>/dev/null + +if [ "$#" -lt "2" ]; then + echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 + echo 1>&2 + exit 1 +fi + +DIR="$1" +BIN="$2" +shift +shift + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + echo "$DIR" | grep -qE '^(/var)?/tmp/' + T1="$?" + + echo "$BIN" | grep -qE '^(/var)?/tmp/' + T2="$?" + + if [ "$T1" = "0" -o "$T2" = "0" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +if + [ "$GDB" = "" ]; then + GDB=gdb +fi + +if [ ! -f "$BIN" -o ! -x "$BIN" ]; then + echo "[-] Error: binary '$2' not found or is not executable." 1>&2 + exit 1 +fi + +if [ ! -d "$DIR/queue" ]; then + echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 + exit 1 +fi + +CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) + +if [ "$CCOUNT" = "0" ]; then + echo "No crashes recorded in the target directory - nothing to be done." + exit 0 +fi + +echo + +for crash in $DIR/crashes/id:*; do + + id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` + sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` + + # Grab the args, converting @@ to $crash + + use_args="" + use_stdio=1 + + for a in $@; do + + if [ "$a" = "@@" ] ; then + use_args="$use_args $crash" + unset use_stdio + else + use_args="$use_args $a" + fi + + done + + # Strip the trailing space + use_args="${use_args# }" + + echo "+++ ID $id, SIGNAL $sig +++" + echo + + if [ "$use_stdio" = "1" ]; then + $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0 + +#define INITIAL_GROWTH_SIZE (64) + +#define RAND_BELOW(limit) (rand() % (limit)) + +/* Use in a struct: creates a name_buf and a name_size variable. */ +#define BUF_VAR(type, name) \ + type * name##_buf; \ + size_t name##_size; +/* this fills in `&structptr->something_buf, &structptr->something_size`. */ +#define BUF_PARAMS(struct, name) \ + (void **)&struct->name##_buf, &struct->name##_size + +typedef struct { + +} afl_t; + +static void surgical_havoc_mutate(u8 *out_buf, s32 begin, s32 end) { + + static s8 interesting_8[] = {INTERESTING_8}; + static s16 interesting_16[] = {INTERESTING_8, INTERESTING_16}; + static s32 interesting_32[] = {INTERESTING_8, INTERESTING_16, INTERESTING_32}; + + switch (RAND_BELOW(12)) { + + case 0: { + + /* Flip a single bit somewhere. Spooky! */ + + s32 bit_idx = ((RAND_BELOW(end - begin) + begin) << 3) + RAND_BELOW(8); + + out_buf[bit_idx >> 3] ^= 128 >> (bit_idx & 7); + + break; + + } + + case 1: { + + /* Set byte to interesting value. */ + + u8 val = interesting_8[RAND_BELOW(sizeof(interesting_8))]; + out_buf[(RAND_BELOW(end - begin) + begin)] = val; + + break; + + } + + case 2: { + + /* Set word to interesting value, randomly choosing endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u16 *)(out_buf + byte_idx) = + interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]; + break; + case 1: + *(u16 *)(out_buf + byte_idx) = + SWAP16(interesting_16[RAND_BELOW(sizeof(interesting_16) >> 1)]); + break; + + } + + break; + + } + + case 3: { + + /* Set dword to interesting value, randomly choosing endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u32 *)(out_buf + byte_idx) = + interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; + break; + case 1: + *(u32 *)(out_buf + byte_idx) = + SWAP32(interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); + break; + + } + + break; + + } + + case 4: { + + /* Set qword to interesting value, randomly choosing endian. */ + + if (end - begin < 8) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 7) break; + + switch (RAND_BELOW(2)) { + + case 0: + *(u64 *)(out_buf + byte_idx) = + (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]; + break; + case 1: + *(u64 *)(out_buf + byte_idx) = SWAP64( + (s64)interesting_32[RAND_BELOW(sizeof(interesting_32) >> 2)]); + break; + + } + + break; + + } + + case 5: { + + /* Randomly subtract from byte. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] -= 1 + RAND_BELOW(ARITH_MAX); + + break; + + } + + case 6: { + + /* Randomly add to byte. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] += 1 + RAND_BELOW(ARITH_MAX); + + break; + + } + + case 7: { + + /* Randomly subtract from word, random endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + if (RAND_BELOW(2)) { + + *(u16 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u16 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u16 *)(out_buf + byte_idx) = + SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) - num); + + } + + break; + + } + + case 8: { + + /* Randomly add to word, random endian. */ + + if (end - begin < 2) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 1) break; + + if (RAND_BELOW(2)) { + + *(u16 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u16 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u16 *)(out_buf + byte_idx) = + SWAP16(SWAP16(*(u16 *)(out_buf + byte_idx)) + num); + + } + + break; + + } + + case 9: { + + /* Randomly subtract from dword, random endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + if (RAND_BELOW(2)) { + + *(u32 *)(out_buf + byte_idx) -= 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u32 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u32 *)(out_buf + byte_idx) = + SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) - num); + + } + + break; + + } + + case 10: { + + /* Randomly add to dword, random endian. */ + + if (end - begin < 4) break; + + s32 byte_idx = (RAND_BELOW(end - begin) + begin); + + if (byte_idx >= end - 3) break; + + if (RAND_BELOW(2)) { + + *(u32 *)(out_buf + byte_idx) += 1 + RAND_BELOW(ARITH_MAX); + + } else { + + u32 num = 1 + RAND_BELOW(ARITH_MAX); + + *(u32 *)(out_buf + byte_idx) = + SWAP32(SWAP32(*(u32 *)(out_buf + byte_idx)) + num); + + } + + break; + + } + + case 11: { + + /* Just set a random byte to a random value. Because, + why not. We use XOR with 1-255 to eliminate the + possibility of a no-op. */ + + out_buf[(RAND_BELOW(end - begin) + begin)] ^= 1 + RAND_BELOW(255); + + break; + + } + + } + +} + +/* This function calculates the next power of 2 greater or equal its argument. + @return The rounded up power of 2 (if no overflow) or 0 on overflow. +*/ +static inline size_t next_pow2(size_t in) { + + if (in == 0 || in > (size_t)-1) + return 0; /* avoid undefined behaviour under-/overflow */ + size_t out = in - 1; + out |= out >> 1; + out |= out >> 2; + out |= out >> 4; + out |= out >> 8; + out |= out >> 16; + return out + 1; + +} + +/* This function makes sure *size is > size_needed after call. + It will realloc *buf otherwise. + *size will grow exponentially as per: + https://blog.mozilla.org/nnethercote/2014/11/04/please-grow-your-buffers-exponentially/ + Will return NULL and free *buf if size_needed is <1 or realloc failed. + @return For convenience, this function returns *buf. + */ +static inline void *maybe_grow(void **buf, size_t *size, size_t size_needed) { + + /* No need to realloc */ + if (likely(size_needed && *size >= size_needed)) return *buf; + + /* No initial size was set */ + if (size_needed < INITIAL_GROWTH_SIZE) size_needed = INITIAL_GROWTH_SIZE; + + /* grow exponentially */ + size_t next_size = next_pow2(size_needed); + + /* handle overflow */ + if (!next_size) { next_size = size_needed; } + + /* alloc */ + *buf = realloc(*buf, next_size); + *size = *buf ? next_size : 0; + + return *buf; + +} + +/* Swaps buf1 ptr and buf2 ptr, as well as their sizes */ +static inline void afl_swap_bufs(void **buf1, size_t *size1, void **buf2, + size_t *size2) { + + void * scratch_buf = *buf1; + size_t scratch_size = *size1; + *buf1 = *buf2; + *size1 = *size2; + *buf2 = scratch_buf; + *size2 = scratch_size; + +} + +#undef INITIAL_GROWTH_SIZE + +#endif + diff --git a/utils/custom_mutators/example.c b/utils/custom_mutators/example.c new file mode 100644 index 00000000..23add128 --- /dev/null +++ b/utils/custom_mutators/example.c @@ -0,0 +1,376 @@ +/* + New Custom Mutator for AFL++ + Written by Khaled Yakdan + Andrea Fioraldi + Shengtuo Hu + Dominik Maier +*/ + +// You need to use -I /path/to/AFLplusplus/include +#include "custom_mutator_helpers.h" + +#include +#include +#include +#include + +#define DATA_SIZE (100) + +static const char *commands[] = { + + "GET", + "PUT", + "DEL", + +}; + +typedef struct my_mutator { + + afl_t *afl; + + // any additional data here! + size_t trim_size_current; + int trimmming_steps; + int cur_step; + + // Reused buffers: + BUF_VAR(u8, fuzz); + BUF_VAR(u8, data); + BUF_VAR(u8, havoc); + BUF_VAR(u8, trim); + BUF_VAR(u8, post_process); + +} my_mutator_t; + +/** + * Initialize this custom mutator + * + * @param[in] afl a pointer to the internal state object. Can be ignored for + * now. + * @param[in] seed A seed for this mutator - the same seed should always mutate + * in the same way. + * @return Pointer to the data object this custom mutator instance should use. + * There may be multiple instances of this mutator in one afl-fuzz run! + * Return NULL on error. + */ +my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { + + srand(seed); // needed also by surgical_havoc_mutate() + + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + data->afl = afl; + + return data; + +} + +/** + * Perform custom mutations on a given input + * + * (Optional for now. Required in the future) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Pointer to input data to be mutated + * @param[in] buf_size Size of input data + * @param[out] out_buf the buffer we will work on. we can reuse *buf. NULL on + * error. + * @param[in] add_buf Buffer containing the additional test case + * @param[in] add_buf_size Size of the additional test case + * @param[in] max_size Maximum size of the mutated output. The mutation must not + * produce data larger than max_size. + * @return Size of the mutated output. + */ +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, + u8 **out_buf, uint8_t *add_buf, + size_t add_buf_size, // add_buf can be NULL + size_t max_size) { + + // Make sure that the packet size does not exceed the maximum size expected by + // the fuzzer + size_t mutated_size = DATA_SIZE <= max_size ? DATA_SIZE : max_size; + + // maybe_grow is optimized to be quick for reused buffers. + u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), mutated_size); + if (!mutated_out) { + + *out_buf = NULL; + perror("custom mutator allocation (maybe_grow)"); + return 0; /* afl-fuzz will very likely error out after this. */ + + } + + // Randomly select a command string to add as a header to the packet + memcpy(mutated_out, commands[rand() % 3], 3); + + // Mutate the payload of the packet + int i; + for (i = 0; i < 8; ++i) { + + // Randomly perform one of the (no len modification) havoc mutations + surgical_havoc_mutate(mutated_out, 3, mutated_size); + + } + + *out_buf = mutated_out; + return mutated_size; + +} + +/** + * A post-processing function to use right before AFL writes the test case to + * disk in order to execute the target. + * + * (Optional) If this functionality is not needed, simply don't define this + * function. + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Buffer containing the test case to be executed + * @param[in] buf_size Size of the test case + * @param[out] out_buf Pointer to the buffer containing the test case after + * processing. External library should allocate memory for out_buf. + * The buf pointer may be reused (up to the given buf_size); + * @return Size of the output buffer after processing or the needed amount. + * A return of 0 indicates an error. + */ +size_t afl_custom_post_process(my_mutator_t *data, uint8_t *buf, + size_t buf_size, uint8_t **out_buf) { + + uint8_t *post_process_buf = + maybe_grow(BUF_PARAMS(data, post_process), buf_size + 5); + if (!post_process_buf) { + + perror("custom mutator realloc failed."); + *out_buf = NULL; + return 0; + + } + + memcpy(post_process_buf + 5, buf, buf_size); + post_process_buf[0] = 'A'; + post_process_buf[1] = 'F'; + post_process_buf[2] = 'L'; + post_process_buf[3] = '+'; + post_process_buf[4] = '+'; + + *out_buf = post_process_buf; + + return buf_size + 5; + +} + +/** + * This method is called at the start of each trimming operation and receives + * the initial buffer. It should return the amount of iteration steps possible + * on this input (e.g. if your input has n elements and you want to remove + * them one by one, return n, if you do a binary search, return log(n), + * and so on...). + * + * If your trimming algorithm doesn't allow you to determine the amount of + * (remaining) steps easily (esp. while running), then you can alternatively + * return 1 here and always return 0 in post_trim until you are finished and + * no steps remain. In that case, returning 1 in post_trim will end the + * trimming routine. The whole current index/max iterations stuff is only used + * to show progress. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param buf Buffer containing the test case + * @param buf_size Size of the test case + * @return The amount of possible iteration steps to trim the input. + * negative on error. + */ +int32_t afl_custom_init_trim(my_mutator_t *data, uint8_t *buf, + size_t buf_size) { + + // We simply trim once + data->trimmming_steps = 1; + + data->cur_step = 0; + + if (!maybe_grow(BUF_PARAMS(data, trim), buf_size)) { + + perror("init_trim grow"); + return -1; + + } + + memcpy(data->trim_buf, buf, buf_size); + + data->trim_size_current = buf_size; + + return data->trimmming_steps; + +} + +/** + * This method is called for each trimming operation. It doesn't have any + * arguments because we already have the initial buffer from init_trim and we + * can memorize the current state in *data. This can also save + * reparsing steps for each iteration. It should return the trimmed input + * buffer, where the returned data must not exceed the initial input data in + * length. Returning anything that is larger than the original data (passed + * to init_trim) will result in a fatal abort of AFLFuzz. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[out] out_buf Pointer to the buffer containing the trimmed test case. + * External library should allocate memory for out_buf. + * AFL++ will not release the memory after saving the test case. + * Keep a ref in *data. + * *out_buf = NULL is treated as error. + * @return Pointer to the size of the trimmed test case + */ +size_t afl_custom_trim(my_mutator_t *data, uint8_t **out_buf) { + + *out_buf = data->trim_buf; + + // Remove the last byte of the trimming input + return data->trim_size_current - 1; + +} + +/** + * This method is called after each trim operation to inform you if your + * trimming step was successful or not (in terms of coverage). If you receive + * a failure here, you should reset your input to the last known good state. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param success Indicates if the last trim operation was successful. + * @return The next trim iteration index (from 0 to the maximum amount of + * steps returned in init_trim). negative ret on failure. + */ +int32_t afl_custom_post_trim(my_mutator_t *data, int success) { + + if (success) { + + ++data->cur_step; + return data->cur_step; + + } + + return data->trimmming_steps; + +} + +/** + * Perform a single custom mutation on a given input. + * This mutation is stacked with the other muatations in havoc. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] buf Pointer to the input data to be mutated and the mutated + * output + * @param[in] buf_size Size of input data + * @param[out] out_buf The output buffer. buf can be reused, if the content + * fits. *out_buf = NULL is treated as error. + * @param[in] max_size Maximum size of the mutated output. The mutation must + * not produce data larger than max_size. + * @return Size of the mutated output. + */ +size_t afl_custom_havoc_mutation(my_mutator_t *data, u8 *buf, size_t buf_size, + u8 **out_buf, size_t max_size) { + + if (buf_size == 0) { + + *out_buf = maybe_grow(BUF_PARAMS(data, havoc), 1); + if (!*out_buf) { + + perror("custom havoc: maybe_grow"); + return 0; + + } + + **out_buf = rand() % 256; + buf_size = 1; + + } else { + + // We reuse buf here. It's legal and faster. + *out_buf = buf; + + } + + size_t victim = rand() % buf_size; + (*out_buf)[victim] += rand() % 10; + + return buf_size; + +} + +/** + * Return the probability (in percentage) that afl_custom_havoc_mutation + * is called in havoc. By default it is 6 %. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @return The probability (0-100). + */ +uint8_t afl_custom_havoc_mutation_probability(my_mutator_t *data) { + + return 5; // 5 % + +} + +/** + * Determine whether the fuzzer should fuzz the queue entry or not. + * + * (Optional) + * + * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param filename File name of the test case in the queue entry + * @return Return True(1) if the fuzzer will fuzz the queue entry, and + * False(0) otherwise. + */ +uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) { + + return 1; + +} + +/** + * Allow for additional analysis (e.g. calling a different tool that does a + * different kind of coverage and saves this for the custom mutator). + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param filename_new_queue File name of the new queue entry + * @param filename_orig_queue File name of the original queue entry + */ +void afl_custom_queue_new_entry(my_mutator_t * data, + const uint8_t *filename_new_queue, + const uint8_t *filename_orig_queue) { + + /* Additional analysis on the original or new test case */ + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->post_process_buf); + free(data->havoc_buf); + free(data->data_buf); + free(data->fuzz_buf); + free(data->trim_buf); + free(data); + +} + diff --git a/utils/custom_mutators/example.py b/utils/custom_mutators/example.py new file mode 100644 index 00000000..cf659e5a --- /dev/null +++ b/utils/custom_mutators/example.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Example Python Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + + +COMMANDS = [ + b"GET", + b"PUT", + b"DEL", + b"AAAAAAAAAAAAAAAAA", +] + + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + random.seed(seed) + + +def deinit(): + pass + + +def fuzz(buf, add_buf, max_size): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @type max_size: int + @param max_size: Maximum size of the mutated output. The mutation must not + produce data larger than max_size. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + ret = bytearray(100) + + ret[:3] = random.choice(COMMANDS) + + return ret + +# Uncomment and implement the following methods if you want to use a custom +# trimming algorithm. See also the documentation for a better API description. + +# def init_trim(buf): +# ''' +# Called per trimming iteration. +# +# @type buf: bytearray +# @param buf: The buffer that should be trimmed. +# +# @rtype: int +# @return: The maximum number of trimming steps. +# ''' +# global ... +# +# # Initialize global variables +# +# # Figure out how many trimming steps are possible. +# # If this is not possible for your trimming, you can +# # return 1 instead and always return 0 in post_trim +# # until you are done (then you return 1). +# +# return steps +# +# def trim(): +# ''' +# Called per trimming iteration. +# +# @rtype: bytearray +# @return: A new bytearray containing the trimmed data. +# ''' +# global ... +# +# # Implement the actual trimming here +# +# return bytearray(...) +# +# def post_trim(success): +# ''' +# Called after each trimming operation. +# +# @type success: bool +# @param success: Indicates if the last trim operation was successful. +# +# @rtype: int +# @return: The next trim index (0 to max number of steps) where max +# number of steps indicates the trimming is done. +# ''' +# global ... +# +# if not success: +# # Restore last known successful input, determine next index +# else: +# # Just determine the next index, based on what was successfully +# # removed in the last step +# +# return next_index +# +# def post_process(buf): +# ''' +# Called just before the execution to write the test case in the format +# expected by the target +# +# @type buf: bytearray +# @param buf: The buffer containing the test case to be executed +# +# @rtype: bytearray +# @return: The buffer containing the test case after +# ''' +# return buf +# +# def havoc_mutation(buf, max_size): +# ''' +# Perform a single custom mutation on a given input. +# +# @type buf: bytearray +# @param buf: The buffer that should be mutated. +# +# @type max_size: int +# @param max_size: Maximum size of the mutated output. The mutation must not +# produce data larger than max_size. +# +# @rtype: bytearray +# @return: A new bytearray containing the mutated data +# ''' +# return mutated_buf +# +# def havoc_mutation_probability(): +# ''' +# Called for each `havoc_mutation`. Return the probability (in percentage) +# that `havoc_mutation` is called in havoc. Be default it is 6%. +# +# @rtype: int +# @return: The probability (0-100) +# ''' +# return prob +# +# def queue_get(filename): +# ''' +# Called at the beginning of each fuzz iteration to determine whether the +# test case should be fuzzed +# +# @type filename: str +# @param filename: File name of the test case in the current queue entry +# +# @rtype: bool +# @return: Return True if the custom mutator decides to fuzz the test case, +# and False otherwise +# ''' +# return True +# +# def queue_new_entry(filename_new_queue, filename_orig_queue): +# ''' +# Called after adding a new test case to the queue +# +# @type filename_new_queue: str +# @param filename_new_queue: File name of the new queue entry +# +# @type filename_orig_queue: str +# @param filename_orig_queue: File name of the original queue entry +# ''' +# pass diff --git a/utils/custom_mutators/post_library_gif.so.c b/utils/custom_mutators/post_library_gif.so.c new file mode 100644 index 00000000..ac10f409 --- /dev/null +++ b/utils/custom_mutators/post_library_gif.so.c @@ -0,0 +1,165 @@ +/* + american fuzzy lop++ - postprocessor library example + -------------------------------------------------- + + Originally written by Michal Zalewski + Edited by Dominik Maier, 2020 + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Postprocessor libraries can be passed to afl-fuzz to perform final cleanup + of any mutated test cases - for example, to fix up checksums in PNG files. + + Please heed the following warnings: + + 1) In almost all cases, it is more productive to comment out checksum logic + in the targeted binary (as shown in ../libpng_no_checksum/). One possible + exception is the process of fuzzing binary-only software in QEMU mode. + + 2) The use of postprocessors for anything other than checksums is + questionable and may cause more harm than good. AFL is normally pretty good + about dealing with length fields, magic values, etc. + + 3) Postprocessors that do anything non-trivial must be extremely robust to + gracefully handle malformed data and other error conditions - otherwise, + they will crash and take afl-fuzz down with them. Be wary of reading past + *len and of integer overflows when calculating file offsets. + + In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, + honestly know what you're doing =) + + With that out of the way: the postprocessor library is passed to afl-fuzz + via AFL_POST_LIBRARY. The library must be compiled with: + + gcc -shared -Wall -O3 post_library.so.c -o post_library.so + + AFL will call the afl_custom_post_process() function for every mutated output + buffer. From there, you have three choices: + + 1) If you don't want to modify the test case, simply set `*out_buf = in_buf` + and return the original `len`. + + 2) If you want to skip this test case altogether and have AFL generate a + new one, return 0 or set `*out_buf = NULL`. + Use this sparingly - it's faster than running the target program + with patently useless inputs, but still wastes CPU time. + + 3) If you want to modify the test case, allocate an appropriately-sized + buffer, move the data into that buffer, make the necessary changes, and + then return the new pointer as out_buf. Return an appropriate len + afterwards. + + Note that the buffer will *not* be freed for you. To avoid memory leaks, + you need to free it or reuse it on subsequent calls (as shown below). + + *** Feel free to reuse the original 'in_buf' BUFFER and return it. *** + + Aight. The example below shows a simple postprocessor that tries to make + sure that all input files start with "GIF89a". + + PS. If you don't like C, you can try out the unix-based wrapper from + Ben Nagy instead: https://github.com/bnagy/aflfix + + */ + +#include +#include +#include + +/* Header that must be present at the beginning of every test case: */ + +#define HEADER "GIF89a" + +typedef struct post_state { + + unsigned char *buf; + size_t size; + +} post_state_t; + +void *afl_custom_init(void *afl) { + + post_state_t *state = malloc(sizeof(post_state_t)); + if (!state) { + + perror("malloc"); + return NULL; + + } + + state->buf = calloc(sizeof(unsigned char), 4096); + if (!state->buf) { + + free(state); + perror("calloc"); + return NULL; + + } + + return state; + +} + +/* The actual postprocessor routine called by afl-fuzz: */ + +size_t afl_custom_post_process(post_state_t *data, unsigned char *in_buf, + unsigned int len, unsigned char **out_buf) { + + /* Skip execution altogether for buffers shorter than 6 bytes (just to + show how it's done). We can trust len to be sane. */ + + if (len < strlen(HEADER)) return 0; + + /* Do nothing for buffers that already start with the expected header. */ + + if (!memcmp(in_buf, HEADER, strlen(HEADER))) { + + *out_buf = in_buf; + return len; + + } + + /* Allocate memory for new buffer, reusing previous allocation if + possible. */ + + *out_buf = realloc(data->buf, len); + + /* If we're out of memory, the most graceful thing to do is to return the + original buffer and give up on modifying it. Let AFL handle OOM on its + own later on. */ + + if (!*out_buf) { + + *out_buf = in_buf; + return len; + + } + + /* Copy the original data to the new location. */ + + memcpy(*out_buf, in_buf, len); + + /* Insert the new header. */ + + memcpy(*out_buf, HEADER, strlen(HEADER)); + + /* Return the new len. It hasn't changed, so it's just len. */ + + return len; + +} + +/* Gets called afterwards */ +void afl_custom_deinit(post_state_t *data) { + + free(data->buf); + free(data); + +} + diff --git a/utils/custom_mutators/post_library_png.so.c b/utils/custom_mutators/post_library_png.so.c new file mode 100644 index 00000000..941f7e55 --- /dev/null +++ b/utils/custom_mutators/post_library_png.so.c @@ -0,0 +1,163 @@ +/* + american fuzzy lop++ - postprocessor for PNG + ------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + Adapted to the new API, 2020 by Dominik Maier + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + See post_library.so.c for a general discussion of how to implement + postprocessors. This specific postprocessor attempts to fix up PNG + checksums, providing a slightly more complicated example than found + in post_library.so.c. + + Compile with: + + gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz + + */ + +#include +#include +#include +#include +#include + +#include + +/* A macro to round an integer up to 4 kB. */ + +#define UP4K(_i) ((((_i) >> 12) + 1) << 12) + +typedef struct post_state { + + unsigned char *buf; + size_t size; + +} post_state_t; + +void *afl_custom_init(void *afl) { + + post_state_t *state = malloc(sizeof(post_state_t)); + if (!state) { + + perror("malloc"); + return NULL; + + } + + state->buf = calloc(sizeof(unsigned char), 4096); + if (!state->buf) { + + free(state); + perror("calloc"); + return NULL; + + } + + return state; + +} + +size_t afl_custom_post_process(post_state_t *data, const unsigned char *in_buf, + unsigned int len, + const unsigned char **out_buf) { + + unsigned char *new_buf = (unsigned char *)in_buf; + unsigned int pos = 8; + + /* Don't do anything if there's not enough room for the PNG header + (8 bytes). */ + + if (len < 8) { + + *out_buf = in_buf; + return len; + + } + + /* Minimum size of a zero-length PNG chunk is 12 bytes; if we + don't have that, we can bail out. */ + + while (pos + 12 <= len) { + + unsigned int chunk_len, real_cksum, file_cksum; + + /* Chunk length is the first big-endian dword in the chunk. */ + + chunk_len = ntohl(*(uint32_t *)(in_buf + pos)); + + /* Bail out if chunk size is too big or goes past EOF. */ + + if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > len) break; + + /* Chunk checksum is calculated for chunk ID (dword) and the actual + payload. */ + + real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); + + /* The in-file checksum is the last dword past the chunk data. */ + + file_cksum = *(uint32_t *)(in_buf + pos + 8 + chunk_len); + + /* If the checksums do not match, we need to fix the file. */ + + if (real_cksum != file_cksum) { + + /* First modification? Make a copy of the input buffer. Round size + up to 4 kB to minimize the number of reallocs needed. */ + + if (new_buf == in_buf) { + + if (len <= data->size) { + + new_buf = data->buf; + + } else { + + new_buf = realloc(data->buf, UP4K(len)); + if (!new_buf) { + + *out_buf = in_buf; + return len; + + } + + data->buf = new_buf; + data->size = UP4K(len); + memcpy(new_buf, in_buf, len); + + } + + } + + *(uint32_t *)(new_buf + pos + 8 + chunk_len) = real_cksum; + + } + + /* Skip the entire chunk and move to the next one. */ + + pos += 12 + chunk_len; + + } + + *out_buf = new_buf; + return len; + +} + +/* Gets called afterwards */ +void afl_custom_deinit(post_state_t *data) { + + free(data->buf); + free(data); + +} + diff --git a/utils/custom_mutators/simple-chunk-replace.py b/utils/custom_mutators/simple-chunk-replace.py new file mode 100644 index 00000000..df2f4ca7 --- /dev/null +++ b/utils/custom_mutators/simple-chunk-replace.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Simple Chunk Cross-Over Replacement Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + # Seed our RNG + random.seed(seed) + + +def fuzz(buf, add_buf, max_size): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @type max_size: int + @param max_size: Maximum size of the mutated output. The mutation must not + produce data larger than max_size. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + # Make a copy of our input buffer for returning + ret = bytearray(buf) + + # Take a random fragment length between 2 and 32 (or less if add_buf is shorter) + fragment_len = random.randint(1, min(len(add_buf), 32)) + + # Determine a random source index where to take the data chunk from + rand_src_idx = random.randint(0, len(add_buf) - fragment_len) + + # Determine a random destination index where to put the data chunk + rand_dst_idx = random.randint(0, len(buf)) + + # Make the chunk replacement + ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len] + + # Return data + return ret diff --git a/utils/custom_mutators/simple_example.c b/utils/custom_mutators/simple_example.c new file mode 100644 index 00000000..d888ec1f --- /dev/null +++ b/utils/custom_mutators/simple_example.c @@ -0,0 +1,74 @@ +// This simple example just creates random buffer <= 100 filled with 'A' +// needs -I /path/to/AFLplusplus/include +#include "custom_mutator_helpers.h" + +#include +#include +#include +#include + +#ifndef _FIXED_CHAR + #define _FIXED_CHAR 0x41 +#endif + +typedef struct my_mutator { + + afl_t *afl; + + // Reused buffers: + BUF_VAR(u8, fuzz); + +} my_mutator_t; + +my_mutator_t *afl_custom_init(afl_t *afl, unsigned int seed) { + + srand(seed); + my_mutator_t *data = calloc(1, sizeof(my_mutator_t)); + if (!data) { + + perror("afl_custom_init alloc"); + return NULL; + + } + + data->afl = afl; + + return data; + +} + +size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size, + u8 **out_buf, uint8_t *add_buf, + size_t add_buf_size, // add_buf can be NULL + size_t max_size) { + + int size = (rand() % 100) + 1; + if (size > max_size) size = max_size; + u8 *mutated_out = maybe_grow(BUF_PARAMS(data, fuzz), size); + if (!mutated_out) { + + *out_buf = NULL; + perror("custom mutator allocation (maybe_grow)"); + return 0; /* afl-fuzz will very likely error out after this. */ + + } + + memset(mutated_out, _FIXED_CHAR, size); + + *out_buf = mutated_out; + return size; + +} + +/** + * Deinitialize everything + * + * @param data The data ptr from afl_custom_init + */ +void afl_custom_deinit(my_mutator_t *data) { + + free(data->fuzz_buf); + free(data); + +} + diff --git a/utils/custom_mutators/wrapper_afl_min.py b/utils/custom_mutators/wrapper_afl_min.py new file mode 100644 index 00000000..ecb03b55 --- /dev/null +++ b/utils/custom_mutators/wrapper_afl_min.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +from XmlMutatorMin import XmlMutatorMin + +# Default settings (production mode) + +__mutator__ = None +__seed__ = "RANDOM" +__log__ = False +__log_file__ = "wrapper.log" + + +# AFL functions +def log(text): + """ + Logger + """ + + global __seed__ + global __log__ + global __log_file__ + + if __log__: + with open(__log_file__, "a") as logf: + logf.write("[%s] %s\n" % (__seed__, text)) + + +def init(seed): + """ + Called once when AFL starts up. Seed is used to identify the AFL instance in log files + """ + + global __mutator__ + global __seed__ + + # Get the seed + __seed__ = seed + + # Create a global mutation class + try: + __mutator__ = XmlMutatorMin(__seed__, verbose=__log__) + log("init(): Mutator created") + except RuntimeError as e: + log("init(): Can't create mutator: %s" % e.message) + + +def fuzz(buf, add_buf, max_size): + """ + Called for each fuzzing iteration. + """ + + global __mutator__ + + # Do we have a working mutator object? + if __mutator__ is None: + log("fuzz(): Can't fuzz, no mutator available") + return buf + + # Try to use the AFL buffer + via_buffer = True + + # Interpret the AFL buffer (an array of bytes) as a string + if via_buffer: + try: + buf_str = str(buf) + log("fuzz(): AFL buffer converted to a string") + except Exception: + via_buffer = False + log("fuzz(): Can't convert AFL buffer to a string") + + # Load XML from the AFL string + if via_buffer: + try: + __mutator__.init_from_string(buf_str) + log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str)) + except Exception: + via_buffer = False + log("fuzz(): Can't initialize mutator with AFL buffer") + + # If init from AFL buffer wasn't succesful + if not via_buffer: + log("fuzz(): Returning unmodified AFL buffer") + return buf + + # Sucessful initialization -> mutate + try: + __mutator__.mutate(max=5) + log("fuzz(): Input mutated") + except Exception: + log("fuzz(): Can't mutate input => returning buf") + return buf + + # Convert mutated data to a array of bytes + try: + data = bytearray(__mutator__.save_to_string()) + log("fuzz(): Mutated data converted as bytes") + except Exception: + log("fuzz(): Can't convert mutated data to bytes => returning buf") + return buf + + # Everything went fine, returning mutated content + log("fuzz(): Returning %d bytes" % len(data)) + return data + + +# Main (for debug) +if __name__ == '__main__': + + __log__ = True + __log_file__ = "/dev/stdout" + __seed__ = "RANDOM" + + init(__seed__) + + in_1 = bytearray("ffffzzzzzzzzzzzz") + in_2 = bytearray("") + out = fuzz(in_1, in_2) + print(out) diff --git a/utils/defork/Makefile b/utils/defork/Makefile new file mode 100644 index 00000000..e8240dba --- /dev/null +++ b/utils/defork/Makefile @@ -0,0 +1,64 @@ +# +# american fuzzy lop++ - defork +# ---------------------------------- +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) +#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" +# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) +# M32FLAG = -mbe32 +# endif +#endif + +all: defork32.so defork64.so + +defork32.so: defork.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork32 build failure (that's fine)" + +defork64.so: defork.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "defork64 build failure (that's fine)" + +install: defork32.so defork64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f defork32.so ]; then set -e; install -m 755 defork32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f defork64.so ]; then set -e; install -m 755 defork64.so $(DESTDIR)$(HELPER_PATH)/; fi + +target: + ../../afl-clang forking_target.c -o forking_target -Wall -Wextra -Werror + +clean: + rm -f defork32.so defork64.so forking_target diff --git a/utils/defork/README.md b/utils/defork/README.md new file mode 100644 index 00000000..7e950323 --- /dev/null +++ b/utils/defork/README.md @@ -0,0 +1,11 @@ +# defork + +when the target forks, this breaks all normal fuzzing runs. +Sometimes, though, it is enough to just run the child process. +If this is the case, then this LD_PRELOAD library will always return 0 on fork, +the target will belive it is running as the child, post-fork. + +This is defork.c from the amazing preeny project +https://github.com/zardus/preeny + +It is altered for afl++ to work with its fork-server: the initial fork will go through, the second fork will be blocked. diff --git a/utils/defork/defork.c b/utils/defork/defork.c new file mode 100644 index 00000000..f71d1124 --- /dev/null +++ b/utils/defork/defork.c @@ -0,0 +1,50 @@ +#define __GNU_SOURCE +#include +#include +#include +#include + +#include "../../include/config.h" + +/* we want to fork once (for the afl++ forkserver), + then immediately return as child on subsequent forks. */ +static bool forked = 0; + +pid_t (*original_fork)(void); + +/* In case we are not running in afl, we use a dummy original_fork */ +static pid_t nop(void) { + + return 0; + +} + +__attribute__((constructor)) void preeny_fork_orig() { + + if (getenv(SHM_ENV_VAR)) { + + printf("defork: running in AFL++. Allowing forkserver.\n"); + original_fork = dlsym(RTLD_NEXT, "socket"); + + } else { + + printf("defork: no AFL++ detected. Disabling fork from the start.\n"); + original_fork = &nop; + + } + +} + +pid_t fork(void) { + + /* If we forked before, or if we're in the child (pid==0), + we don't want to fork anymore, else, we are still in the forkserver. + The forkserver parent needs to fork infinite times, each child should never + fork again. This can be written without branches and I hate myself for it. + */ + pid_t ret = !forked && original_fork(); + forked = !ret; + return ret; + +} + diff --git a/utils/defork/forking_target.c b/utils/defork/forking_target.c new file mode 100644 index 00000000..628d23c9 --- /dev/null +++ b/utils/defork/forking_target.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +/* This is an example target for defork.c - fuzz using +``` +mkdir in; echo a > ./in/a +AFL_PRELOAD=./defork64.so ../../afl-fuzz -i in -o out -- ./forking_target @@ +``` +*/ + +int main(int argc, char **argv) { + + if (argc < 2) { + + printf("Example tool to test defork.\nUsage ./forking_target \n"); + return -1; + + } + + pid_t pid = fork(); + if (pid == 0) { + + printf("We're in the child.\n"); + FILE *f = fopen(argv[1], "r"); + char buf[4096]; + fread(buf, 1, 4096, f); + fclose(f); + uint32_t offset = buf[100] + (buf[101] << 8); + char test_val = buf[offset]; + return test_val < 100; + + } else if (pid < 0) { + + perror("fork"); + return -1; + + } else { + + printf("We are in the parent - defork didn't work! :( (pid=%d)\n", + (int)pid); + + } + + return 0; + +} + diff --git a/utils/distributed_fuzzing/sync_script.sh b/utils/distributed_fuzzing/sync_script.sh new file mode 100755 index 00000000..b28ff6cd --- /dev/null +++ b/utils/distributed_fuzzing/sync_script.sh @@ -0,0 +1,97 @@ +#!/bin/sh +# +# american fuzzy lop++ - fuzzer synchronization tool +# -------------------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2014 Google Inc. All rights reserved. +# Copyright 2019-2020 AFLplusplus Project. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# To make this script work: +# +# - Edit FUZZ_HOSTS, FUZZ_DOMAIN, FUZZ_USER, and SYNC_DIR to reflect your +# environment. +# +# - Make sure that the system you are running this on can log into FUZZ_HOSTS +# without a password (authorized_keys or otherwise). +# +# - Make sure that every fuzzer is running with -o pointing to SYNC_DIR and -S +# that consists of its local host name, followed by an underscore, and then +# by some host-local fuzzer ID. +# + +# Hosts to synchronize the data across. +FUZZ_HOSTS='host1 host2 host3 host4' + +# Domain for all hosts +FUZZ_DOMAIN='example.com' + +# Remote user for SSH +FUZZ_USER=bob + +# Directory to synchronize +SYNC_DIR='/home/bob/sync_dir' + +# We only capture -M main nodes, set the name to your chosen naming scheme +MAIN_NAME='main' + +# Interval (seconds) between sync attempts (eg one hour) +SYNC_INTERVAL=$((60 * 60)) + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + if [ "$PWD" = "/tmp" -o "$PWD" = "/var/tmp" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +rm -rf .sync_tmp 2>/dev/null +mkdir .sync_tmp || exit 1 + +while :; do + + # Pull data in... + + for host in $FUZZ_HOSTS; do + + echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ + "cd '$SYNC_DIR' && tar -czf - ${host}_${MAIN_NAME}*/" > ".sync_tmp/${host}.tgz" + + done + + # Distribute data. For large fleets, see tips in the docs/ directory. + + for dst_host in $FUZZ_HOSTS; do + + echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." + + for src_host in $FUZZ_HOSTS; do + + test "$src_host" = "$dst_host" && continue + + echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ + "cd '$SYNC_DIR' && tar -xkzf - " < ".sync_tmp/${src_host}.tgz" + + done + + done + + echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." + + sleep $SYNC_INTERVAL + +done + diff --git a/utils/libpng_no_checksum/libpng-nocrc.patch b/utils/libpng_no_checksum/libpng-nocrc.patch new file mode 100644 index 00000000..0a3793a0 --- /dev/null +++ b/utils/libpng_no_checksum/libpng-nocrc.patch @@ -0,0 +1,15 @@ +--- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 ++++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 +@@ -268,7 +268,11 @@ + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); +- return ((int)(crc != png_ptr->crc)); ++ ++ if (crc != png_ptr->crc) ++ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); ++ ++ return ((int)(1 != 1)); + } + + else diff --git a/utils/persistent_mode/Makefile b/utils/persistent_mode/Makefile new file mode 100644 index 00000000..6fa1c30e --- /dev/null +++ b/utils/persistent_mode/Makefile @@ -0,0 +1,10 @@ +all: + afl-clang-fast -o persistent_demo persistent_demo.c + afl-clang-fast -o persistent_demo_new persistent_demo_new.c + AFL_DONT_OPTIMIZE=1 afl-clang-fast -o test-instr test-instr.c + +document: + AFL_DONT_OPTIMIZE=1 afl-clang-fast -D_AFL_DOCUMENT_MUTATIONS -o test-instr test-instr.c + +clean: + rm -f persistent_demo persistent_demo_new test-instr diff --git a/utils/persistent_mode/persistent_demo.c b/utils/persistent_mode/persistent_demo.c new file mode 100644 index 00000000..4cedc32c --- /dev/null +++ b/utils/persistent_mode/persistent_demo.c @@ -0,0 +1,112 @@ +/* + american fuzzy lop++ - persistent mode example + -------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file demonstrates the high-performance "persistent mode" that may be + suitable for fuzzing certain fast and well-behaved libraries, provided that + they are stateless or that their internal state can be easily reset + across runs. + + To make this work, the library and this shim need to be compiled in LLVM + mode using afl-clang-fast (other compiler wrappers will *not* work). + + */ + +#include +#include +#include +#include +#include + +/* Main entry point. */ + +int main(int argc, char **argv) { + + ssize_t len; /* how much input did we read? */ + char buf[100]; /* Example-only buffer, you'd replace it with other global or + local variables appropriate for your use case. */ + + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + __AFL_INIT(); + while (__AFL_LOOP(1000)) { + + /*** PLACEHOLDER CODE ***/ + + /* STEP 1: Fully re-initialize all critical variables. In our example, this + involves zeroing buf[], our input buffer. */ + + memset(buf, 0, 100); + + /* STEP 2: Read input data. When reading from stdin, no special preparation + is required. When reading from a named file, you need to close + the old descriptor and reopen the file first! + + Beware of reading from buffered FILE* objects such as stdin. Use + raw file descriptors or call fopen() / fdopen() in every pass. */ + + len = read(0, buf, 100); + + /* STEP 3: This is where we'd call the tested library on the read data. + We just have some trivial inline code that faults on 'foo!'. */ + + /* do we have enough data? */ + if (len < 8) continue; + + if (buf[0] == 'f') { + + printf("one\n"); + if (buf[1] == 'o') { + + printf("two\n"); + if (buf[2] == 'o') { + + printf("three\n"); + if (buf[3] == '!') { + + printf("four\n"); + if (buf[4] == '!') { + + printf("five\n"); + if (buf[5] == '!') { + + printf("six\n"); + abort(); + + } + + } + + } + + } + + } + + } + + /*** END PLACEHOLDER CODE ***/ + + } + + /* Once the loop is exited, terminate normally - AFL will restart the process + when this happens, with a clean slate when it comes to allocated memory, + leftover file descriptors, etc. */ + + return 0; + +} + diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c new file mode 100644 index 00000000..a29792ff --- /dev/null +++ b/utils/persistent_mode/persistent_demo_new.c @@ -0,0 +1,117 @@ +/* + american fuzzy lop++ - persistent mode example + -------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file demonstrates the high-performance "persistent mode" that may be + suitable for fuzzing certain fast and well-behaved libraries, provided that + they are stateless or that their internal state can be easily reset + across runs. + + To make this work, the library and this shim need to be compiled in LLVM + mode using afl-clang-fast (other compiler wrappers will *not* work). + + */ + +#include +#include +#include +#include +#include + +/* this lets the source compile without afl-clang-fast/lto */ +#ifndef __AFL_FUZZ_TESTCASE_LEN + +ssize_t fuzz_len; +unsigned char fuzz_buf[1024000]; + + #define __AFL_FUZZ_TESTCASE_LEN fuzz_len + #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf + #define __AFL_FUZZ_INIT() void sync(void); + #define __AFL_LOOP(x) \ + ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) + #define __AFL_INIT() sync() + +#endif + +__AFL_FUZZ_INIT(); + +/* Main entry point. */ + +int main(int argc, char **argv) { + + ssize_t len; /* how much input did we read? */ + unsigned char *buf; /* test case buffer pointer */ + + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + __AFL_INIT(); + buf = __AFL_FUZZ_TESTCASE_BUF; // this must be assigned before __AFL_LOOP! + + while (__AFL_LOOP(1000)) { // increase if you have good stability + + len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! + + fprintf(stderr, "input: %zd \"%s\"\n", len, buf); + + /* do we have enough data? */ + if (len < 8) continue; + + if (strcmp((char *)buf, "thisisateststring") == 0) printf("teststring\n"); + + if (buf[0] == 'f') { + + printf("one\n"); + if (buf[1] == 'o') { + + printf("two\n"); + if (buf[2] == 'o') { + + printf("three\n"); + if (buf[3] == '!') { + + printf("four\n"); + if (buf[4] == '!') { + + printf("five\n"); + if (buf[6] == '!') { + + printf("six\n"); + abort(); + + } + + } + + } + + } + + } + + } + + /*** END PLACEHOLDER CODE ***/ + + } + + /* Once the loop is exited, terminate normally - AFL will restart the process + when this happens, with a clean slate when it comes to allocated memory, + leftover file descriptors, etc. */ + + return 0; + +} + diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c new file mode 100644 index 00000000..a6188b22 --- /dev/null +++ b/utils/persistent_mode/test-instr.c @@ -0,0 +1,69 @@ +/* + american fuzzy lop++ - a trivial program to test the build + -------------------------------------------------------- + Originally written by Michal Zalewski + Copyright 2014 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +__AFL_FUZZ_INIT(); + +int main(int argc, char **argv) { + + __AFL_INIT(); + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; + + while (__AFL_LOOP(2147483647)) { // MAX_INT if you have 100% stability + + unsigned int len = __AFL_FUZZ_TESTCASE_LEN; + +#ifdef _AFL_DOCUMENT_MUTATIONS + static unsigned int counter = 0; + char fn[32]; + sprintf(fn, "%09u:test-instr", counter); + int fd_doc = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd_doc >= 0) { + + if (write(fd_doc, buf, len) != __afl_fuzz_len) { + + fprintf(stderr, "write of mutation file failed: %s\n", fn); + unlink(fn); + + } + + close(fd_doc); + + } + + counter++; +#endif + + // fprintf(stderr, "len: %u\n", len); + + if (!len) continue; + + if (buf[0] == '0') + printf("Looks like a zero to me!\n"); + else if (buf[0] == '1') + printf("Pretty sure that is a one!\n"); + else + printf("Neither one or zero? How quaint!\n"); + + } + + return 0; + +} + diff --git a/utils/qemu_persistent_hook/Makefile b/utils/qemu_persistent_hook/Makefile new file mode 100644 index 00000000..85db1b46 --- /dev/null +++ b/utils/qemu_persistent_hook/Makefile @@ -0,0 +1,6 @@ +all: + $(CC) -no-pie test.c -o test + $(CC) -fPIC -shared read_into_rdi.c -o read_into_rdi.so + +clean: + rm -rf in out test read_into_rdi.so diff --git a/utils/qemu_persistent_hook/README.md b/utils/qemu_persistent_hook/README.md new file mode 100644 index 00000000..3f908c22 --- /dev/null +++ b/utils/qemu_persistent_hook/README.md @@ -0,0 +1,19 @@ +# QEMU persistent hook example + +Compile the test binary and the library: + +``` +make +``` + +Fuzz with: + +``` +export AFL_QEMU_PERSISTENT_ADDR=0x$(nm test | grep "T target_func" | awk '{print $1}') +export AFL_QEMU_PERSISTENT_HOOK=./read_into_rdi.so + +mkdir in +echo 0000 > in/in + +../../afl-fuzz -Q -i in -o out -- ./test +``` diff --git a/utils/qemu_persistent_hook/read_into_rdi.c b/utils/qemu_persistent_hook/read_into_rdi.c new file mode 100644 index 00000000..f4a8ae59 --- /dev/null +++ b/utils/qemu_persistent_hook/read_into_rdi.c @@ -0,0 +1,34 @@ +#include "../../qemu_mode/qemuafl/qemuafl/api.h" + +#include +#include + +void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base, + uint8_t *input_buf, uint32_t input_buf_len) { +\ +#define g2h(x) ((void *)((unsigned long)(x) + guest_base)) +#define h2g(x) ((uint64_t)(x)-guest_base) + + // In this example the register RDI is pointing to the memory location + // of the target buffer, and the length of the input is in RSI. + // This can be seen with a debugger, e.g. gdb (and "disass main") + + printf("Placing input into 0x%lx\n", regs->rdi); + + if (input_buf_len > 1024) input_buf_len = 1024; + memcpy(g2h(regs->rdi), input_buf, input_buf_len); + regs->rsi = input_buf_len; + +#undef g2h +#undef h2g + +} + +int afl_persistent_hook_init(void) { + + // 1 for shared memory input (faster), 0 for normal input (you have to use + // read(), input_buf will be NULL) + return 1; + +} + diff --git a/utils/qemu_persistent_hook/test.c b/utils/qemu_persistent_hook/test.c new file mode 100644 index 00000000..afeff202 --- /dev/null +++ b/utils/qemu_persistent_hook/test.c @@ -0,0 +1,35 @@ +#include + +int target_func(unsigned char *buf, int size) { + + printf("buffer:%p, size:%p\n", buf, size); + switch (buf[0]) { + + case 1: + if (buf[1] == '\x44') { puts("a"); } + break; + case 0xff: + if (buf[2] == '\xff') { + + if (buf[1] == '\x44') { puts("b"); } + + } + + break; + default: + break; + + } + + return 1; + +} + +char data[1024]; + +int main() { + + target_func(data, 1024); + +} + diff --git a/utils/socket_fuzzing/Makefile b/utils/socket_fuzzing/Makefile new file mode 100644 index 00000000..9476e2d5 --- /dev/null +++ b/utils/socket_fuzzing/Makefile @@ -0,0 +1,61 @@ +# +# american fuzzy lop++ - socket_fuzz +# ---------------------------------- +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +UNAME_SAYS_LINUX=$(shell uname | grep -E '^Linux|^GNU' >/dev/null; echo $$?) +UNAME_SAYS_LINUX:sh=uname | grep -E '^Linux|^GNU' >/dev/null; echo $$? + +_LDFLAGS_ADD=$(UNAME_SAYS_LINUX:1=) +LDFLAGS_ADD=$(_LDFLAGS_ADD:0=-ldl) +LDFLAGS += $(LDFLAGS_ADD) + +# on gcc for arm there is no -m32, but -mbe32 +M32FLAG = -m32 +M64FLAG = -m64 + +CC_IS_GCC=$(shell $(CC) --version 2>/dev/null | grep -q gcc; echo $$?) +CC_IS_GCC:sh=$(CC) --version 2>/dev/null | grep -q gcc; echo $$? +CC_IS_ARMCOMPILER=$(shell $(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$?) +CC_IS_ARMCOMPILER:sh=$(CC) -v 2>&1 >/dev/null | grep -q arm; echo $$? + +_M32FLAG=$(CC_IS_GCC)$(CC_IS_ARMCOMPILER) +__M32FLAG=$(_M32FLAG:00=-mbe32) +___M32FLAG=$(__M32FLAG:$(CC_IS_GCC)$(CC_IS_ARMCOMPILER)=-m32) +M32FLAG=$(___M32FLAG) +#ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" "" +# ifneq (,$(findstring arm, "$(shell $(CC) -v 2>&1 >/dev/null)")) +# M32FLAG = -mbe32 +# endif +#endif + +all: socketfuzz32.so socketfuzz64.so + +socketfuzz32.so: socketfuzz.c + -@$(CC) $(M32FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz32 build failure (that's fine)" + +socketfuzz64.so: socketfuzz.c + -@$(CC) $(M64FLAG) $(CFLAGS) $^ $(LDFLAGS) -o $@ 2>/dev/null || echo "socketfuzz64 build failure (that's fine)" + +install: socketfuzz32.so socketfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + if [ -f socketfuzz64.so ]; then set -e; install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/; fi + +clean: + rm -f socketfuzz32.so socketfuzz64.so diff --git a/utils/socket_fuzzing/README.md b/utils/socket_fuzzing/README.md new file mode 100644 index 00000000..79f28bea --- /dev/null +++ b/utils/socket_fuzzing/README.md @@ -0,0 +1,11 @@ +# socketfuzz + +when you want to fuzz a network service and you can not/do not want to modify +the source (or just have a binary), then this LD_PRELOAD library will allow +for sending input to stdin which the target binary will think is coming from +a network socket. + +This is desock_dup.c from the amazing preeny project +https://github.com/zardus/preeny + +It is packaged in afl++ to have it at hand if needed diff --git a/utils/socket_fuzzing/socketfuzz.c b/utils/socket_fuzzing/socketfuzz.c new file mode 100644 index 00000000..3ec8383b --- /dev/null +++ b/utils/socket_fuzzing/socketfuzz.c @@ -0,0 +1,110 @@ +/* + * This is desock_dup.c from the amazing preeny project + * https://github.com/zardus/preeny + * + * It is packaged in afl++ to have it at hand if needed + * + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include // +#include // +#include // +#include // +#include +#include +#include +#include +#include +#include +#include +//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " + +// +// originals +// +int (*original_close)(int); +int (*original_dup2)(int, int); +__attribute__((constructor)) void preeny_desock_dup_orig() { + + original_close = dlsym(RTLD_NEXT, "close"); + original_dup2 = dlsym(RTLD_NEXT, "dup2"); + +} + +int close(int sockfd) { + + if (sockfd <= 2) { + + fprintf(stderr, "Info: Disabling close on %d\n", sockfd); + return 0; + + } else { + + return original_close(sockfd); + + } + +} + +int dup2(int old, int new) { + + if (new <= 2) { + + fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); + return 0; + + } else { + + return original_dup2(old, new); + + } + +} + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); + return 0; + +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating bind on port %d\n", + ntohs(((struct sockaddr_in *)addr)->sin_port)); + return 0; + +} + +int listen(int sockfd, int backlog) { + + (void)sockfd; + (void)backlog; + return 0; + +} + +int setsockopt(int sockfd, int level, int optid, const void *optdata, + socklen_t optdatalen) { + + (void)sockfd; + (void)level; + (void)optid; + (void)optdata; + (void)optdatalen; + return 0; + +} + -- cgit 1.4.1 From 0942158ad1210e3933ea15dd9c5ab0a6516febb0 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 1 Dec 2020 23:17:20 +0100 Subject: remove docs/README symlink and update references --- afl-cmin | 2 +- afl-cmin.bash | 2 +- dictionaries/README.md | 2 +- docs/README.md | 1 - libdislocator/README.md | 2 +- libtokencap/README.md | 2 +- qemu_mode/README.md | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) delete mode 120000 docs/README.md (limited to 'docs') diff --git a/afl-cmin b/afl-cmin index 0dbf1390..91ed8d6d 100755 --- a/afl-cmin +++ b/afl-cmin @@ -113,7 +113,7 @@ function usage() { " -C - keep crashing inputs, reject everything else\n" \ " -e - solve for edge coverage only, ignore hit counts\n" \ "\n" \ -"For additional tips, please consult docs/README.md\n" \ +"For additional tips, please consult README.md\n" \ "\n" \ "Environment variables used:\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ diff --git a/afl-cmin.bash b/afl-cmin.bash index 3e29aa5c..637949bc 100755 --- a/afl-cmin.bash +++ b/afl-cmin.bash @@ -128,7 +128,7 @@ Minimization settings: -C - keep crashing inputs, reject everything else -e - solve for edge coverage only, ignore hit counts -For additional tips, please consult docs/README.md. +For additional tips, please consult README.md. Environment variables used: AFL_KEEP_TRACES: leave the temporary \.traces directory diff --git a/dictionaries/README.md b/dictionaries/README.md index 616a83cc..7c587abb 100644 --- a/dictionaries/README.md +++ b/dictionaries/README.md @@ -1,6 +1,6 @@ # AFL dictionaries -(See [../docs/README.md](../docs/README.md) for the general instruction manual.) +(See [../README.md](../README.md) for the general instruction manual.) This subdirectory contains a set of dictionaries that can be used in conjunction with the -x option to allow the fuzzer to effortlessly explore the diff --git a/docs/README.md b/docs/README.md deleted file mode 120000 index 32d46ee8..00000000 --- a/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -../README.md \ No newline at end of file diff --git a/libdislocator/README.md b/libdislocator/README.md index 873d8806..1785463e 100644 --- a/libdislocator/README.md +++ b/libdislocator/README.md @@ -1,6 +1,6 @@ # libdislocator, an abusive allocator - (See ../docs/README.md for the general instruction manual.) + (See ../README.md for the general instruction manual.) This is a companion library that can be used as a drop-in replacement for the libc allocator in the fuzzed binaries. It improves the odds of bumping into diff --git a/libtokencap/README.md b/libtokencap/README.md index 0a3591eb..13a440da 100644 --- a/libtokencap/README.md +++ b/libtokencap/README.md @@ -1,6 +1,6 @@ # strcmp() / memcmp() token capture library - (See ../docs/README.md for the general instruction manual.) + (See ../README.md for the general instruction manual.) This companion library allows you to instrument `strcmp()`, `memcmp()`, and related functions to automatically extract syntax tokens passed to any of diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 58e48e91..9818846d 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -1,6 +1,6 @@ # High-performance binary-only instrumentation for afl-fuzz - (See ../docs/README.md for the general instruction manual.) + (See ../README.md for the general instruction manual.) ## 1) Introduction -- cgit 1.4.1 From a2e2fae840e9946c7994ac6807bed8496d71af56 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 3 Dec 2020 14:43:06 +0100 Subject: AFL_CRASH_EXITCODE env var added, u8->bool --- .gitignore | 1 + afl-cmin | 5 +++-- docs/Changelog.md | 4 +++- docs/env_variables.md | 7 +++++++ include/afl-fuzz.h | 7 ++++--- include/common.h | 2 +- include/envs.h | 1 + include/forkserver.h | 21 +++++++++++++-------- src/afl-analyze.c | 4 ++-- src/afl-common.c | 4 ++-- src/afl-forkserver.c | 22 +++++++++++++++------- src/afl-fuzz-init.c | 27 +++++++++++++++++++++++++-- src/afl-fuzz-state.c | 7 +++++++ src/afl-fuzz.c | 26 ++++++++++++++++++++++++-- src/afl-showmap.c | 19 +++++++++++++++++++ src/afl-tmin.c | 32 +++++++++++++++++++++++++------- 16 files changed, 152 insertions(+), 37 deletions(-) (limited to 'docs') diff --git a/.gitignore b/.gitignore index 97f99bf6..82a81605 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.pyc *.dSYM as +a.out ld in out diff --git a/afl-cmin b/afl-cmin index 91ed8d6d..b3b1ead8 100755 --- a/afl-cmin +++ b/afl-cmin @@ -116,11 +116,12 @@ function usage() { "For additional tips, please consult README.md\n" \ "\n" \ "Environment variables used:\n" \ +"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" \ +"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ +"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ "AFL_PATH: path for the afl-showmap binary\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" \ -"AFL_ALLOW_TMP: allow unsafe use of input/output directories under {/var}/tmp\n" -"AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" exit 1 } diff --git a/docs/Changelog.md b/docs/Changelog.md index fd30c7b0..02728f10 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -60,8 +60,10 @@ sending a mail to . - Our afl++ Grammar-Mutator is now better integrated into custom_mutators/ - added INTROSPECTION support for custom modules - python fuzz function was not optional, fixed - - unicornafl synced with upstream (arm64 fix, better rust bindings) + - some python mutator speed improvements + - unicornafl synced with upstream version 1.02 (fixes, better rust bindings) - renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD + - added AFL_CRASH_EXITCODE env variable to treat a child exitcode as crash ### Version ++2.68c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index ada89257..e203055f 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -428,6 +428,13 @@ checks or alter some of the more exotic semantics of the tool: matches your StatsD server. Available flavors are `dogstatsd`, `librato`, `signalfx` and `influxdb`. + - Setting `AFL_CRASH_EXITCODE` sets the exit code afl treats as crash. + For example, if `AFL_CRASH_EXITCODE='-1'` is set, each input resulting + in an `-1` return code (i.e. `exit(-1)` got called), will be treated + as if a crash had ocurred. + This may be beneficial if you look for higher-level faulty conditions in which your + target still exits gracefully. + - Outdated environment variables that are not supported anymore: `AFL_DEFER_FORKSRV` `AFL_PERSISTENT` diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 933af65d..62d76323 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -144,8 +144,8 @@ struct queue_entry { u8 *fname; /* File name for the test case */ u32 len; /* Input length */ - u8 cal_failed, /* Calibration failed? */ - trim_done, /* Trimmed? */ + u8 cal_failed; /* Calibration failed? */ + bool trim_done, /* Trimmed? */ was_fuzzed, /* historical, but needed for MOpt */ passed_det, /* Deterministic stages passed? */ has_new_cov, /* Triggers new coverage? */ @@ -368,7 +368,8 @@ typedef struct afl_env_vars { u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, *afl_max_det_extras, *afl_statsd_host, *afl_statsd_port, - *afl_statsd_tags_flavor, *afl_testcache_size, *afl_testcache_entries; + *afl_crash_exitcode, *afl_statsd_tags_flavor, *afl_testcache_size, + *afl_testcache_entries; } afl_env_vars_t; diff --git a/include/common.h b/include/common.h index c364ade0..6e5039d8 100644 --- a/include/common.h +++ b/include/common.h @@ -38,7 +38,7 @@ #define STRINGIFY_VAL_SIZE_MAX (16) -void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin); +void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin); void check_environment_vars(char **env); char **argv_cpy_dup(int argc, char **argv); diff --git a/include/envs.h b/include/envs.h index 3aa05cb5..43c87148 100644 --- a/include/envs.h +++ b/include/envs.h @@ -32,6 +32,7 @@ static char *afl_environment_variables[] = { "AFL_CODE_START", "AFL_COMPCOV_BINNAME", "AFL_COMPCOV_LEVEL", + "AFL_CRASH_EXITCODE", "AFL_CUSTOM_MUTATOR_LIBRARY", "AFL_CUSTOM_MUTATOR_ONLY", "AFL_CXX", diff --git a/include/forkserver.h b/include/forkserver.h index 300ecffc..5d5c728f 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -37,9 +37,7 @@ typedef struct afl_forkserver { /* a program that includes afl-forkserver needs to define these */ - u8 uses_asan; /* Target uses ASAN? */ u8 *trace_bits; /* SHM with instrumentation bitmap */ - u8 use_stdin; /* use stdin for sending data */ s32 fsrv_pid, /* PID of the fork server */ child_pid, /* PID of the fuzzed program */ @@ -53,8 +51,6 @@ typedef struct afl_forkserver { fsrv_ctl_fd, /* Fork server control pipe (write) */ fsrv_st_fd; /* Fork server status pipe (read) */ - u8 no_unlink; /* do not unlink cur_input */ - u32 exec_tmout; /* Configurable exec timeout (ms) */ u32 init_tmout; /* Configurable init timeout (ms) */ u32 map_size; /* map size used by the target */ @@ -73,13 +69,22 @@ typedef struct afl_forkserver { u8 last_kill_signal; /* Signal that killed the child */ - u8 use_shmem_fuzz; /* use shared mem for test cases */ + bool use_shmem_fuzz; /* use shared mem for test cases */ + + bool support_shmem_fuzz; /* set by afl-fuzz */ + + bool use_fauxsrv; /* Fauxsrv for non-forking targets? */ + + bool qemu_mode; /* if running in qemu mode or not */ + + bool use_stdin; /* use stdin for sending data */ - u8 support_shmem_fuzz; /* set by afl-fuzz */ + bool no_unlink; /* do not unlink cur_input */ - u8 use_fauxsrv; /* Fauxsrv for non-forking targets? */ + bool uses_asan; /* Target uses ASAN? */ - u8 qemu_mode; /* if running in qemu mode or not */ + bool uses_crash_exitcode; /* Custom crash exitcode specified? */ + u8 crash_exitcode; /* The crash exitcode specified */ u32 *shmem_fuzz_len; /* length of the fuzzing test case */ diff --git a/src/afl-analyze.c b/src/afl-analyze.c index c8acebb3..2780deff 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -78,9 +78,9 @@ static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ static s32 dev_null_fd = -1; /* FD to /dev/null */ -static u8 edges_only, /* Ignore hit counts? */ +static bool edges_only, /* Ignore hit counts? */ use_hex_offsets, /* Show hex offsets? */ - use_stdin = 1; /* Use stdin for program input? */ + use_stdin = true; /* Use stdin for program input? */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_timed_out; /* Child timed out? */ diff --git a/src/afl-common.c b/src/afl-common.c index 8cf1a444..ed0b0e53 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -46,7 +46,7 @@ u8 be_quiet = 0; u8 *doc_path = ""; u8 last_intr = 0; -void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { +void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) { u32 i = 0; u8 cwd[PATH_MAX]; @@ -63,7 +63,7 @@ void detect_file_args(char **argv, u8 *prog_in, u8 *use_stdin) { if (!prog_in) { FATAL("@@ syntax is not supported by this tool."); } - *use_stdin = 0; + *use_stdin = false; if (prog_in[0] != 0) { // not afl-showmap special case diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 01ef1d9e..20117c1d 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -76,8 +76,8 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->dev_urandom_fd = -1; /* Settings */ - fsrv->use_stdin = 1; - fsrv->no_unlink = 0; + fsrv->use_stdin = true; + fsrv->no_unlink = false; fsrv->exec_tmout = EXEC_TIMEOUT; fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; @@ -86,8 +86,11 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { /* exec related stuff */ fsrv->child_pid = -1; fsrv->map_size = get_map_size(); - fsrv->use_fauxsrv = 0; - fsrv->last_run_timed_out = 0; + fsrv->use_fauxsrv = false; + fsrv->last_run_timed_out = false; + + fsrv->uses_crash_exitcode = false; + fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; @@ -109,6 +112,8 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->dev_urandom_fd = from->dev_urandom_fd; fsrv_to->out_fd = from->out_fd; // not sure this is a good idea fsrv_to->no_unlink = from->no_unlink; + fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; + fsrv_to->crash_exitcode = from->crash_exitcode; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -1136,10 +1141,13 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, } - /* A somewhat nasty hack for MSAN, which doesn't support abort_on_error and - must use a special exit code. */ + /* MSAN in uses_asan mode uses a special exit code as it doesn't support + abort_on_error. + On top, a user may specify a custom AFL_CRASH_EXITCODE. Handle both here. */ - if (fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) { + if ((fsrv->uses_asan && WEXITSTATUS(fsrv->child_status) == MSAN_ERROR) || + (fsrv->uses_crash_exitcode && + WEXITSTATUS(fsrv->child_status) == fsrv->crash_exitcode)) { fsrv->last_kill_signal = 0; return FSRV_RUN_CRASH; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 0360cdb0..6707340b 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -868,7 +868,19 @@ void perform_dry_run(afl_state_t *afl) { if (skip_crashes) { - WARNF("Test case results in a crash (skipping)"); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case results in a crash or AFL_CRASH_EXITCODE %d " + "(skipping)", + (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case results in a crash (skipping)"); + + } + q->cal_failed = CAL_CHANCES; ++cal_failures; break; @@ -954,7 +966,18 @@ void perform_dry_run(afl_state_t *afl) { #undef MSG_ULIMIT_USAGE #undef MSG_FORK_ON_APPLE - WARNF("Test case '%s' results in a crash, skipping", fn); + if (afl->fsrv.uses_crash_exitcode) { + + WARNF( + "Test case '%s' results in a crash or AFL_CRASH_EXITCODE %d, " + "skipping", + fn, (int)(s8)afl->fsrv.crash_exitcode); + + } else { + + WARNF("Test case '%s' results in a crash, skipping", fn); + + } /* Remove from fuzzing queue but keep for splicing */ diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 489d4e53..73b94466 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -394,6 +394,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_statsd_tags_flavor = (u8 *)get_afl_env(afl_environment_variables[i]); + } else if (!strncmp(env, "AFL_CRASH_EXITCODE", + + afl_environment_variable_len)) { + + afl->afl_env.afl_crash_exitcode = + (u8 *)get_afl_env(afl_environment_variables[i]); + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index b91d862d..eb5e9307 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -26,6 +26,7 @@ #include "afl-fuzz.h" #include "cmplog.h" #include +#include #ifndef USEMMAP #include #include @@ -165,6 +166,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n" "AFL_BENCH_JUST_ONE: run the target just once\n" "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" "AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n" @@ -702,7 +704,7 @@ int main(int argc, char **argv_orig, char **envp) { case 'N': /* Unicorn mode */ if (afl->no_unlink) { FATAL("Multiple -N options not supported"); } - afl->fsrv.no_unlink = afl->no_unlink = 1; + afl->fsrv.no_unlink = (afl->no_unlink = true); break; @@ -1135,6 +1137,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->afl_env.afl_crash_exitcode) { + + long exitcode = strtol(afl->afl_env.afl_crash_exitcode, NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + afl->afl_env.afl_crash_exitcode); + + } + + afl->fsrv.uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + afl->fsrv.crash_exitcode = (u8)exitcode; + + } + if (afl->non_instrumented_mode == 2 && afl->no_forkserver) { FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); @@ -1486,9 +1505,12 @@ int main(int argc, char **argv_orig, char **envp) { cull_queue(afl); - if (!afl->pending_not_fuzzed) + if (!afl->pending_not_fuzzed) { + FATAL("We need at least on valid input seed that does not crash!"); + } + show_init_stats(afl); if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index a8e7d3f9..e07e76c8 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -667,6 +667,8 @@ static void usage(u8 *argv0) { "AFL_CMIN_CRASHES_ONLY: (cmin_mode) only write tuples for crashing " "inputs\n" "AFL_CMIN_ALLOW_ANY: (cmin_mode) write tuples for crashing inputs also\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as " + "crash\n" "AFL_DEBUG: enable extra developer output\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " "size\n" @@ -1090,6 +1092,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + afl_fsrv_start(fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) diff --git a/src/afl-tmin.c b/src/afl-tmin.c index e4fb068d..b9045551 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -841,17 +842,17 @@ static void usage(u8 *argv0) { "For additional tips, please consult %s/README.md.\n\n" "Environment variables used:\n" - "TMPDIR: directory to use for temporary input files\n" - "ASAN_OPTIONS: custom settings for ASAN\n" - " (must contain abort_on_error=1 and symbolize=0)\n" - "MSAN_OPTIONS: custom settings for MSAN\n" - " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" + "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_TMIN_EXACT: require execution paths to match for crashing inputs\n" - "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" - + "ASAN_OPTIONS: custom settings for ASAN\n" + " (must contain abort_on_error=1 and symbolize=0)\n" + "MSAN_OPTIONS: custom settings for MSAN\n" + " (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n" + "TMPDIR: directory to use for temporary input files\n" , argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); exit(1); @@ -1122,6 +1123,23 @@ int main(int argc, char **argv_orig, char **envp) { } + if (getenv("AFL_CRASH_EXITCODE")) { + + long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); + if ((!exitcode && (errno == EINVAL || errno == ERANGE)) || + exitcode < -127 || exitcode > 128) { + + FATAL("Invalid crash exitcode, expected -127 to 128, but got %s", + getenv("AFL_CRASH_EXITCODE")); + + } + + fsrv->uses_crash_exitcode = true; + // WEXITSTATUS is 8 bit unsigned + fsrv->crash_exitcode = (u8)exitcode; + + } + shm_fuzz = ck_alloc(sizeof(sharedmem_t)); /* initialize cmplog_mode */ -- cgit 1.4.1 From a19b3022d93195d3703817c728817d7e071e89fe Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 3 Dec 2020 19:22:44 +0100 Subject: afl_custom_describe api added --- docs/Changelog.md | 10 ++++--- include/afl-fuzz.h | 14 +++++++++ src/afl-fuzz-bitmap.c | 63 +++++++++++++++++++++++++++++--------- src/afl-fuzz-mutators.c | 80 ++++++++++++++++++++++++++++++++++++++++--------- src/afl-fuzz-one.c | 9 ++++-- unicorn_mode/unicornafl | 2 +- 6 files changed, 143 insertions(+), 35 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 02728f10..5201eb8b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,18 +22,18 @@ sending a mail to . a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - rpc.statsd support by Edznux, thanks a lot! + - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - not specifying -M or -S will now auto-set "-S default" - reading testcases from -i now descends into subdirectories - - allow up to 4 times the -x command line option - - loaded extras now have a duplicate protection + - allow the -x command line option up to 4 times + - loaded extras now have a duplication protection - If test cases are too large we do a partial read on the maximum supported size - longer seeds with the same trace information will now be ignored for fuzzing but still be used for splicing - crashing seeds are now not prohibiting a run anymore but are - skipped. They are used for splicing though. + skipped - they are used for splicing, though - update MOpt for expanded havoc modes - setting the env var AFL_NO_AUTODICT will not load an LTO autodictionary - added NO_SPLICING compile option and makefile define @@ -42,6 +42,8 @@ sending a mail to . - print special compile time options used in help output - when using -c cmplog, one of the childs was not killed, fixed - somewhere we broke -n dumb fuzzing, fixed + - added afl_custom_describe to the custom mutator API to allow for easy + mutation reproduction on crashing inputs - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 62d76323..92465e7e 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -798,6 +798,20 @@ struct custom_mutator { size_t (*afl_custom_fuzz)(void *data, u8 *buf, size_t buf_size, u8 **out_buf, u8 *add_buf, size_t add_buf_size, size_t max_size); + /** + * Describe the current testcase, generated by the last mutation. + * This will be called, for example, to give the written testcase a name + * after a crash ocurred. It can help to reproduce crashing mutations. + * + * (Optional) + * + * @param data pointer returned in afl_custom_init for this fuzz case + * @param[in] max_size Maximum size of the mutated output. The mutation must + * not produce data larger than max_size. + * @return A valid ptr to a 0-terminated string, or NULL on error. + */ + const char *(*afl_custom_describe)(void *data, size_t max_size); + /** * A post-processing function to use right before AFL writes the test case to * disk in order to execute the target. diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 2d14b04e..a78bf374 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -425,7 +425,7 @@ void minimize_bits(afl_state_t *afl, u8 *dst, u8 *src) { /* Construct a file name for a new test case, capturing the operation that led to its discovery. Returns a ptr to afl->describe_op_buf_256. */ -u8 *describe_op(afl_state_t *afl, u8 hnb) { +u8 *describe_op(afl_state_t *afl, u8 new_bits) { u8 *ret = afl->describe_op_buf_256; @@ -445,29 +445,64 @@ u8 *describe_op(afl_state_t *afl, u8 hnb) { sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); - sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + if (afl->current_custom_fuzz && + afl->current_custom_fuzz->afl_custom_describe) { - if (afl->stage_cur_byte >= 0) { + /* We are currently in a custom mutator that supports afl_custom_describe, + * use it! */ - sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + size_t len_current = strlen(ret); + ret[len_current++] = ','; + ret[len_current++] = '\0'; - if (afl->stage_val_type != STAGE_VAL_NONE) { + size_t size_left = + sizeof(afl->describe_op_buf_256) - len_current - strlen(",+cov") - 2; + assert(size_left > 0); - sprintf(ret + strlen(ret), ",val:%s%+d", - (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", - afl->stage_cur_val); + const char *custom_description = + afl->current_custom_fuzz->afl_custom_describe( + afl->current_custom_fuzz->data, size_left); + if (!custom_description || !custom_description[0]) { + + DEBUGF("Error getting a description from afl_custom_describe"); + /* Take the stage name as description fallback */ + sprintf(ret + len_current, "op:%s", afl->stage_short); + + } else { + + /* We got a proper custom description, use it */ + strncat(ret + len_current, custom_description, size_left); } } else { - sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + /* Normal testcase descriptions start here */ + sprintf(ret + strlen(ret), ",op:%s", afl->stage_short); + + if (afl->stage_cur_byte >= 0) { + + sprintf(ret + strlen(ret), ",pos:%d", afl->stage_cur_byte); + + if (afl->stage_val_type != STAGE_VAL_NONE) { + + sprintf(ret + strlen(ret), ",val:%s%+d", + (afl->stage_val_type == STAGE_VAL_BE) ? "be:" : "", + afl->stage_cur_val); + + } + + } else { + + sprintf(ret + strlen(ret), ",rep:%d", afl->stage_cur_val); + + } } } - if (hnb == 2) { strcat(ret, ",+cov"); } + if (new_bits == 2) { strcat(ret, ",+cov"); } return ret; @@ -540,7 +575,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { if (unlikely(len == 0)) { return 0; } u8 *queue_fn = ""; - u8 hnb = '\0'; + u8 new_bits = '\0'; s32 fd; u8 keeping = 0, res; u64 cksum = 0; @@ -566,7 +601,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { /* Keep only if there are new bits in the map, add to queue for future fuzzing, etc. */ - if (!(hnb = has_new_bits(afl, afl->virgin_bits))) { + if (!(new_bits = has_new_bits(afl, afl->virgin_bits))) { if (unlikely(afl->crash_mode)) { ++afl->total_crashes; } return 0; @@ -576,7 +611,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #ifndef SIMPLE_FILES queue_fn = alloc_printf("%s/queue/id:%06u,%s", afl->out_dir, - afl->queued_paths, describe_op(afl, hnb)); + afl->queued_paths, describe_op(afl, new_bits)); #else @@ -619,7 +654,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) { #endif - if (hnb == 2) { + if (new_bits == 2) { afl->queue_top->has_new_cov = 1; ++afl->queued_with_cov; diff --git a/src/afl-fuzz-mutators.c b/src/afl-fuzz-mutators.c index 1d14f657..0c85458e 100644 --- a/src/afl-fuzz-mutators.c +++ b/src/afl-fuzz-mutators.c @@ -151,7 +151,11 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* Mutator */ /* "afl_custom_init", optional for backward compatibility */ mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); - if (!mutator->afl_custom_init) FATAL("Symbol 'afl_custom_init' not found."); + if (!mutator->afl_custom_init) { + + FATAL("Symbol 'afl_custom_init' not found."); + + } /* "afl_custom_fuzz" or "afl_custom_mutator", required */ mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); @@ -161,49 +165,74 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'."); mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); - if (!mutator->afl_custom_fuzz) + if (!mutator->afl_custom_fuzz) { + WARNF("Symbol 'afl_custom_mutator' not found."); + } + } /* "afl_custom_introspection", optional */ #ifdef INTROSPECTION mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); - if (!mutator->afl_custom_introspection) + if (!mutator->afl_custom_introspection) { + ACTF("optional symbol 'afl_custom_introspection' not found."); + + } + #endif /* "afl_custom_fuzz_count", optional */ mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); - if (!mutator->afl_custom_fuzz_count) + if (!mutator->afl_custom_fuzz_count) { + ACTF("optional symbol 'afl_custom_fuzz_count' not found."); + } + /* "afl_custom_deinit", optional for backward compatibility */ mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); - if (!mutator->afl_custom_deinit) + if (!mutator->afl_custom_deinit) { + FATAL("Symbol 'afl_custom_deinit' not found."); + } + /* "afl_custom_post_process", optional */ mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process"); - if (!mutator->afl_custom_post_process) + if (!mutator->afl_custom_post_process) { + ACTF("optional symbol 'afl_custom_post_process' not found."); + } + u8 notrim = 0; /* "afl_custom_init_trim", optional */ mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); - if (!mutator->afl_custom_init_trim) + if (!mutator->afl_custom_init_trim) { + ACTF("optional symbol 'afl_custom_init_trim' not found."); + } + /* "afl_custom_trim", optional */ mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); - if (!mutator->afl_custom_trim) + if (!mutator->afl_custom_trim) { + ACTF("optional symbol 'afl_custom_trim' not found."); + } + /* "afl_custom_post_trim", optional */ mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); - if (!mutator->afl_custom_post_trim) + if (!mutator->afl_custom_post_trim) { + ACTF("optional symbol 'afl_custom_post_trim' not found."); + } + if (notrim) { mutator->afl_custom_init_trim = NULL; @@ -217,31 +246,54 @@ struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation"); - if (!mutator->afl_custom_havoc_mutation) + if (!mutator->afl_custom_havoc_mutation) { + ACTF("optional symbol 'afl_custom_havoc_mutation' not found."); + } + /* "afl_custom_havoc_mutation", optional */ mutator->afl_custom_havoc_mutation_probability = dlsym(dh, "afl_custom_havoc_mutation_probability"); - if (!mutator->afl_custom_havoc_mutation_probability) + if (!mutator->afl_custom_havoc_mutation_probability) { + ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found."); + } + /* "afl_custom_queue_get", optional */ mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); - if (!mutator->afl_custom_queue_get) + if (!mutator->afl_custom_queue_get) { + ACTF("optional symbol 'afl_custom_queue_get' not found."); + } + /* "afl_custom_queue_new_entry", optional */ mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry"); - if (!mutator->afl_custom_queue_new_entry) + if (!mutator->afl_custom_queue_new_entry) { + ACTF("optional symbol 'afl_custom_queue_new_entry' not found"); + } + + /* "afl_custom_describe", optional */ + mutator->afl_custom_describe = dlsym(dh, "afl_custom_describe"); + if (!mutator->afl_custom_describe) { + + ACTF("Symbol 'afl_custom_describe' not found."); + + } + OKF("Custom mutator '%s' installed successfully.", fn); /* Initialize the custom mutator */ - if (mutator->afl_custom_init) + if (mutator->afl_custom_init) { + mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); + } + mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation); mutator->stacked_custom_prob = 6; // like one of the default mutations in havoc diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 0adc3719..ca48f72a 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1790,11 +1790,16 @@ custom_mutator_stage: afl->current_custom_fuzz = el; - if (el->afl_custom_fuzz_count) + if (el->afl_custom_fuzz_count) { + afl->stage_max = el->afl_custom_fuzz_count(el->data, out_buf, len); - else + + } else { + afl->stage_max = saved_max; + } + has_custom_fuzz = true; afl->stage_short = el->name_short; diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index f44ec48f..8cca4801 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit f44ec48f8d5929f243522c1152b5b3c0985a5548 +Subproject commit 8cca4801adb767dce7cf72202d7d25bdb420cf7d -- cgit 1.4.1 From 1f34b9f8e185998e4c9c4b96b0c1878b6615115a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 4 Dec 2020 05:28:36 +0100 Subject: added python mutator, documentation --- docs/custom_mutators.md | 11 +++++++++++ include/afl-fuzz.h | 36 +++++++++++++++++++----------------- src/afl-fuzz-python.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 17 deletions(-) (limited to 'docs') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 6e16ba0f..6d3c9f38 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -34,6 +34,7 @@ C/C++: void *afl_custom_init(afl_state_t *afl, unsigned int seed); unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size); size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size); +const char *afl_custom_describe(void *data, size_t max_description_len); size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf); int afl_custom_init_trim(void *data, unsigned char *buf, size_t buf_size); size_t afl_custom_trim(void *data, unsigned char **out_buf); @@ -57,6 +58,9 @@ def fuzz_count(buf, add_buf, max_size): def fuzz(buf, add_buf, max_size): return mutated_out +def describe(max_description_length): + return "description_of_current_mutation" + def post_process(buf): return out_buf @@ -112,6 +116,13 @@ def introspection(): You would only skip this if `post_process` is used to fix checksums etc. so you are using it e.g. as a post processing library. +- `describe` (optional): + + When this function is called, is shall describe the current testcase, + generated by the last mutation. This will be called, for example, + to give the written testcase a name after a crash ocurred. + Using it can help to reproduce crashing mutations. + - `havoc_mutation` and `havoc_mutation_probability` (optional): `havoc_mutation` performs a single custom mutation on a given input. This diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 92465e7e..4efa1a6c 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -312,6 +312,7 @@ enum { /* 10 */ PY_FUNC_QUEUE_GET, /* 11 */ PY_FUNC_QUEUE_NEW_ENTRY, /* 12 */ PY_FUNC_INTROSPECTION, + /* 13 */ PY_FUNC_DESCRIBE, PY_FUNC_COUNT }; @@ -755,7 +756,7 @@ struct custom_mutator { * When afl-fuzz was compiled with INTROSPECTION=1 then custom mutators can * also give introspection information back with this function. * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @return pointer to a text string (const char*) */ const char *(*afl_custom_introspection)(void *data); @@ -771,7 +772,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param buf Buffer containing the test case * @param buf_size Size of the test case * @return The amount of fuzzes to perform on this queue entry, 0 = skip @@ -783,7 +784,7 @@ struct custom_mutator { * * (Optional for now. Required in the future) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Pointer to the input data to be mutated and the mutated * output * @param[in] buf_size Size of the input/output data @@ -805,12 +806,13 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case - * @param[in] max_size Maximum size of the mutated output. The mutation must - * not produce data larger than max_size. - * @return A valid ptr to a 0-terminated string, or NULL on error. + * @param data pointer returned by afl_customm_init for this custom mutator + * @paramp[in] max_description_len maximum size avaliable for the description. + * A longer return string is legal, but will be truncated. + * @return A valid ptr to a 0-terminated string. + * An empty or NULL return will result in a default description */ - const char *(*afl_custom_describe)(void *data, size_t max_size); + const char *(*afl_custom_describe)(void *data, size_t max_description_len); /** * A post-processing function to use right before AFL writes the test case to @@ -819,7 +821,7 @@ struct custom_mutator { * (Optional) If this functionality is not needed, simply don't define this * function. * - * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Buffer containing the test case to be executed * @param[in] buf_size Size of the test case * @param[out] out_buf Pointer to the buffer storing the test case after @@ -846,7 +848,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param buf Buffer containing the test case * @param buf_size Size of the test case * @return The amount of possible iteration steps to trim the input. @@ -865,7 +867,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param[out] out_buf Pointer to the buffer containing the trimmed test case. * The library can reuse a buffer for each call * and will have to free the buf (for example in deinit) @@ -880,7 +882,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param success Indicates if the last trim operation was successful. * @return The next trim iteration index (from 0 to the maximum amount of * steps returned in init_trim). Negative on error. @@ -893,7 +895,7 @@ struct custom_mutator { * * (Optional) * - * @param[in] data pointer returned in afl_custom_init for this fuzz case + * @param[in] data pointer returned in afl_custom_init by this custom mutator * @param[in] buf Pointer to the input data to be mutated and the mutated * output * @param[in] buf_size Size of input data @@ -912,7 +914,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @return The probability (0-100). */ u8 (*afl_custom_havoc_mutation_probability)(void *data); @@ -922,7 +924,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param filename File name of the test case in the queue entry * @return Return True(1) if the fuzzer will fuzz the queue entry, and * False(0) otherwise. @@ -935,7 +937,7 @@ struct custom_mutator { * * (Optional) * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator * @param filename_new_queue File name of the new queue entry * @param filename_orig_queue File name of the original queue entry. This * argument can be NULL while initializing the fuzzer @@ -945,7 +947,7 @@ struct custom_mutator { /** * Deinitialize the custom mutator. * - * @param data pointer returned in afl_custom_init for this fuzz case + * @param data pointer returned in afl_custom_init by this custom mutator */ void (*afl_custom_deinit)(void *data); diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 9ac4403b..8760194c 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -111,6 +111,37 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, } +static const char *custom_describe_py(void * py_mutator, + size_t max_description_len) { + + PyObject *py_args, *py_value; + + py_args = PyTuple_New(1); + + PyLong_FromSize_t(max_description_len); + + /* add_buf */ + py_value = PyLong_FromSize_t(max_description_len); + if (!py_value) { + + Py_DECREF(py_args); + FATAL("Failed to convert arguments"); + + } + + PyTuple_SetItem(py_args, 0, py_value); + + py_value = PyObject_CallObject( + ((py_mutator_t *)py_mutator)->py_functions[PY_FUNC_DESCRIBE], py_args); + + Py_DECREF(py_args); + + if (py_value != NULL) { return PyBytes_AsString(py_value); } + + return NULL; + +} + static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { (void)afl; @@ -156,6 +187,8 @@ static py_mutator_t *init_py_module(afl_state_t *afl, u8 *module_name) { py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "fuzz"); if (!py_functions[PY_FUNC_FUZZ]) py_functions[PY_FUNC_FUZZ] = PyObject_GetAttrString(py_module, "mutate"); + py_functions[PY_FUNC_DESCRIBE] = + PyObject_GetAttrString(py_module, "describe"); py_functions[PY_FUNC_FUZZ_COUNT] = PyObject_GetAttrString(py_module, "fuzz_count"); if (!py_functions[PY_FUNC_FUZZ]) @@ -342,6 +375,12 @@ struct custom_mutator *load_custom_mutator_py(afl_state_t *afl, if (py_functions[PY_FUNC_FUZZ]) { mutator->afl_custom_fuzz = fuzz_py; } + if (py_functions[PY_FUNC_DESCRIBE]) { + + mutator->afl_custom_describe = custom_describe_py; + + } + if (py_functions[PY_FUNC_POST_PROCESS]) { mutator->afl_custom_post_process = post_process_py; -- cgit 1.4.1 From cf0c49dec5341791d4a1f5f8f796dbce370696bf Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Tue, 8 Dec 2020 17:46:29 +0100 Subject: typos/wording --- docs/custom_mutators.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'docs') diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index 6d3c9f38..a2c544e3 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -106,7 +106,7 @@ def introspection(): of fuzzing attempts with this input based on a few factors. If however the custom mutator wants to set this number instead on how often it is called for a specific queue entry, use this function. - This function in mostly useful if **not** `AFL_CUSTOM_MUTATOR_ONLY` is used. + This function is most useful if `AFL_CUSTOM_MUTATOR_ONLY` is **not** used. - `fuzz` (optional): @@ -114,19 +114,19 @@ def introspection(): additional test case. Note that this function is optional - but it makes sense to use it. You would only skip this if `post_process` is used to fix checksums etc. - so you are using it e.g. as a post processing library. + so if you are using it e.g. as a post processing library. - `describe` (optional): - When this function is called, is shall describe the current testcase, + When this function is called, it shall describe the current testcase, generated by the last mutation. This will be called, for example, - to give the written testcase a name after a crash ocurred. + to name the written testcase file after a crash occurred. Using it can help to reproduce crashing mutations. - `havoc_mutation` and `havoc_mutation_probability` (optional): `havoc_mutation` performs a single custom mutation on a given input. This - mutation is stacked with the other mutations in havoc. The other method, + mutation is stacked with other mutations in havoc. The other method, `havoc_mutation_probability`, returns the probability that `havoc_mutation` is called in havoc. By default, it is 6%. @@ -182,7 +182,7 @@ trimmed input. Here's a quick API description: on this input (e.g. if your input has n elements and you want to remove them one by one, return n, if you do a binary search, return log(n), and so on). - If your trimming algorithm doesn't allow you to determine the amount of + If your trimming algorithm doesn't allow to determine the amount of (remaining) steps easily (esp. while running), then you can alternatively return 1 here and always return 0 in `post_trim` until you are finished and no steps remain. In that case, returning 1 in `post_trim` will end the @@ -224,19 +224,20 @@ Optionally, the following environment variables are supported: - `AFL_PYTHON_ONLY` - Deprecated and removed, use `AFL_CUSTOM_MUTATOR_ONLY` instead - trimming can cause the same test breakage like havoc and splice. + Deprecated and removed, use `AFL_CUSTOM_MUTATOR_ONLY` instead. - `AFL_DEBUG` - When combined with `AFL_NO_UI`, this causes the C trimming code to emit additional messages about the performance and actions of your custom trimmer. Use this to see if it works :) + When combined with `AFL_NO_UI`, this causes the C trimming code to emit + additional messages about the performance and actions of your custom + trimmer. Use this to see if it works :) ## 3) Usage ### Prerequisite -For Python mutator, the python 3 or 2 development package is required. On -Debian/Ubuntu/Kali this can be done: +For Python mutators, the python 3 or 2 development package is required. On +Debian/Ubuntu/Kali it can be installed like this: ```bash sudo apt install python3-dev @@ -254,13 +255,13 @@ In case your setup is different, set the necessary variables like this: ### Custom Mutator Preparation -For C/C++ mutator, the source code must be compiled as a shared object: +For C/C++ mutators, the source code must be compiled as a shared object: ```bash gcc -shared -Wall -O3 example.c -o example.so ``` Note that if you specify multiple custom mutators, the corresponding functions will be called in the order in which they are specified. e.g first `post_process` function of -`example_first.so` will be called and then that of `example_second.so` +`example_first.so` will be called and then that of `example_second.so`. ### Run -- cgit 1.4.1 From a8e568f248628c39e0bc34173470988034723627 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 9 Dec 2020 11:30:04 +0100 Subject: move libdislocator, libtokencap and qbdi_mode to utils/ --- GNUmakefile | 18 +- README.md | 3 +- docs/Changelog.md | 1 + docs/life_pro_tips.md | 2 +- docs/notes_for_asan.md | 2 +- libdislocator/Makefile | 43 -- libdislocator/README.md | 68 --- libdislocator/libdislocator.so.c | 544 ---------------------- libtokencap/Makefile | 94 ---- libtokencap/README.md | 64 --- libtokencap/libtokencap.so.c | 794 --------------------------------- qbdi_mode/README.md | 199 --------- qbdi_mode/assets/screen1.png | Bin 88333 -> 0 bytes qbdi_mode/build.sh | 57 --- qbdi_mode/demo-so.c | 41 -- qbdi_mode/template.cpp | 251 ----------- utils/libdislocator/Makefile | 43 ++ utils/libdislocator/README.md | 68 +++ utils/libdislocator/libdislocator.so.c | 544 ++++++++++++++++++++++ utils/libtokencap/Makefile | 94 ++++ utils/libtokencap/README.md | 64 +++ utils/libtokencap/libtokencap.so.c | 794 +++++++++++++++++++++++++++++++++ utils/qbdi_mode/README.md | 199 +++++++++ utils/qbdi_mode/assets/screen1.png | Bin 0 -> 88333 bytes utils/qbdi_mode/build.sh | 57 +++ utils/qbdi_mode/demo-so.c | 41 ++ utils/qbdi_mode/template.cpp | 251 +++++++++++ 27 files changed, 2168 insertions(+), 2168 deletions(-) delete mode 100644 libdislocator/Makefile delete mode 100644 libdislocator/README.md delete mode 100644 libdislocator/libdislocator.so.c delete mode 100644 libtokencap/Makefile delete mode 100644 libtokencap/README.md delete mode 100644 libtokencap/libtokencap.so.c delete mode 100755 qbdi_mode/README.md delete mode 100644 qbdi_mode/assets/screen1.png delete mode 100755 qbdi_mode/build.sh delete mode 100755 qbdi_mode/demo-so.c delete mode 100755 qbdi_mode/template.cpp create mode 100644 utils/libdislocator/Makefile create mode 100644 utils/libdislocator/README.md create mode 100644 utils/libdislocator/libdislocator.so.c create mode 100644 utils/libtokencap/Makefile create mode 100644 utils/libtokencap/README.md create mode 100644 utils/libtokencap/libtokencap.so.c create mode 100755 utils/qbdi_mode/README.md create mode 100644 utils/qbdi_mode/assets/screen1.png create mode 100755 utils/qbdi_mode/build.sh create mode 100755 utils/qbdi_mode/demo-so.c create mode 100755 utils/qbdi_mode/template.cpp (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index 309a7d4c..de398ed7 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -493,8 +493,6 @@ endif code-format: ./.custom-format.py -i src/*.c ./.custom-format.py -i include/*.h - ./.custom-format.py -i libdislocator/*.c - ./.custom-format.py -i libtokencap/*.c ./.custom-format.py -i instrumentation/*.h ./.custom-format.py -i instrumentation/*.cc ./.custom-format.py -i instrumentation/*.c @@ -545,8 +543,8 @@ clean: rm -f $(PROGS) libradamsa.so afl-fuzz-document afl-as as afl-g++ afl-clang afl-clang++ *.o src/*.o *~ a.out core core.[1-9][0-9]* *.stackdump .test .test1 .test2 test-instr .test-instr0 .test-instr1 afl-qemu-trace afl-gcc-fast afl-gcc-pass.so afl-g++-fast ld *.so *.8 test/unittests/*.o test/unittests/unit_maybe_alloc test/unittests/preallocable .afl-* afl-gcc afl-g++ afl-clang afl-clang++ test/unittests/unit_hash test/unittests/unit_rand -$(MAKE) -f GNUmakefile.llvm clean -$(MAKE) -f GNUmakefile.gcc_plugin clean - $(MAKE) -C libdislocator clean - $(MAKE) -C libtokencap clean + $(MAKE) -C utils/libdislocator clean + $(MAKE) -C utils/libtokencap clean $(MAKE) -C utils/afl_network_proxy clean $(MAKE) -C utils/socket_fuzzing clean $(MAKE) -C utils/argv_fuzzing clean @@ -570,8 +568,8 @@ deepclean: clean distrib: all -$(MAKE) -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin - $(MAKE) -C libdislocator - $(MAKE) -C libtokencap + $(MAKE) -C utils/libdislocator + $(MAKE) -C utils/libtokencap $(MAKE) -C utils/aflpp_driver $(MAKE) -C utils/afl_network_proxy $(MAKE) -C utils/socket_fuzzing @@ -581,8 +579,8 @@ distrib: all .PHONY: binary-only binary-only: all - $(MAKE) -C libdislocator - $(MAKE) -C libtokencap + $(MAKE) -C utils/libdislocator + $(MAKE) -C utils/libtokencap $(MAKE) -C utils/afl_network_proxy $(MAKE) -C utils/socket_fuzzing $(MAKE) -C utils/argv_fuzzing @@ -593,8 +591,8 @@ binary-only: all source-only: all -$(MAKE) -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin - $(MAKE) -C libdislocator - $(MAKE) -C libtokencap + $(MAKE) -C utils/libdislocator + $(MAKE) -C utils/libtokencap $(MAKE) -C utils/aflpp_driver %.8: % diff --git a/README.md b/README.md index 94d5008e..16c85e26 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ behaviours and defaults: editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). * examples/ got renamed to utils/ + * libtokencap/ libdislocator/ and qdbi_mode/ were moved to utils/ ## Contents @@ -547,7 +548,7 @@ something is already included for your data format, and tell afl-fuzz to load that dictionary by adding `-x dictionaries/FORMAT.dict`. With afl-clang-lto you have an autodictionary generation for which you need to do nothing except to use afl-clang-lto as the compiler. You also have the option to generate -a dictionary yourself, see [libtokencap/README.md](libtokencap/README.md). +a dictionary yourself, see [utils/libtokencap/README.md](utils/libtokencap/README.md). afl-fuzz has a variety of options that help to workaround target quirks like specific locations for the input file (`-f`), not performing deterministic diff --git a/docs/Changelog.md b/docs/Changelog.md index 5201eb8b..1a47d18f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,6 +12,7 @@ sending a mail to . ### Version ++3.00a (develop) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ - examples/ renamed to utils/ + - moved libdislocator, libtokencap and qdbi_mode to utils/ - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz diff --git a/docs/life_pro_tips.md b/docs/life_pro_tips.md index 77845c63..50ad75d4 100644 --- a/docs/life_pro_tips.md +++ b/docs/life_pro_tips.md @@ -13,7 +13,7 @@ See [parallel_fuzzing.md](parallel_fuzzing.md) for step-by-step tips. ## Improve the odds of spotting memory corruption bugs with libdislocator.so! -It's easy. Consult [libdislocator/README.md](../libdislocator/README.md) for usage tips. +It's easy. Consult [utils/libdislocator/README.md](../utils/libdislocator/README.md) for usage tips. ## Want to understand how your target parses a particular input file? diff --git a/docs/notes_for_asan.md b/docs/notes_for_asan.md index f08ae3fb..2b3bc028 100644 --- a/docs/notes_for_asan.md +++ b/docs/notes_for_asan.md @@ -35,7 +35,7 @@ no sanitizers compiled in. There is also the option of generating a corpus using a non-ASAN binary, and then feeding it to an ASAN-instrumented one to check for bugs. This is faster, and can give you somewhat comparable results. You can also try using -libdislocator (see libdislocator/README.dislocator.md in the parent directory) as a +libdislocator (see [utils/libdislocator/README.dislocator.md](../utils/libdislocator/README.dislocator.md) in the parent directory) as a lightweight and hassle-free (but less thorough) alternative. ## 2) Long version diff --git a/libdislocator/Makefile b/libdislocator/Makefile deleted file mode 100644 index 1c2fcaa7..00000000 --- a/libdislocator/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# -# american fuzzy lop++ - libdislocator -# ---------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2016 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -PREFIX ?= /usr/local -HELPER_PATH = $(PREFIX)/lib/afl - -VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) - -CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2 -CFLAGS += -I ../include/ -Wall -g -Wno-pointer-sign - -CFLAGS_ADD=$(USEHUGEPAGE:1=-DUSEHUGEPAGE) -CFLAGS += $(CFLAGS_ADD) - -all: libdislocator.so - -VPATH = .. -libdislocator.so: libdislocator.so.c ../config.h - $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC libdislocator.so.c -o ../$@ $(LDFLAGS) - -.NOTPARALLEL: clean - -clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* - rm -f ../libdislocator.so - -install: all - install -m 755 -d $${DESTDIR}$(HELPER_PATH) - install -m 755 ../libdislocator.so $${DESTDIR}$(HELPER_PATH) - install -m 644 -T README.md $${DESTDIR}$(HELPER_PATH)/README.dislocator.md - diff --git a/libdislocator/README.md b/libdislocator/README.md deleted file mode 100644 index 1785463e..00000000 --- a/libdislocator/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# libdislocator, an abusive allocator - - (See ../README.md for the general instruction manual.) - -This is a companion library that can be used as a drop-in replacement for the -libc allocator in the fuzzed binaries. It improves the odds of bumping into -heap-related security bugs in several ways: - - - It allocates all buffers so that they are immediately adjacent to a - subsequent PROT_NONE page, causing most off-by-one reads and writes to - immediately segfault, - - - It adds a canary immediately below the allocated buffer, to catch writes - to negative offsets (won't catch reads, though), - - - It sets the memory returned by malloc() to garbage values, improving the - odds of crashing when the target accesses uninitialized data, - - - It sets freed memory to PROT_NONE and does not actually reuse it, causing - most use-after-free bugs to segfault right away, - - - It forces all realloc() calls to return a new address - and sets - PROT_NONE on the original block. This catches use-after-realloc bugs, - - - It checks for calloc() overflows and can cause soft or hard failures - of alloc requests past a configurable memory limit (AFL_LD_LIMIT_MB, - AFL_LD_HARD_FAIL). - - - Optionally, in platforms supporting it, huge pages can be used by passing - USEHUGEPAGE=1 to make. - - - Size alignment to `max_align_t` can be enforced with AFL_ALIGNED_ALLOC=1. - In this case, a tail canary is inserted in the padding bytes at the end - of the allocated zone. This reduce the ability of libdislocator to detect - off-by-one bugs but also it make slibdislocator compliant to the C standard. - -Basically, it is inspired by some of the non-default options available for the -OpenBSD allocator - see malloc.conf(5) on that platform for reference. It is -also somewhat similar to several other debugging libraries, such as gmalloc -and DUMA - but is simple, plug-and-play, and designed specifically for fuzzing -jobs. - -Note that it does nothing for stack-based memory handling errors. The --fstack-protector-all setting for GCC / clang, enabled when using AFL_HARDEN, -can catch some subset of that. - -The allocator is slow and memory-intensive (even the tiniest allocation uses up -4 kB of physical memory and 8 kB of virtual mem), making it completely unsuitable -for "production" uses; but it can be faster and more hassle-free than ASAN / MSAN -when fuzzing small, self-contained binaries. - -To use this library, run AFL like so: - -``` -AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...] -``` - -You *have* to specify path, even if it's just ./libdislocator.so or -$PWD/libdislocator.so. - -Similarly to afl-tmin, the library is not "proprietary" and can be used with -other fuzzers or testing tools without the need for any code tweaks. It does not -require AFL-instrumented binaries to work. - -Note that the AFL_PRELOAD approach (which AFL internally maps to LD_PRELOAD or -DYLD_INSERT_LIBRARIES, depending on the OS) works only if the target binary is -dynamically linked. Otherwise, attempting to use the library will have no -effect. diff --git a/libdislocator/libdislocator.so.c b/libdislocator/libdislocator.so.c deleted file mode 100644 index 2324e390..00000000 --- a/libdislocator/libdislocator.so.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - - american fuzzy lop++ - dislocator, an abusive allocator - ----------------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This is a companion library that can be used as a drop-in replacement - for the libc allocator in the fuzzed binaries. See README.dislocator.md for - more info. - - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#ifdef __APPLE__ - #include -#endif - -#ifdef __FreeBSD__ - #include -#endif - -#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__HAIKU__) - #include - #ifdef __linux__ - #include - #include - #endif - #ifdef __NR_getrandom - #define arc4random_buf(p, l) \ - do { \ - \ - ssize_t rd = syscall(__NR_getrandom, p, l, 0); \ - if (rd != l) DEBUGF("getrandom failed"); \ - \ - } while (0) - - #else - #include - #define arc4random_buf(p, l) \ - do { \ - \ - srand(time(NULL)); \ - u32 i; \ - u8 *ptr = (u8 *)p; \ - for (i = 0; i < l; i++) \ - ptr[i] = rand() % INT_MAX; \ - \ - } while (0) - - #endif -#endif - -#include "config.h" -#include "types.h" - -#if __STDC_VERSION__ < 201112L || \ - (defined(__FreeBSD__) && __FreeBSD_version < 1200000) -// use this hack if not C11 -typedef struct { - - long long __ll; - long double __ld; - -} max_align_t; - -#endif - -#define ALLOC_ALIGN_SIZE (_Alignof(max_align_t)) - -#ifndef PAGE_SIZE - #define PAGE_SIZE 4096 -#endif /* !PAGE_SIZE */ - -#ifndef MAP_ANONYMOUS - #define MAP_ANONYMOUS MAP_ANON -#endif /* !MAP_ANONYMOUS */ - -#define SUPER_PAGE_SIZE 1 << 21 - -/* Error / message handling: */ - -#define DEBUGF(_x...) \ - do { \ - \ - if (alloc_verbose) { \ - \ - if (++call_depth == 1) { \ - \ - fprintf(stderr, "[AFL] " _x); \ - fprintf(stderr, "\n"); \ - \ - } \ - call_depth--; \ - \ - } \ - \ - } while (0) - -#define FATAL(_x...) \ - do { \ - \ - if (++call_depth == 1) { \ - \ - fprintf(stderr, "*** [AFL] " _x); \ - fprintf(stderr, " ***\n"); \ - abort(); \ - \ - } \ - call_depth--; \ - \ - } while (0) - -/* Macro to count the number of pages needed to store a buffer: */ - -#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE) - -/* Canary & clobber bytes: */ - -#define ALLOC_CANARY 0xAACCAACC -#define ALLOC_CLOBBER 0xCC - -#define TAIL_ALLOC_CANARY 0xAC - -#define PTR_C(_p) (((u32 *)(_p))[-1]) -#define PTR_L(_p) (((u32 *)(_p))[-2]) - -/* Configurable stuff (use AFL_LD_* to set): */ - -static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */ -static u8 alloc_verbose, /* Additional debug messages */ - hard_fail, /* abort() when max_mem exceeded? */ - no_calloc_over, /* abort() on calloc() overflows? */ - align_allocations; /* Force alignment to sizeof(void*) */ - -#if defined __OpenBSD__ || defined __APPLE__ - #define __thread - #warning no thread support available -#endif -static __thread size_t total_mem; /* Currently allocated mem */ - -static __thread u32 call_depth; /* To avoid recursion via fprintf() */ -static u32 alloc_canary; - -/* This is the main alloc function. It allocates one page more than necessary, - sets that tailing page to PROT_NONE, and then increments the return address - so that it is right-aligned to that boundary. Since it always uses mmap(), - the returned memory will be zeroed. */ - -static void *__dislocator_alloc(size_t len) { - - u8 * ret, *base; - size_t tlen; - int flags, fd, sp; - - if (total_mem + len > max_mem || total_mem + len < total_mem) { - - if (hard_fail) FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024); - - DEBUGF("total allocs exceed %u MB, returning NULL", max_mem / 1024 / 1024); - - return NULL; - - } - - size_t rlen; - if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) - rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; - else - rlen = len; - - /* We will also store buffer length and a canary below the actual buffer, so - let's add 8 bytes for that. */ - - base = NULL; - tlen = (1 + PG_COUNT(rlen + 8)) * PAGE_SIZE; - flags = MAP_PRIVATE | MAP_ANONYMOUS; - fd = -1; -#if defined(USEHUGEPAGE) - sp = (rlen >= SUPER_PAGE_SIZE && !(rlen % SUPER_PAGE_SIZE)); - - #if defined(__APPLE__) - if (sp) fd = VM_FLAGS_SUPERPAGE_SIZE_2MB; - #elif defined(__linux__) - if (sp) flags |= MAP_HUGETLB; - #elif defined(__FreeBSD__) - if (sp) flags |= MAP_ALIGNED_SUPER; - #elif defined(__sun) - if (sp) { - - base = (void *)(caddr_t)(1 << 21); - flags |= MAP_ALIGN; - - } - - #endif -#else - (void)sp; -#endif - - ret = (u8 *)mmap(base, tlen, PROT_READ | PROT_WRITE, flags, fd, 0); -#if defined(USEHUGEPAGE) - /* We try one more time with regular call */ - if (ret == MAP_FAILED) { - - #if defined(__APPLE__) - fd = -1; - #elif defined(__linux__) - flags &= -MAP_HUGETLB; - #elif defined(__FreeBSD__) - flags &= -MAP_ALIGNED_SUPER; - #elif defined(__sun) - flags &= -MAP_ALIGN; - #endif - ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0); - - } - -#endif - - if (ret == MAP_FAILED) { - - if (hard_fail) FATAL("mmap() failed on alloc (OOM?)"); - - DEBUGF("mmap() failed on alloc (OOM?)"); - - return NULL; - - } - - /* Set PROT_NONE on the last page. */ - - if (mprotect(ret + PG_COUNT(rlen + 8) * PAGE_SIZE, PAGE_SIZE, PROT_NONE)) - FATAL("mprotect() failed when allocating memory"); - - /* Offset the return pointer so that it's right-aligned to the page - boundary. */ - - ret += PAGE_SIZE * PG_COUNT(rlen + 8) - rlen - 8; - - /* Store allocation metadata. */ - - ret += 8; - - PTR_L(ret) = len; - PTR_C(ret) = alloc_canary; - - total_mem += len; - - if (rlen != len) { - - size_t i; - for (i = len; i < rlen; ++i) - ret[i] = TAIL_ALLOC_CANARY; - - } - - return ret; - -} - -/* The "user-facing" wrapper for calloc(). This just checks for overflows and - displays debug messages if requested. */ - -void *calloc(size_t elem_len, size_t elem_cnt) { - - void *ret; - - size_t len = elem_len * elem_cnt; - - /* Perform some sanity checks to detect obvious issues... */ - - if (elem_cnt && len / elem_cnt != elem_len) { - - if (no_calloc_over) { - - DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len, - elem_cnt); - return NULL; - - } - - FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt); - - } - - ret = __dislocator_alloc(len); - - DEBUGF("calloc(%zu, %zu) = %p [%zu total]", elem_len, elem_cnt, ret, - total_mem); - - return ret; - -} - -/* The wrapper for malloc(). Roughly the same, also clobbers the returned - memory (unlike calloc(), malloc() is not guaranteed to return zeroed - memory). */ - -void *malloc(size_t len) { - - void *ret; - - ret = __dislocator_alloc(len); - - DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem); - - if (ret && len) memset(ret, ALLOC_CLOBBER, len); - - return ret; - -} - -/* The wrapper for free(). This simply marks the entire region as PROT_NONE. - If the region is already freed, the code will segfault during the attempt to - read the canary. Not very graceful, but works, right? */ - -void free(void *ptr) { - - u32 len; - - DEBUGF("free(%p)", ptr); - - if (!ptr) return; - - if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on free()"); - - len = PTR_L(ptr); - - total_mem -= len; - - if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) { - - u8 * ptr_ = ptr; - size_t rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; - for (; len < rlen; ++len) - if (ptr_[len] != TAIL_ALLOC_CANARY) - FATAL("bad tail allocator canary on free()"); - - } - - /* Protect everything. Note that the extra page at the end is already - set as PROT_NONE, so we don't need to touch that. */ - - ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8; - - if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE)) - FATAL("mprotect() failed when freeing memory"); - - /* Keep the mapping; this is wasteful, but prevents ptr reuse. */ - -} - -/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer, - move data, and then free (aka mprotect()) the original one. */ - -void *realloc(void *ptr, size_t len) { - - void *ret; - - ret = malloc(len); - - if (ret && ptr) { - - if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on realloc()"); - // Here the tail canary check is delayed to free() - - memcpy(ret, ptr, MIN(len, PTR_L(ptr))); - free(ptr); - - } - - DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem); - - return ret; - -} - -/* posix_memalign we mainly check the proper alignment argument - if the requested size fits within the alignment we do - a normal request */ - -int posix_memalign(void **ptr, size_t align, size_t len) { - - // if (*ptr == NULL) return EINVAL; // (andrea) Why? I comment it out for now - if ((align % 2) || (align % sizeof(void *))) return EINVAL; - if (len == 0) { - - *ptr = NULL; - return 0; - - } - - size_t rem = len % align; - if (rem) len += align - rem; - - *ptr = __dislocator_alloc(len); - - if (*ptr && len) memset(*ptr, ALLOC_CLOBBER, len); - - DEBUGF("posix_memalign(%p %zu, %zu) [*ptr = %p]", ptr, align, len, *ptr); - - return 0; - -} - -/* just the non-posix fashion */ - -void *memalign(size_t align, size_t len) { - - void *ret = NULL; - - if (posix_memalign(&ret, align, len)) { - - DEBUGF("memalign(%zu, %zu) failed", align, len); - - } - - return ret; - -} - -/* sort of C11 alias of memalign only more severe, alignment-wise */ - -void *aligned_alloc(size_t align, size_t len) { - - void *ret = NULL; - - if ((len % align)) return NULL; - - if (posix_memalign(&ret, align, len)) { - - DEBUGF("aligned_alloc(%zu, %zu) failed", align, len); - - } - - return ret; - -} - -/* specific BSD api mainly checking possible overflow for the size */ - -void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) { - - const size_t elem_lim = 1UL << (sizeof(size_t) * 4); - const size_t elem_tot = elem_len * elem_cnt; - void * ret = NULL; - - if ((elem_len >= elem_lim || elem_cnt >= elem_lim) && elem_len > 0 && - elem_cnt > (SIZE_MAX / elem_len)) { - - DEBUGF("reallocarray size overflow (%zu)", elem_tot); - - } else { - - ret = realloc(ptr, elem_tot); - - } - - return ret; - -} - -#if !defined(__ANDROID__) -size_t malloc_usable_size(void *ptr) { - -#else -size_t malloc_usable_size(const void *ptr) { - -#endif - - return ptr ? PTR_L(ptr) : 0; - -} - -__attribute__((constructor)) void __dislocator_init(void) { - - u8 *tmp = (u8 *)getenv("AFL_LD_LIMIT_MB"); - - if (tmp) { - - u8 *tok; - s32 mmem = (s32)strtol((char *)tmp, (char **)&tok, 10); - if (*tok != '\0' || errno == ERANGE) FATAL("Bad value for AFL_LD_LIMIT_MB"); - max_mem = mmem * 1024 * 1024; - - } - - alloc_canary = ALLOC_CANARY; - tmp = (u8 *)getenv("AFL_RANDOM_ALLOC_CANARY"); - - if (tmp) arc4random_buf(&alloc_canary, sizeof(alloc_canary)); - - alloc_verbose = !!getenv("AFL_LD_VERBOSE"); - hard_fail = !!getenv("AFL_LD_HARD_FAIL"); - no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER"); - align_allocations = !!getenv("AFL_ALIGNED_ALLOC"); - -} - -/* NetBSD fault handler specific api subset */ - -void (*esetfunc(void (*fn)(int, const char *, ...)))(int, const char *, ...) { - - /* Might not be meaningful to implement; upper calls already report errors */ - return NULL; - -} - -void *emalloc(size_t len) { - - return malloc(len); - -} - -void *ecalloc(size_t elem_len, size_t elem_cnt) { - - return calloc(elem_len, elem_cnt); - -} - -void *erealloc(void *ptr, size_t len) { - - return realloc(ptr, len); - -} - diff --git a/libtokencap/Makefile b/libtokencap/Makefile deleted file mode 100644 index 2343125f..00000000 --- a/libtokencap/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -# -# american fuzzy lop++ - libtokencap -# -------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2016 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -PREFIX ?= /usr/local -HELPER_PATH = $(PREFIX)/lib/afl -DOC_PATH ?= $(PREFIX)/share/doc/afl -MAN_PATH ?= $(PREFIX)/share/man/man8 - -VERSION = $(shell grep '^\#define VERSION ' ../config.h | cut -d '"' -f2) - -CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2 -CFLAGS += -I ../include/ -Wall -g -Wno-pointer-sign - - -UNAME_S =$(shell uname -s)# GNU make -UNAME_S:sh=uname -s # BSD make -_UNIQ=_QINU_ - - _OS_DL = $(_UNIQ)$(UNAME_S) - __OS_DL = $(_OS_DL:$(_UNIQ)Linux=$(_UNIQ)) - ___OS_DL = $(__OS_DL:$(_UNIQ)Darwin=$(_UNIQ)) - ____OS_DL = $(___OS_DL:$(_UNIQ)$(UNAME_S)=) -_____OS_DL = $(____OS_DL:$(_UNIQ)="-ldl") - - _OS_TARGET = $(___OS_DL:$(_UNIQ)FreeBSD=$(_UNIQ)) - __OS_TARGET = $(_OS_TARGET:$(_UNIQ)OpenBSD=$(_UNIQ)) - ___OS_TARGET = $(__OS_TARGET:$(_UNIQ)NetBSD=$(_UNIQ)) - ____OS_TARGET = $(___OS_TARGET:$(_UNIQ)Haiku=$(_UNIQ)) - _____OS_TARGET = $(____OS_TARGET:$(_UNIQ)SunOS=$(_UNIQ)) -______OS_TARGET = $(_____OS_TARGET:$(_UNIQ)$(UNAME_S)=) - -TARGETS = $(______OS_TARGET:$(_UNIQ)=libtokencap.so) - -LDFLAGS += $(_____OS_DL) - -#ifeq "$(shell uname)" "Linux" -# TARGETS = libtokencap.so -# LDFLAGS += -ldl -#endif -#ifeq "$(shell uname)" "Darwin" -# TARGETS = libtokencap.so -# LDFLAGS += -ldl -#endif -#ifeq "$(shell uname)" "FreeBSD" -# TARGETS = libtokencap.so -#endif -#ifeq "$(shell uname)" "OpenBSD" -# TARGETS = libtokencap.so -#endif -#ifeq "$(shell uname)" "NetBSD" -# TARGETS = libtokencap.so -#endif -#ifeq "$(shell uname)" "DragonFly" -# TARGETS = libtokencap.so -# LDFLAGS += -ldl -#endif -all: $(TARGETS) - -VPATH = .. -libtokencap.so: libtokencap.so.c ../config.h - $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC $< -o ../$@ $(LDFLAGS) - -.NOTPARALLEL: clean - -debug: - @echo $(UNAME_S)$(_UNIQ) | hexdump -C - @echo from $(____OS_DL) : $(_UNIQ)$(UNAME_S) = -\> $(_____OS_DL) - @echo from $(_____OS_DL) : $(_UNIQ) = -ldl -\> $(______OS_DL) - @echo from $(____OS_DL) : $(_UNIQ)FreeBSD = $(_UNIQ) -\> $(_OS_TARGET) - @echo from $(_OS_TARGET) : $(_UNIQ)OpenBSD = $(_UNIQ) -\> $(__OS_TARGET) - @echo from $(__OS_TARGET) : $(_UNIQ)NetBSD = $(_UNIQ) -\> $(___OS_TARGET) - @echo from $(___OS_TARGET) : $(_UNIQ)$(_UNIQ) = -\> $(____OS_TARGET) - @echo from $(____OS_TARGET) : $(_UNIQ) = libtokencap.so -\> $(TARGETS) - -clean: - rm -f *.o *.so *~ a.out core core.[1-9][0-9]* - rm -f ../libtokencap.so - -install: all - install -m 755 -d $${DESTDIR}$(HELPER_PATH) - install -m 755 ../libtokencap.so $${DESTDIR}$(HELPER_PATH) - install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.tokencap.md diff --git a/libtokencap/README.md b/libtokencap/README.md deleted file mode 100644 index 13a440da..00000000 --- a/libtokencap/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# strcmp() / memcmp() token capture library - - (See ../README.md for the general instruction manual.) - -This companion library allows you to instrument `strcmp()`, `memcmp()`, -and related functions to automatically extract syntax tokens passed to any of -these libcalls. The resulting list of tokens may be then given as a starting -dictionary to afl-fuzz (the -x option) to improve coverage on subsequent -fuzzing runs. - -This may help improving coverage in some targets, and do precisely nothing in -others. In some cases, it may even make things worse: if libtokencap picks up -syntax tokens that are not used to process the input data, but that are a part -of - say - parsing a config file... well, you're going to end up wasting a lot -of CPU time on trying them out in the input stream. In other words, use this -feature with care. Manually screening the resulting dictionary is almost -always a necessity. - -As for the actual operation: the library stores tokens, without any deduping, -by appending them to a file specified via AFL_TOKEN_FILE. If the variable is not -set, the tool uses stderr (which is probably not what you want). - -Similarly to afl-tmin, the library is not "proprietary" and can be used with -other fuzzers or testing tools without the need for any code tweaks. It does not -require AFL-instrumented binaries to work. - -To use the library, you *need* to make sure that your fuzzing target is compiled -with -fno-builtin and is linked dynamically. If you wish to automate the first -part without mucking with CFLAGS in Makefiles, you can set AFL_NO_BUILTIN=1 -when using afl-gcc. This setting specifically adds the following flags: - -``` - -fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp - -fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr - -fno-builtin-strcasestr -``` - -The next step is simply loading this library via LD_PRELOAD. The optimal usage -pattern is to allow afl-fuzz to fuzz normally for a while and build up a corpus, -and then fire off the target binary, with libtokencap.so loaded, on every file -found by AFL in that earlier run. This demonstrates the basic principle: - -``` - export AFL_TOKEN_FILE=$PWD/temp_output.txt - - for i in /queue/id*; do - LD_PRELOAD=/path/to/libtokencap.so \ - /path/to/target/program [...params, including $i...] - done - - sort -u temp_output.txt >afl_dictionary.txt -``` - -If you don't get any results, the target library is probably not using strcmp() -and memcmp() to parse input; or you haven't compiled it with -fno-builtin; or -the whole thing isn't dynamically linked, and LD_PRELOAD is having no effect. - -Portability hints: There is probably no particularly portable and non-invasive -way to distinguish between read-only and read-write memory mappings. -The `__tokencap_load_mappings()` function is the only thing that would -need to be changed for other OSes. - -Current supported OSes are: Linux, Darwin, FreeBSD (thanks to @devnexen) - diff --git a/libtokencap/libtokencap.so.c b/libtokencap/libtokencap.so.c deleted file mode 100644 index 3629e804..00000000 --- a/libtokencap/libtokencap.so.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - - american fuzzy lop++ - extract tokens passed to strcmp / memcmp - ------------------------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This Linux-only companion library allows you to instrument strcmp(), - memcmp(), and related functions to automatically extract tokens. - See README.tokencap.md for more info. - - */ - -#ifndef _GNU_SOURCE - #define _GNU_SOURCE -#endif -#include -#include -#include -#include -#include -#include - -#include "../types.h" -#include "../config.h" - -#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \ - !defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__ && \ - !defined(__HAIKU__) && !defined(__sun) - #error "Sorry, this library is unsupported in this platform for now!" -#endif /* !__linux__ && !__APPLE__ && ! __FreeBSD__ && ! __OpenBSD__ && \ - !__NetBSD__*/ - -#if defined __APPLE__ - #include - #include -#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ - #include - #include - #if !defined __NetBSD__ - #include - #endif - #include -#elif defined __HAIKU__ - #include -#elif defined __sun - /* For map addresses the old struct is enough */ - #include - #include -#endif - -#include - -#ifdef RTLD_NEXT -/* The libc functions are a magnitude faster than our replacements. - Use them when RTLD_NEXT is available. */ -int (*__libc_strcmp)(const char *str1, const char *str2); -int (*__libc_strncmp)(const char *str1, const char *str2, size_t len); -int (*__libc_strcasecmp)(const char *str1, const char *str2); -int (*__libc_strncasecmp)(const char *str1, const char *str2, size_t len); -int (*__libc_memcmp)(const void *mem1, const void *mem2, size_t len); -int (*__libc_bcmp)(const void *mem1, const void *mem2, size_t len); -char *(*__libc_strstr)(const char *haystack, const char *needle); -char *(*__libc_strcasestr)(const char *haystack, const char *needle); -void *(*__libc_memmem)(const void *haystack, size_t haystack_len, - const void *needle, size_t needle_len); -#endif - -/* Mapping data and such */ - -#define MAX_MAPPINGS 1024 - -static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS]; - -static u32 __tokencap_ro_cnt; -static u8 __tokencap_ro_loaded; -static int __tokencap_out_file = -1; -static pid_t __tokencap_pid = -1; - -/* Identify read-only regions in memory. Only parameters that fall into these - ranges are worth dumping when passed to strcmp() and so on. Read-write - regions are far more likely to contain user input instead. */ - -static void __tokencap_load_mappings(void) { - -#if defined __linux__ - - u8 buf[MAX_LINE]; - FILE *f = fopen("/proc/self/maps", "r"); - - __tokencap_ro_loaded = 1; - - if (!f) return; - - while (fgets(buf, MAX_LINE, f)) { - - u8 rf, wf; - void *st, *en; - - if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue; - if (wf == 'w' || rf != 'r') continue; - - __tokencap_ro[__tokencap_ro_cnt].st = (void *)st; - __tokencap_ro[__tokencap_ro_cnt].en = (void *)en; - - if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; - - } - - fclose(f); - -#elif defined __APPLE__ - - struct vm_region_submap_info_64 region; - mach_msg_type_number_t cnt = VM_REGION_SUBMAP_INFO_COUNT_64; - vm_address_t base = 0; - vm_size_t size = 0; - natural_t depth = 0; - - __tokencap_ro_loaded = 1; - - while (1) { - - if (vm_region_recurse_64(mach_task_self(), &base, &size, &depth, - (vm_region_info_64_t)®ion, - &cnt) != KERN_SUCCESS) - break; - - if (region.is_submap) { - - depth++; - - } else { - - /* We only care of main map addresses and the read only kinds */ - if ((region.protection & VM_PROT_READ) && - !(region.protection & VM_PROT_WRITE)) { - - __tokencap_ro[__tokencap_ro_cnt].st = (void *)base; - __tokencap_ro[__tokencap_ro_cnt].en = (void *)(base + size); - - if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; - - } - - base += size; - size = 0; - - } - - } - -#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ - - #if defined __FreeBSD__ - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; - #elif defined __OpenBSD__ - int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid}; - #elif defined __NetBSD__ - int mib[] = {CTL_VM, VM_PROC, VM_PROC_MAP, __tokencap_pid, - sizeof(struct kinfo_vmentry)}; - #endif - char * buf, *low, *high; - size_t miblen = sizeof(mib) / sizeof(mib[0]); - size_t len; - - if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) return; - - #if defined __FreeBSD__ || defined __NetBSD__ - len = len * 4 / 3; - #elif defined __OpenBSD__ - len -= len % sizeof(struct kinfo_vmentry); - #endif - - buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (buf == MAP_FAILED) return; - - if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { - - munmap(buf, len); - return; - - } - - low = buf; - high = low + len; - - __tokencap_ro_loaded = 1; - - while (low < high) { - - struct kinfo_vmentry *region = (struct kinfo_vmentry *)low; - - #if defined __FreeBSD__ || defined __NetBSD__ - - #if defined __FreeBSD__ - size_t size = region->kve_structsize; - - if (size == 0) break; - #elif defined __NetBSD__ - size_t size = sizeof(*region); - #endif - - /* We go through the whole mapping of the process and track read-only - * addresses */ - if ((region->kve_protection & KVME_PROT_READ) && - !(region->kve_protection & KVME_PROT_WRITE)) { - - #elif defined __OpenBSD__ - - size_t size = sizeof(*region); - - /* We go through the whole mapping of the process and track read-only - * addresses */ - if ((region->kve_protection & KVE_PROT_READ) && - !(region->kve_protection & KVE_PROT_WRITE)) { - - #endif - __tokencap_ro[__tokencap_ro_cnt].st = (void *)region->kve_start; - __tokencap_ro[__tokencap_ro_cnt].en = (void *)region->kve_end; - - if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; - - } - - low += size; - - } - - munmap(buf, len); -#elif defined __HAIKU__ - image_info ii; - int32_t group = 0; - - __tokencap_ro_loaded = 1; - - while (get_next_image_info(0, &group, &ii) == B_OK) { - - __tokencap_ro[__tokencap_ro_cnt].st = ii.text; - __tokencap_ro[__tokencap_ro_cnt].en = ((char *)ii.text) + ii.text_size; - - if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; - - } - -#elif defined __sun - prmap_t *c, *map; - char path[PATH_MAX]; - ssize_t r; - size_t hint; - int fd; - - snprintf(path, sizeof(path), "/proc/%ld/map", getpid()); - fd = open(path, O_RDONLY); - hint = (1 << 20); - map = malloc(hint); - - __tokencap_ro_loaded = 1; - - for (; (r = pread(fd, map, hint, 0)) == hint;) { - - hint <<= 1; - map = realloc(map, hint); - - } - - for (c = map; r > 0; c++, r -= sizeof(prmap_t)) { - - __tokencap_ro[__tokencap_ro_cnt].st = (void *)c->pr_vaddr; - __tokencap_ro[__tokencap_ro_cnt].en = (void *)(c->pr_vaddr + c->pr_size); - - if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; - - } - - free(map); - close(fd); -#endif - -} - -/* Check an address against the list of read-only mappings. */ - -static u8 __tokencap_is_ro(const void *ptr) { - - u32 i; - - if (!__tokencap_ro_loaded) __tokencap_load_mappings(); - - for (i = 0; i < __tokencap_ro_cnt; i++) - if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1; - - return 0; - -} - -/* Dump an interesting token to output file, quoting and escaping it - properly. */ - -static void __tokencap_dump(const u8 *ptr, size_t len, u8 is_text) { - - u8 buf[MAX_AUTO_EXTRA * 4 + 1]; - u32 i; - u32 pos = 0; - - if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || __tokencap_out_file == -1) - return; - - for (i = 0; i < len; i++) { - - if (is_text && !ptr[i]) break; - - switch (ptr[i]) { - - case 0 ... 31: - case 127 ... 255: - case '\"': - case '\\': - - sprintf(buf + pos, "\\x%02x", ptr[i]); - pos += 4; - break; - - default: - buf[pos++] = ptr[i]; - - } - - } - - buf[pos] = 0; - - int wrt_ok = (1 == write(__tokencap_out_file, "\"", 1)); - wrt_ok &= (pos == write(__tokencap_out_file, buf, pos)); - wrt_ok &= (2 == write(__tokencap_out_file, "\"\n", 2)); - -} - -/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used - only if the target is compiled with -fno-builtins and linked dynamically. */ - -#undef strcmp - -int strcmp(const char *str1, const char *str2) { - - if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); - if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); - -#ifdef RTLD_NEXT - if (__libc_strcmp) return __libc_strcmp(str1, str2); -#endif - - while (1) { - - const unsigned char c1 = *str1, c2 = *str2; - - if (c1 != c2) return (c1 > c2) ? 1 : -1; - if (!c1) return 0; - str1++; - str2++; - - } - -} - -#undef strncmp - -int strncmp(const char *str1, const char *str2, size_t len) { - - if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); - if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); - -#ifdef RTLD_NEXT - if (__libc_strncmp) return __libc_strncmp(str1, str2, len); -#endif - - while (len--) { - - unsigned char c1 = *str1, c2 = *str2; - - if (c1 != c2) return (c1 > c2) ? 1 : -1; - if (!c1) return 0; - str1++; - str2++; - - } - - return 0; - -} - -#undef strcasecmp - -int strcasecmp(const char *str1, const char *str2) { - - if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); - if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); - -#ifdef RTLD_NEXT - if (__libc_strcasecmp) return __libc_strcasecmp(str1, str2); -#endif - - while (1) { - - const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2); - - if (c1 != c2) return (c1 > c2) ? 1 : -1; - if (!c1) return 0; - str1++; - str2++; - - } - -} - -#undef strncasecmp - -int strncasecmp(const char *str1, const char *str2, size_t len) { - - if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); - if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); - -#ifdef RTLD_NEXT - if (__libc_strncasecmp) return __libc_strncasecmp(str1, str2, len); -#endif - - while (len--) { - - const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2); - - if (c1 != c2) return (c1 > c2) ? 1 : -1; - if (!c1) return 0; - str1++; - str2++; - - } - - return 0; - -} - -#undef memcmp - -int memcmp(const void *mem1, const void *mem2, size_t len) { - - if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); - if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); - -#ifdef RTLD_NEXT - if (__libc_memcmp) return __libc_memcmp(mem1, mem2, len); -#endif - - const char *strmem1 = (const char *)mem1; - const char *strmem2 = (const char *)mem2; - - while (len--) { - - const unsigned char c1 = *strmem1, c2 = *strmem2; - if (c1 != c2) return (c1 > c2) ? 1 : -1; - strmem1++; - strmem2++; - - } - - return 0; - -} - -#undef bcmp - -int bcmp(const void *mem1, const void *mem2, size_t len) { - - if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); - if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); - -#ifdef RTLD_NEXT - if (__libc_bcmp) return __libc_bcmp(mem1, mem2, len); -#endif - - const char *strmem1 = (const char *)mem1; - const char *strmem2 = (const char *)mem2; - - while (len--) { - - int diff = *strmem1 ^ *strmem2; - if (diff != 0) return 1; - strmem1++; - strmem2++; - - } - - return 0; - -} - -#undef strstr - -char *strstr(const char *haystack, const char *needle) { - - if (__tokencap_is_ro(haystack)) - __tokencap_dump(haystack, strlen(haystack), 1); - - if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); - -#ifdef RTLD_NEXT - if (__libc_strstr) return __libc_strstr(haystack, needle); -#endif - - do { - - const char *n = needle; - const char *h = haystack; - - while (*n && *h && *n == *h) - n++, h++; - - if (!*n) return (char *)haystack; - - } while (*(haystack++)); - - return 0; - -} - -#undef strcasestr - -char *strcasestr(const char *haystack, const char *needle) { - - if (__tokencap_is_ro(haystack)) - __tokencap_dump(haystack, strlen(haystack), 1); - - if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); - -#ifdef RTLD_NEXT - if (__libc_strcasestr) return __libc_strcasestr(haystack, needle); -#endif - - do { - - const char *n = needle; - const char *h = haystack; - - while (*n && *h && tolower((int)*n) == tolower((int)*h)) - n++, h++; - - if (!*n) return (char *)haystack; - - } while (*(haystack++)); - - return 0; - -} - -#undef memmem - -void *memmem(const void *haystack, size_t haystack_len, const void *needle, - size_t needle_len) { - - if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, haystack_len, 1); - - if (__tokencap_is_ro(needle)) __tokencap_dump(needle, needle_len, 1); - -#ifdef RTLD_NEXT - if (__libc_memmem) - return __libc_memmem(haystack, haystack_len, needle, needle_len); -#endif - - const char *n = (const char *)needle; - const char *h = (const char *)haystack; - if (haystack_len < needle_len) return 0; - if (needle_len == 0) return (void *)haystack; - if (needle_len == 1) return memchr(haystack, *n, haystack_len); - - const char *end = h + (haystack_len - needle_len); - - do { - - if (*h == *n) { - - if (memcmp(h, n, needle_len) == 0) return (void *)h; - - } - - } while (h++ <= end); - - return 0; - -} - -/* Common libraries wrappers (from honggfuzz) */ - -/* - * Apache's httpd wrappers - */ -int ap_cstr_casecmp(const char *s1, const char *s2) { - - return strcasecmp(s1, s2); - -} - -int ap_cstr_casecmpn(const char *s1, const char *s2, size_t n) { - - return strncasecmp(s1, s2, n); - -} - -const char *ap_strcasestr(const char *s1, const char *s2) { - - return strcasestr(s1, s2); - -} - -int apr_cstr_casecmp(const char *s1, const char *s2) { - - return strcasecmp(s1, s2); - -} - -int apr_cstr_casecmpn(const char *s1, const char *s2, size_t n) { - - return strncasecmp(s1, s2, n); - -} - -/* - * *SSL wrappers - */ -int CRYPTO_memcmp(const void *m1, const void *m2, size_t len) { - - return memcmp(m1, m2, len); - -} - -int OPENSSL_memcmp(const void *m1, const void *m2, size_t len) { - - return memcmp(m1, m2, len); - -} - -int OPENSSL_strcasecmp(const char *s1, const char *s2) { - - return strcasecmp(s1, s2); - -} - -int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t len) { - - return strncasecmp(s1, s2, len); - -} - -int32_t memcmpct(const void *s1, const void *s2, size_t len) { - - return memcmp(s1, s2, len); - -} - -/* - * libXML wrappers - */ -int xmlStrncmp(const char *s1, const char *s2, int len) { - - if (len <= 0) { return 0; } - if (s1 == s2) { return 0; } - if (s1 == NULL) { return -1; } - if (s2 == NULL) { return 1; } - return strncmp(s1, s2, (size_t)len); - -} - -int xmlStrcmp(const char *s1, const char *s2) { - - if (s1 == s2) { return 0; } - if (s1 == NULL) { return -1; } - if (s2 == NULL) { return 1; } - return strcmp(s1, s2); - -} - -int xmlStrEqual(const char *s1, const char *s2) { - - if (s1 == s2) { return 1; } - if (s1 == NULL) { return 0; } - if (s2 == NULL) { return 0; } - if (strcmp(s1, s2) == 0) { return 1; } - return 0; - -} - -int xmlStrcasecmp(const char *s1, const char *s2) { - - if (s1 == s2) { return 0; } - if (s1 == NULL) { return -1; } - if (s2 == NULL) { return 1; } - return strcasecmp(s1, s2); - -} - -int xmlStrncasecmp(const char *s1, const char *s2, int len) { - - if (len <= 0) { return 0; } - if (s1 == s2) { return 0; } - if (s1 == NULL) { return -1; } - if (s2 == NULL) { return 1; } - return strncasecmp(s1, s2, (size_t)len); - -} - -const char *xmlStrstr(const char *haystack, const char *needle) { - - if (haystack == NULL) { return NULL; } - if (needle == NULL) { return NULL; } - return strstr(haystack, needle); - -} - -const char *xmlStrcasestr(const char *haystack, const char *needle) { - - if (haystack == NULL) { return NULL; } - if (needle == NULL) { return NULL; } - return strcasestr(haystack, needle); - -} - -/* - * Samba wrappers - */ -int memcmp_const_time(const void *s1, const void *s2, size_t n) { - - return memcmp(s1, s2, n); - -} - -bool strcsequal(const void *s1, const void *s2) { - - if (s1 == s2) { return true; } - if (!s1 || !s2) { return false; } - return (strcmp(s1, s2) == 0); - -} - -/* bcmp/memcmp BSD flavors, similar to CRYPTO_memcmp */ - -int timingsafe_bcmp(const void *mem1, const void *mem2, size_t len) { - - return bcmp(mem1, mem2, len); - -} - -int timingsafe_memcmp(const void *mem1, const void *mem2, size_t len) { - - return memcmp(mem1, mem2, len); - -} - -/* Init code to open the output file (or default to stderr). */ - -__attribute__((constructor)) void __tokencap_init(void) { - - u8 *fn = getenv("AFL_TOKEN_FILE"); - if (fn) __tokencap_out_file = open(fn, O_RDWR | O_CREAT | O_APPEND, 0655); - if (__tokencap_out_file == -1) __tokencap_out_file = STDERR_FILENO; - __tokencap_pid = getpid(); - -#ifdef RTLD_NEXT - __libc_strcmp = dlsym(RTLD_NEXT, "strcmp"); - __libc_strncmp = dlsym(RTLD_NEXT, "strncmp"); - __libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp"); - __libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp"); - __libc_memcmp = dlsym(RTLD_NEXT, "memcmp"); - __libc_bcmp = dlsym(RTLD_NEXT, "bcmp"); - __libc_strstr = dlsym(RTLD_NEXT, "strstr"); - __libc_strcasestr = dlsym(RTLD_NEXT, "strcasestr"); - __libc_memmem = dlsym(RTLD_NEXT, "memmem"); -#endif - -} - -/* closing as best as we can the tokens file */ -__attribute__((destructor)) void __tokencap_shutdown(void) { - - if (__tokencap_out_file != STDERR_FILENO) close(__tokencap_out_file); - -} - diff --git a/qbdi_mode/README.md b/qbdi_mode/README.md deleted file mode 100755 index 641a6e85..00000000 --- a/qbdi_mode/README.md +++ /dev/null @@ -1,199 +0,0 @@ -# qbdi-based binary-only instrumentation for afl-fuzz - -## 1) Introduction - -The code in ./qbdi_mode allows you to build a standalone feature that -using the QBDI framework to fuzz android native library. - - -## 2) Build - -First download the Android NDK - -``` -https://developer.android.com/ndk/downloads -https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip -``` - -Then unzip it and build the standalone-toolchain -For x86_64 standalone-toolchain - -``` -unzip android-ndk-r20-linux-x86_64.zip -cd android-ndk-r20/ -./build/tools/make_standalone_toolchain.py --arch x86_64 --api 21 --install-dir ../android-standalone-toolchain-x86_64 -``` - -For x86 standalone-toolchain - -``` -./build/tools/make_standalone_toolchain.py --arch x86 --api 21 --install-dir ../android-standalone-toolchain-x86 -``` - -In alternative you can also use the prebuilt toolchain, in that case make sure to set the proper CC and CXX env variables because there are many different compilers for each API version in the prebuilt toolchain. - -For example: - -``` -export STANDALONE_TOOLCHAIN_PATH=~/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/ -export CC=x86_64-linux-android21-clang -export CXX=x86_64-linux-android21-clang++ -``` - -Then download the QBDI SDK from website - -``` -https://qbdi.quarkslab.com/ -``` - -For Android x86_64 -``` -https://github.com/QBDI/QBDI/releases/download/v0.7.0/QBDI-0.7.0-android-X86_64.tar.gz -``` - -Then decompress the sdk - -``` -mkdir android-qbdi-sdk-x86_64 -cp QBDI-0.7.0-android-X86_64.tar.gz android-qbdi-sdk-x86_64/ -cd android-qbdi-sdk-x86_64/ -tar xvf QBDI-0.7.0-android-X86_64.tar.gz -``` - -Now set the `STANDALONE_TOOLCHAIN_PATH` to the path of standalone-toolchain - -``` -export STANDALONE_TOOLCHAIN_PATH=/home/hac425/workspace/android-standalone-toolchain-x86_64 -``` - -set the `QBDI_SDK_PATH` to the path of QBDI SDK - -``` -export QBDI_SDK_PATH=/home/hac425/workspace/AFLplusplus/qbdi_mode/android-qbdi-sdk-x86_64/ -``` - -Then run the build.sh - -``` -./build.sh x86_64 -``` - -this could build the afl-fuzz and also the qbdi template for android x86_64 - - -### Example - -The demo-so.c is an vulnerable library, it has a function for test - -```c -int target_func(char *buf, int size) { - - printf("buffer:%p, size:%p\n", buf, size); - switch (buf[0]) { - - case 1: - puts("222"); - if (buf[1] == '\x44') { - - puts("null ptr deference"); - *(char *)(0) = 1; - - } - - break; - case 0xff: - if (buf[2] == '\xff') { - - if (buf[1] == '\x44') { - - puts("crash...."); - *(char *)(0xdeadbeef) = 1; - - } - - } - - break; - default: puts("default action"); break; - - } - - return 1; - -} -``` - -This could be build to `libdemo.so`. - -Then we should load the library in template.cpp and find the `target` function address. -```c - void *handle = dlopen(lib_path, RTLD_LAZY); - .......................................... - .......................................... - .......................................... - p_target_func = (target_func)dlsym(handle, "target_func"); -``` - -then we read the data from file and call the function in `fuzz_func` - -```c -QBDI_NOINLINE int fuzz_func() { - - if (afl_setup()) { afl_forkserver(); } - - /* Read the input from file */ - unsigned long len = 0; - char * data = read_file(input_pathname, &len); - - /* Call the target function with the input data */ - p_target_func(data, len); - return 1; - -} -``` - -Just compile it -``` -./build.sh x86_64 -``` - -Then push the `afl-fuzz`, `loader`, `libdemo.so`, the `libQBDI.so` from the QBDI SDK and the `libc++_shared.so` from android-standalone-toolchain to android device - -``` -adb push afl-fuzz /data/local/tmp -adb push libdemo.so /data/local/tmp -adb push loader /data/local/tmp -adb push android-qbdi-sdk-x86_64/usr/local/lib/libQBDI.so /data/local/tmp -adb push ../../android-standalone-toolchain-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so -/data/local/tmp -``` - -In android adb shell, run the loader to test if it runs -``` -cd /data/local/tmp -export LD_LIBRARY_PATH=/data/local/tmp -mkdir in -echo 0000 > in/1 -./loader libdemo.so in/1 -p_target_func:0x716d96a98600 - offset:0x600 - offset:0x580 -buffer:0x716d96609050, size:0x5 - offset:0x628 - offset:0x646 - offset:0x64b - offset:0x65c - offset:0x6df - offset:0x590 -default action - offset:0x6eb -``` - -Now run `afl-fuzz` to fuzz the demo library - -``` -./afl-fuzz -i in -o out -- ./loader /data/local/tmp/libdemo.so @@ -``` - -![screen1](assets/screen1.png) - diff --git a/qbdi_mode/assets/screen1.png b/qbdi_mode/assets/screen1.png deleted file mode 100644 index 3cf1cb76..00000000 Binary files a/qbdi_mode/assets/screen1.png and /dev/null differ diff --git a/qbdi_mode/build.sh b/qbdi_mode/build.sh deleted file mode 100755 index b10971d9..00000000 --- a/qbdi_mode/build.sh +++ /dev/null @@ -1,57 +0,0 @@ - -if [ -z ${STANDALONE_TOOLCHAIN_PATH} ]; then - echo "please set the android-standalone-toolchain path in STANDALONE_TOOLCHAIN_PATH environmental variable" - echo "for example: " - echo " export STANDALONE_TOOLCHAIN_PATH=/home/android-standalone-toolchain-21/" - exit -fi - -if [ -z ${QBDI_SDK_PATH} ]; then - echo "please set the qbdi sdk path in QBDI_SDK_PATH environmental variable" - echo "for example: " - echo " export QBDI_SDK_PATH=/home/QBDI-Android/" - exit -fi - - - -if [ "$1" = "x86" ]; then - echo "build x86 qbdi" - compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/" - if [ -z ${CC} ]; then - export CC=i686-linux-android-gcc - fi - if [ -z ${CXX} ]; then - export CXX=i686-linux-android-g++ - fi -elif [ "$1" = "x86_64" ]; then - echo "build x86_64 qbdi" - compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/" - if [ -z ${CC} ]; then - export CC=x86_64-linux-android-gcc - fi - if [ -z ${CXX} ]; then - export CXX=x86_64-linux-android-g++ - fi -else - echo "usage: ./build.sh arch[x86, x86_64]" - exit -fi - - -CFLAGS="-I${QBDI_SDK_PATH}/usr/local/include/ -L${QBDI_SDK_PATH}/usr/local/lib/" - -echo "[+] Building the QBDI template" -# build the qbdi template -${compiler_prefix}${CXX} -o loader template.cpp -lQBDI -ldl -w -g ${CFLAGS} - -echo "[+] Building the demo library" -# build the demo share library -${compiler_prefix}${CC} -shared -o libdemo.so demo-so.c -w -g - -echo "[+] Building afl-fuzz for Android" -# build afl-fuzz -cd .. -${compiler_prefix}${CC} -DANDROID_DISABLE_FANCY=1 -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ -DAFL_PATH=\"/usr/local/lib/afl\" -DBIN_PATH=\"/usr/local/bin\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -Wno-unused-function src/afl-fuzz-*.c src/afl-fuzz.c src/afl-common.c src/afl-sharedmem.c src/afl-forkserver.c -o qbdi_mode/afl-fuzz -ldl -w - -echo "[+] All done. Enjoy!" diff --git a/qbdi_mode/demo-so.c b/qbdi_mode/demo-so.c deleted file mode 100755 index dd367036..00000000 --- a/qbdi_mode/demo-so.c +++ /dev/null @@ -1,41 +0,0 @@ -#include - -// gcc -shared -o libdemo.so demo-so.c -w -int target_func(char *buf, int size) { - - printf("buffer:%p, size:%p\n", buf, size); - switch (buf[0]) { - - case 1: - puts("222"); - if (buf[1] == '\x44') { - - puts("null ptr deference"); - *(char *)(0) = 1; - - } - - break; - case 0xff: - if (buf[2] == '\xff') { - - if (buf[1] == '\x44') { - - puts("crash...."); - *(char *)(0xdeadbeef) = 1; - - } - - } - - break; - default: - puts("default action"); - break; - - } - - return 1; - -} - diff --git a/qbdi_mode/template.cpp b/qbdi_mode/template.cpp deleted file mode 100755 index b2066cc8..00000000 --- a/qbdi_mode/template.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __ANDROID__ - #include "../include/android-ashmem.h" -#endif - -#include -#include -#include "../config.h" - -#include - -/* NeverZero */ - -#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO) - #define INC_AFL_AREA(loc) \ - asm volatile( \ - "incb (%0, %1, 1)\n" \ - "adcb $0, (%0, %1, 1)\n" \ - : /* no out */ \ - : "r"(afl_area_ptr), "r"(loc) \ - : "memory", "eax") -#else - #define INC_AFL_AREA(loc) afl_area_ptr[loc]++ -#endif - -using namespace QBDI; - -typedef int (*target_func)(char *buf, int size); - -static const size_t STACK_SIZE = 0x100000; // 1MB -static const QBDI::rword FAKE_RET_ADDR = 0x40000; -target_func p_target_func = NULL; -rword module_base = 0; -rword module_end = 0; -static unsigned char - dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */ -unsigned char *afl_area_ptr = NULL; /* Exported for afl_gen_trace */ - -unsigned long afl_prev_loc = 0; - -char input_pathname[PATH_MAX]; - -/* Set up SHM region and initialize other stuff. */ - -int afl_setup(void) { - - char *id_str = getenv(SHM_ENV_VAR); - int shm_id; - if (id_str) { - - shm_id = atoi(id_str); - afl_area_ptr = (unsigned char *)shmat(shm_id, NULL, 0); - if (afl_area_ptr == (void *)-1) return 0; - memset(afl_area_ptr, 0, MAP_SIZE); - - } - - return 1; - -} - -/* Fork server logic, invoked once we hit _start. */ -static void afl_forkserver() { - - static unsigned char tmp[4]; - pid_t child_pid; - - if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; - - while (1) { - - int status; - u32 was_killed; - // wait for afl-fuzz - if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2); - - child_pid = fork(); - if (child_pid < 0) exit(4); - - if (!child_pid) { - - // child return to execute code - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - return; - - } - - // write child pid to afl-fuzz - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5); - - // wait child stop - if (waitpid(child_pid, &status, 0) < 0) exit(6); - - // send child stop status to afl-fuzz - if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7); - - } - -} - -void afl_maybe_log(unsigned long cur_loc) { - - if (afl_area_ptr == NULL) { return; } - unsigned long afl_idx = cur_loc ^ afl_prev_loc; - afl_idx &= MAP_SIZE - 1; - INC_AFL_AREA(afl_idx); - afl_prev_loc = cur_loc >> 1; - -} - -char *read_file(char *path, unsigned long *length) { - - unsigned long len; - char * buf; - - FILE *fp = fopen(path, "rb"); - fseek(fp, 0, SEEK_END); - len = ftell(fp); - buf = (char *)malloc(len); - rewind(fp); - fread(buf, 1, len, fp); - fclose(fp); - *length = len; - return buf; - -} - -QBDI_NOINLINE int fuzz_func() { - - if (afl_setup()) { afl_forkserver(); } - - unsigned long len = 0; - char * data = read_file(input_pathname, &len); - - // printf("In fuzz_func\n"); - p_target_func(data, len); - return 1; - -} - -static QBDI::VMAction bbcallback(QBDI::VMInstanceRef vm, - const QBDI::VMState *state, - QBDI::GPRState * gprState, - QBDI::FPRState *fprState, void *data) { - - // errno = SAVED_ERRNO; - -#ifdef __x86_64__ - unsigned long pc = gprState->rip; -#elif defined(i386) - unsigned long pc = gprState->eip; -#elif defined(__arm__) - unsigned long pc = gprState->pc; -#endif - - // just log the module path - if (pc >= module_base && pc <= module_end) { - - unsigned long offset = pc - module_base; - printf("\toffset:%p\n", offset); - afl_maybe_log(offset); - - } - - return QBDI::VMAction::CONTINUE; - -} - -int main(int argc, char **argv) { - - if (argc < 3) { - - puts("usage: ./loader library_path input_file_path"); - exit(0); - - } - - const char *lib_path; - lib_path = argv[1]; - strcpy(input_pathname, argv[2]); - void *handle = dlopen(lib_path, RTLD_LAZY); - - if (handle == nullptr) { - - perror("Cannot load library"); - exit(EXIT_FAILURE); - - } - - const char *lib_name = lib_path; - if (strrchr(lib_name, '/') != nullptr) lib_name = strrchr(lib_name, '/') + 1; - - // printf("library name:%s\n", lib_name); - // load library module address for log path - for (MemoryMap &map : getCurrentProcessMaps()) { - - // printf("module:%s\n", map.name.c_str()); - if ((map.permission & PF_EXEC) && - strstr(map.name.c_str(), lib_name) != NULL) { - - module_base = map.range.start; - module_end = map.range.end; - - } - - } - - if (module_base == 0) { - - std::cerr << "Fail to find base address" << std::endl; - return -1; - - } - - // printf("module base:%p, module end:%p\n", module_base, module_end); - p_target_func = (target_func)dlsym(handle, "target_func"); - // p_target_func = (target_func)(module_base + 0x61a); - printf("p_target_func:%p\n", p_target_func); - - VM vm; - uint8_t *fakestack = nullptr; - - GPRState *state = vm.getGPRState(); - allocateVirtualStack(state, STACK_SIZE, &fakestack); - vm.addInstrumentedModuleFromAddr(module_base); - vm.addInstrumentedModuleFromAddr((rword)&main); - - vm.addVMEventCB(BASIC_BLOCK_ENTRY, bbcallback, nullptr); - - // QBDI::simulateCall(state, FAKE_RET_ADDR); - // vm.run((rword)&fuzz_func, (rword)FAKE_RET_ADDR); - - rword ret; - vm.call(&ret, (rword)&fuzz_func, {}); - - return 0; - -} - diff --git a/utils/libdislocator/Makefile b/utils/libdislocator/Makefile new file mode 100644 index 00000000..2942c3c3 --- /dev/null +++ b/utils/libdislocator/Makefile @@ -0,0 +1,43 @@ +# +# american fuzzy lop++ - libdislocator +# ---------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2016 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +PREFIX ?= /usr/local +HELPER_PATH = $(PREFIX)/lib/afl + +VERSION = $(shell grep '^\#define VERSION ' ../../config.h | cut -d '"' -f2) + +CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2 +CFLAGS += -I ../../include/ -Wall -g -Wno-pointer-sign + +CFLAGS_ADD=$(USEHUGEPAGE:1=-DUSEHUGEPAGE) +CFLAGS += $(CFLAGS_ADD) + +all: libdislocator.so + +libdislocator.so: libdislocator.so.c ../../config.h + $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC libdislocator.so.c -o $@ $(LDFLAGS) + cp -fv libdislocator.so ../../ + +.NOTPARALLEL: clean + +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* + rm -f ../../libdislocator.so + +install: all + install -m 755 -d $${DESTDIR}$(HELPER_PATH) + install -m 755 ../../libdislocator.so $${DESTDIR}$(HELPER_PATH) + install -m 644 -T README.md $${DESTDIR}$(HELPER_PATH)/README.dislocator.md + diff --git a/utils/libdislocator/README.md b/utils/libdislocator/README.md new file mode 100644 index 00000000..1785463e --- /dev/null +++ b/utils/libdislocator/README.md @@ -0,0 +1,68 @@ +# libdislocator, an abusive allocator + + (See ../README.md for the general instruction manual.) + +This is a companion library that can be used as a drop-in replacement for the +libc allocator in the fuzzed binaries. It improves the odds of bumping into +heap-related security bugs in several ways: + + - It allocates all buffers so that they are immediately adjacent to a + subsequent PROT_NONE page, causing most off-by-one reads and writes to + immediately segfault, + + - It adds a canary immediately below the allocated buffer, to catch writes + to negative offsets (won't catch reads, though), + + - It sets the memory returned by malloc() to garbage values, improving the + odds of crashing when the target accesses uninitialized data, + + - It sets freed memory to PROT_NONE and does not actually reuse it, causing + most use-after-free bugs to segfault right away, + + - It forces all realloc() calls to return a new address - and sets + PROT_NONE on the original block. This catches use-after-realloc bugs, + + - It checks for calloc() overflows and can cause soft or hard failures + of alloc requests past a configurable memory limit (AFL_LD_LIMIT_MB, + AFL_LD_HARD_FAIL). + + - Optionally, in platforms supporting it, huge pages can be used by passing + USEHUGEPAGE=1 to make. + + - Size alignment to `max_align_t` can be enforced with AFL_ALIGNED_ALLOC=1. + In this case, a tail canary is inserted in the padding bytes at the end + of the allocated zone. This reduce the ability of libdislocator to detect + off-by-one bugs but also it make slibdislocator compliant to the C standard. + +Basically, it is inspired by some of the non-default options available for the +OpenBSD allocator - see malloc.conf(5) on that platform for reference. It is +also somewhat similar to several other debugging libraries, such as gmalloc +and DUMA - but is simple, plug-and-play, and designed specifically for fuzzing +jobs. + +Note that it does nothing for stack-based memory handling errors. The +-fstack-protector-all setting for GCC / clang, enabled when using AFL_HARDEN, +can catch some subset of that. + +The allocator is slow and memory-intensive (even the tiniest allocation uses up +4 kB of physical memory and 8 kB of virtual mem), making it completely unsuitable +for "production" uses; but it can be faster and more hassle-free than ASAN / MSAN +when fuzzing small, self-contained binaries. + +To use this library, run AFL like so: + +``` +AFL_PRELOAD=/path/to/libdislocator.so ./afl-fuzz [...other params...] +``` + +You *have* to specify path, even if it's just ./libdislocator.so or +$PWD/libdislocator.so. + +Similarly to afl-tmin, the library is not "proprietary" and can be used with +other fuzzers or testing tools without the need for any code tweaks. It does not +require AFL-instrumented binaries to work. + +Note that the AFL_PRELOAD approach (which AFL internally maps to LD_PRELOAD or +DYLD_INSERT_LIBRARIES, depending on the OS) works only if the target binary is +dynamically linked. Otherwise, attempting to use the library will have no +effect. diff --git a/utils/libdislocator/libdislocator.so.c b/utils/libdislocator/libdislocator.so.c new file mode 100644 index 00000000..2324e390 --- /dev/null +++ b/utils/libdislocator/libdislocator.so.c @@ -0,0 +1,544 @@ +/* + + american fuzzy lop++ - dislocator, an abusive allocator + ----------------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This is a companion library that can be used as a drop-in replacement + for the libc allocator in the fuzzed binaries. See README.dislocator.md for + more info. + + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ + #include +#endif + +#ifdef __FreeBSD__ + #include +#endif + +#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__HAIKU__) + #include + #ifdef __linux__ + #include + #include + #endif + #ifdef __NR_getrandom + #define arc4random_buf(p, l) \ + do { \ + \ + ssize_t rd = syscall(__NR_getrandom, p, l, 0); \ + if (rd != l) DEBUGF("getrandom failed"); \ + \ + } while (0) + + #else + #include + #define arc4random_buf(p, l) \ + do { \ + \ + srand(time(NULL)); \ + u32 i; \ + u8 *ptr = (u8 *)p; \ + for (i = 0; i < l; i++) \ + ptr[i] = rand() % INT_MAX; \ + \ + } while (0) + + #endif +#endif + +#include "config.h" +#include "types.h" + +#if __STDC_VERSION__ < 201112L || \ + (defined(__FreeBSD__) && __FreeBSD_version < 1200000) +// use this hack if not C11 +typedef struct { + + long long __ll; + long double __ld; + +} max_align_t; + +#endif + +#define ALLOC_ALIGN_SIZE (_Alignof(max_align_t)) + +#ifndef PAGE_SIZE + #define PAGE_SIZE 4096 +#endif /* !PAGE_SIZE */ + +#ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON +#endif /* !MAP_ANONYMOUS */ + +#define SUPER_PAGE_SIZE 1 << 21 + +/* Error / message handling: */ + +#define DEBUGF(_x...) \ + do { \ + \ + if (alloc_verbose) { \ + \ + if (++call_depth == 1) { \ + \ + fprintf(stderr, "[AFL] " _x); \ + fprintf(stderr, "\n"); \ + \ + } \ + call_depth--; \ + \ + } \ + \ + } while (0) + +#define FATAL(_x...) \ + do { \ + \ + if (++call_depth == 1) { \ + \ + fprintf(stderr, "*** [AFL] " _x); \ + fprintf(stderr, " ***\n"); \ + abort(); \ + \ + } \ + call_depth--; \ + \ + } while (0) + +/* Macro to count the number of pages needed to store a buffer: */ + +#define PG_COUNT(_l) (((_l) + (PAGE_SIZE - 1)) / PAGE_SIZE) + +/* Canary & clobber bytes: */ + +#define ALLOC_CANARY 0xAACCAACC +#define ALLOC_CLOBBER 0xCC + +#define TAIL_ALLOC_CANARY 0xAC + +#define PTR_C(_p) (((u32 *)(_p))[-1]) +#define PTR_L(_p) (((u32 *)(_p))[-2]) + +/* Configurable stuff (use AFL_LD_* to set): */ + +static u32 max_mem = MAX_ALLOC; /* Max heap usage to permit */ +static u8 alloc_verbose, /* Additional debug messages */ + hard_fail, /* abort() when max_mem exceeded? */ + no_calloc_over, /* abort() on calloc() overflows? */ + align_allocations; /* Force alignment to sizeof(void*) */ + +#if defined __OpenBSD__ || defined __APPLE__ + #define __thread + #warning no thread support available +#endif +static __thread size_t total_mem; /* Currently allocated mem */ + +static __thread u32 call_depth; /* To avoid recursion via fprintf() */ +static u32 alloc_canary; + +/* This is the main alloc function. It allocates one page more than necessary, + sets that tailing page to PROT_NONE, and then increments the return address + so that it is right-aligned to that boundary. Since it always uses mmap(), + the returned memory will be zeroed. */ + +static void *__dislocator_alloc(size_t len) { + + u8 * ret, *base; + size_t tlen; + int flags, fd, sp; + + if (total_mem + len > max_mem || total_mem + len < total_mem) { + + if (hard_fail) FATAL("total allocs exceed %u MB", max_mem / 1024 / 1024); + + DEBUGF("total allocs exceed %u MB, returning NULL", max_mem / 1024 / 1024); + + return NULL; + + } + + size_t rlen; + if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) + rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; + else + rlen = len; + + /* We will also store buffer length and a canary below the actual buffer, so + let's add 8 bytes for that. */ + + base = NULL; + tlen = (1 + PG_COUNT(rlen + 8)) * PAGE_SIZE; + flags = MAP_PRIVATE | MAP_ANONYMOUS; + fd = -1; +#if defined(USEHUGEPAGE) + sp = (rlen >= SUPER_PAGE_SIZE && !(rlen % SUPER_PAGE_SIZE)); + + #if defined(__APPLE__) + if (sp) fd = VM_FLAGS_SUPERPAGE_SIZE_2MB; + #elif defined(__linux__) + if (sp) flags |= MAP_HUGETLB; + #elif defined(__FreeBSD__) + if (sp) flags |= MAP_ALIGNED_SUPER; + #elif defined(__sun) + if (sp) { + + base = (void *)(caddr_t)(1 << 21); + flags |= MAP_ALIGN; + + } + + #endif +#else + (void)sp; +#endif + + ret = (u8 *)mmap(base, tlen, PROT_READ | PROT_WRITE, flags, fd, 0); +#if defined(USEHUGEPAGE) + /* We try one more time with regular call */ + if (ret == MAP_FAILED) { + + #if defined(__APPLE__) + fd = -1; + #elif defined(__linux__) + flags &= -MAP_HUGETLB; + #elif defined(__FreeBSD__) + flags &= -MAP_ALIGNED_SUPER; + #elif defined(__sun) + flags &= -MAP_ALIGN; + #endif + ret = (u8 *)mmap(NULL, tlen, PROT_READ | PROT_WRITE, flags, fd, 0); + + } + +#endif + + if (ret == MAP_FAILED) { + + if (hard_fail) FATAL("mmap() failed on alloc (OOM?)"); + + DEBUGF("mmap() failed on alloc (OOM?)"); + + return NULL; + + } + + /* Set PROT_NONE on the last page. */ + + if (mprotect(ret + PG_COUNT(rlen + 8) * PAGE_SIZE, PAGE_SIZE, PROT_NONE)) + FATAL("mprotect() failed when allocating memory"); + + /* Offset the return pointer so that it's right-aligned to the page + boundary. */ + + ret += PAGE_SIZE * PG_COUNT(rlen + 8) - rlen - 8; + + /* Store allocation metadata. */ + + ret += 8; + + PTR_L(ret) = len; + PTR_C(ret) = alloc_canary; + + total_mem += len; + + if (rlen != len) { + + size_t i; + for (i = len; i < rlen; ++i) + ret[i] = TAIL_ALLOC_CANARY; + + } + + return ret; + +} + +/* The "user-facing" wrapper for calloc(). This just checks for overflows and + displays debug messages if requested. */ + +void *calloc(size_t elem_len, size_t elem_cnt) { + + void *ret; + + size_t len = elem_len * elem_cnt; + + /* Perform some sanity checks to detect obvious issues... */ + + if (elem_cnt && len / elem_cnt != elem_len) { + + if (no_calloc_over) { + + DEBUGF("calloc(%zu, %zu) would overflow, returning NULL", elem_len, + elem_cnt); + return NULL; + + } + + FATAL("calloc(%zu, %zu) would overflow", elem_len, elem_cnt); + + } + + ret = __dislocator_alloc(len); + + DEBUGF("calloc(%zu, %zu) = %p [%zu total]", elem_len, elem_cnt, ret, + total_mem); + + return ret; + +} + +/* The wrapper for malloc(). Roughly the same, also clobbers the returned + memory (unlike calloc(), malloc() is not guaranteed to return zeroed + memory). */ + +void *malloc(size_t len) { + + void *ret; + + ret = __dislocator_alloc(len); + + DEBUGF("malloc(%zu) = %p [%zu total]", len, ret, total_mem); + + if (ret && len) memset(ret, ALLOC_CLOBBER, len); + + return ret; + +} + +/* The wrapper for free(). This simply marks the entire region as PROT_NONE. + If the region is already freed, the code will segfault during the attempt to + read the canary. Not very graceful, but works, right? */ + +void free(void *ptr) { + + u32 len; + + DEBUGF("free(%p)", ptr); + + if (!ptr) return; + + if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on free()"); + + len = PTR_L(ptr); + + total_mem -= len; + + if (align_allocations && (len & (ALLOC_ALIGN_SIZE - 1))) { + + u8 * ptr_ = ptr; + size_t rlen = (len & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; + for (; len < rlen; ++len) + if (ptr_[len] != TAIL_ALLOC_CANARY) + FATAL("bad tail allocator canary on free()"); + + } + + /* Protect everything. Note that the extra page at the end is already + set as PROT_NONE, so we don't need to touch that. */ + + ptr -= PAGE_SIZE * PG_COUNT(len + 8) - len - 8; + + if (mprotect(ptr - 8, PG_COUNT(len + 8) * PAGE_SIZE, PROT_NONE)) + FATAL("mprotect() failed when freeing memory"); + + /* Keep the mapping; this is wasteful, but prevents ptr reuse. */ + +} + +/* Realloc is pretty straightforward, too. We forcibly reallocate the buffer, + move data, and then free (aka mprotect()) the original one. */ + +void *realloc(void *ptr, size_t len) { + + void *ret; + + ret = malloc(len); + + if (ret && ptr) { + + if (PTR_C(ptr) != alloc_canary) FATAL("bad allocator canary on realloc()"); + // Here the tail canary check is delayed to free() + + memcpy(ret, ptr, MIN(len, PTR_L(ptr))); + free(ptr); + + } + + DEBUGF("realloc(%p, %zu) = %p [%zu total]", ptr, len, ret, total_mem); + + return ret; + +} + +/* posix_memalign we mainly check the proper alignment argument + if the requested size fits within the alignment we do + a normal request */ + +int posix_memalign(void **ptr, size_t align, size_t len) { + + // if (*ptr == NULL) return EINVAL; // (andrea) Why? I comment it out for now + if ((align % 2) || (align % sizeof(void *))) return EINVAL; + if (len == 0) { + + *ptr = NULL; + return 0; + + } + + size_t rem = len % align; + if (rem) len += align - rem; + + *ptr = __dislocator_alloc(len); + + if (*ptr && len) memset(*ptr, ALLOC_CLOBBER, len); + + DEBUGF("posix_memalign(%p %zu, %zu) [*ptr = %p]", ptr, align, len, *ptr); + + return 0; + +} + +/* just the non-posix fashion */ + +void *memalign(size_t align, size_t len) { + + void *ret = NULL; + + if (posix_memalign(&ret, align, len)) { + + DEBUGF("memalign(%zu, %zu) failed", align, len); + + } + + return ret; + +} + +/* sort of C11 alias of memalign only more severe, alignment-wise */ + +void *aligned_alloc(size_t align, size_t len) { + + void *ret = NULL; + + if ((len % align)) return NULL; + + if (posix_memalign(&ret, align, len)) { + + DEBUGF("aligned_alloc(%zu, %zu) failed", align, len); + + } + + return ret; + +} + +/* specific BSD api mainly checking possible overflow for the size */ + +void *reallocarray(void *ptr, size_t elem_len, size_t elem_cnt) { + + const size_t elem_lim = 1UL << (sizeof(size_t) * 4); + const size_t elem_tot = elem_len * elem_cnt; + void * ret = NULL; + + if ((elem_len >= elem_lim || elem_cnt >= elem_lim) && elem_len > 0 && + elem_cnt > (SIZE_MAX / elem_len)) { + + DEBUGF("reallocarray size overflow (%zu)", elem_tot); + + } else { + + ret = realloc(ptr, elem_tot); + + } + + return ret; + +} + +#if !defined(__ANDROID__) +size_t malloc_usable_size(void *ptr) { + +#else +size_t malloc_usable_size(const void *ptr) { + +#endif + + return ptr ? PTR_L(ptr) : 0; + +} + +__attribute__((constructor)) void __dislocator_init(void) { + + u8 *tmp = (u8 *)getenv("AFL_LD_LIMIT_MB"); + + if (tmp) { + + u8 *tok; + s32 mmem = (s32)strtol((char *)tmp, (char **)&tok, 10); + if (*tok != '\0' || errno == ERANGE) FATAL("Bad value for AFL_LD_LIMIT_MB"); + max_mem = mmem * 1024 * 1024; + + } + + alloc_canary = ALLOC_CANARY; + tmp = (u8 *)getenv("AFL_RANDOM_ALLOC_CANARY"); + + if (tmp) arc4random_buf(&alloc_canary, sizeof(alloc_canary)); + + alloc_verbose = !!getenv("AFL_LD_VERBOSE"); + hard_fail = !!getenv("AFL_LD_HARD_FAIL"); + no_calloc_over = !!getenv("AFL_LD_NO_CALLOC_OVER"); + align_allocations = !!getenv("AFL_ALIGNED_ALLOC"); + +} + +/* NetBSD fault handler specific api subset */ + +void (*esetfunc(void (*fn)(int, const char *, ...)))(int, const char *, ...) { + + /* Might not be meaningful to implement; upper calls already report errors */ + return NULL; + +} + +void *emalloc(size_t len) { + + return malloc(len); + +} + +void *ecalloc(size_t elem_len, size_t elem_cnt) { + + return calloc(elem_len, elem_cnt); + +} + +void *erealloc(void *ptr, size_t len) { + + return realloc(ptr, len); + +} + diff --git a/utils/libtokencap/Makefile b/utils/libtokencap/Makefile new file mode 100644 index 00000000..8bbdc259 --- /dev/null +++ b/utils/libtokencap/Makefile @@ -0,0 +1,94 @@ +# +# american fuzzy lop++ - libtokencap +# -------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2016 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +PREFIX ?= /usr/local +HELPER_PATH = $(PREFIX)/lib/afl +DOC_PATH ?= $(PREFIX)/share/doc/afl +MAN_PATH ?= $(PREFIX)/share/man/man8 + +VERSION = $(shell grep '^\#define VERSION ' ../../config.h | cut -d '"' -f2) + +CFLAGS ?= -O3 -funroll-loops -D_FORTIFY_SOURCE=2 +CFLAGS += -I ../../include/ -Wall -g -Wno-pointer-sign + + +UNAME_S =$(shell uname -s)# GNU make +UNAME_S:sh=uname -s # BSD make +_UNIQ=_QINU_ + + _OS_DL = $(_UNIQ)$(UNAME_S) + __OS_DL = $(_OS_DL:$(_UNIQ)Linux=$(_UNIQ)) + ___OS_DL = $(__OS_DL:$(_UNIQ)Darwin=$(_UNIQ)) + ____OS_DL = $(___OS_DL:$(_UNIQ)$(UNAME_S)=) +_____OS_DL = $(____OS_DL:$(_UNIQ)="-ldl") + + _OS_TARGET = $(___OS_DL:$(_UNIQ)FreeBSD=$(_UNIQ)) + __OS_TARGET = $(_OS_TARGET:$(_UNIQ)OpenBSD=$(_UNIQ)) + ___OS_TARGET = $(__OS_TARGET:$(_UNIQ)NetBSD=$(_UNIQ)) + ____OS_TARGET = $(___OS_TARGET:$(_UNIQ)Haiku=$(_UNIQ)) + _____OS_TARGET = $(____OS_TARGET:$(_UNIQ)SunOS=$(_UNIQ)) +______OS_TARGET = $(_____OS_TARGET:$(_UNIQ)$(UNAME_S)=) + +TARGETS = $(______OS_TARGET:$(_UNIQ)=libtokencap.so) + +LDFLAGS += $(_____OS_DL) + +#ifeq "$(shell uname)" "Linux" +# TARGETS = libtokencap.so +# LDFLAGS += -ldl +#endif +#ifeq "$(shell uname)" "Darwin" +# TARGETS = libtokencap.so +# LDFLAGS += -ldl +#endif +#ifeq "$(shell uname)" "FreeBSD" +# TARGETS = libtokencap.so +#endif +#ifeq "$(shell uname)" "OpenBSD" +# TARGETS = libtokencap.so +#endif +#ifeq "$(shell uname)" "NetBSD" +# TARGETS = libtokencap.so +#endif +#ifeq "$(shell uname)" "DragonFly" +# TARGETS = libtokencap.so +# LDFLAGS += -ldl +#endif +all: $(TARGETS) + +libtokencap.so: libtokencap.so.c ../../config.h + $(CC) $(CFLAGS) $(CPPFLAGS) -shared -fPIC $< -o $@ $(LDFLAGS) + cp -f libtokencap.so ../../ + +.NOTPARALLEL: clean + +debug: + @echo $(UNAME_S)$(_UNIQ) | hexdump -C + @echo from $(____OS_DL) : $(_UNIQ)$(UNAME_S) = -\> $(_____OS_DL) + @echo from $(_____OS_DL) : $(_UNIQ) = -ldl -\> $(______OS_DL) + @echo from $(____OS_DL) : $(_UNIQ)FreeBSD = $(_UNIQ) -\> $(_OS_TARGET) + @echo from $(_OS_TARGET) : $(_UNIQ)OpenBSD = $(_UNIQ) -\> $(__OS_TARGET) + @echo from $(__OS_TARGET) : $(_UNIQ)NetBSD = $(_UNIQ) -\> $(___OS_TARGET) + @echo from $(___OS_TARGET) : $(_UNIQ)$(_UNIQ) = -\> $(____OS_TARGET) + @echo from $(____OS_TARGET) : $(_UNIQ) = libtokencap.so -\> $(TARGETS) + +clean: + rm -f *.o *.so *~ a.out core core.[1-9][0-9]* + rm -fv ../../libtokencap.so + +install: all + install -m 755 -d $${DESTDIR}$(HELPER_PATH) + install -m 755 ../../libtokencap.so $${DESTDIR}$(HELPER_PATH) + install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.tokencap.md diff --git a/utils/libtokencap/README.md b/utils/libtokencap/README.md new file mode 100644 index 00000000..13a440da --- /dev/null +++ b/utils/libtokencap/README.md @@ -0,0 +1,64 @@ +# strcmp() / memcmp() token capture library + + (See ../README.md for the general instruction manual.) + +This companion library allows you to instrument `strcmp()`, `memcmp()`, +and related functions to automatically extract syntax tokens passed to any of +these libcalls. The resulting list of tokens may be then given as a starting +dictionary to afl-fuzz (the -x option) to improve coverage on subsequent +fuzzing runs. + +This may help improving coverage in some targets, and do precisely nothing in +others. In some cases, it may even make things worse: if libtokencap picks up +syntax tokens that are not used to process the input data, but that are a part +of - say - parsing a config file... well, you're going to end up wasting a lot +of CPU time on trying them out in the input stream. In other words, use this +feature with care. Manually screening the resulting dictionary is almost +always a necessity. + +As for the actual operation: the library stores tokens, without any deduping, +by appending them to a file specified via AFL_TOKEN_FILE. If the variable is not +set, the tool uses stderr (which is probably not what you want). + +Similarly to afl-tmin, the library is not "proprietary" and can be used with +other fuzzers or testing tools without the need for any code tweaks. It does not +require AFL-instrumented binaries to work. + +To use the library, you *need* to make sure that your fuzzing target is compiled +with -fno-builtin and is linked dynamically. If you wish to automate the first +part without mucking with CFLAGS in Makefiles, you can set AFL_NO_BUILTIN=1 +when using afl-gcc. This setting specifically adds the following flags: + +``` + -fno-builtin-strcmp -fno-builtin-strncmp -fno-builtin-strcasecmp + -fno-builtin-strcasencmp -fno-builtin-memcmp -fno-builtin-strstr + -fno-builtin-strcasestr +``` + +The next step is simply loading this library via LD_PRELOAD. The optimal usage +pattern is to allow afl-fuzz to fuzz normally for a while and build up a corpus, +and then fire off the target binary, with libtokencap.so loaded, on every file +found by AFL in that earlier run. This demonstrates the basic principle: + +``` + export AFL_TOKEN_FILE=$PWD/temp_output.txt + + for i in /queue/id*; do + LD_PRELOAD=/path/to/libtokencap.so \ + /path/to/target/program [...params, including $i...] + done + + sort -u temp_output.txt >afl_dictionary.txt +``` + +If you don't get any results, the target library is probably not using strcmp() +and memcmp() to parse input; or you haven't compiled it with -fno-builtin; or +the whole thing isn't dynamically linked, and LD_PRELOAD is having no effect. + +Portability hints: There is probably no particularly portable and non-invasive +way to distinguish between read-only and read-write memory mappings. +The `__tokencap_load_mappings()` function is the only thing that would +need to be changed for other OSes. + +Current supported OSes are: Linux, Darwin, FreeBSD (thanks to @devnexen) + diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c new file mode 100644 index 00000000..3629e804 --- /dev/null +++ b/utils/libtokencap/libtokencap.so.c @@ -0,0 +1,794 @@ +/* + + american fuzzy lop++ - extract tokens passed to strcmp / memcmp + ------------------------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This Linux-only companion library allows you to instrument strcmp(), + memcmp(), and related functions to automatically extract tokens. + See README.tokencap.md for more info. + + */ + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "../config.h" + +#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__ && \ + !defined __OpenBSD__ && !defined __NetBSD__ && !defined __DragonFly__ && \ + !defined(__HAIKU__) && !defined(__sun) + #error "Sorry, this library is unsupported in this platform for now!" +#endif /* !__linux__ && !__APPLE__ && ! __FreeBSD__ && ! __OpenBSD__ && \ + !__NetBSD__*/ + +#if defined __APPLE__ + #include + #include +#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ + #include + #include + #if !defined __NetBSD__ + #include + #endif + #include +#elif defined __HAIKU__ + #include +#elif defined __sun + /* For map addresses the old struct is enough */ + #include + #include +#endif + +#include + +#ifdef RTLD_NEXT +/* The libc functions are a magnitude faster than our replacements. + Use them when RTLD_NEXT is available. */ +int (*__libc_strcmp)(const char *str1, const char *str2); +int (*__libc_strncmp)(const char *str1, const char *str2, size_t len); +int (*__libc_strcasecmp)(const char *str1, const char *str2); +int (*__libc_strncasecmp)(const char *str1, const char *str2, size_t len); +int (*__libc_memcmp)(const void *mem1, const void *mem2, size_t len); +int (*__libc_bcmp)(const void *mem1, const void *mem2, size_t len); +char *(*__libc_strstr)(const char *haystack, const char *needle); +char *(*__libc_strcasestr)(const char *haystack, const char *needle); +void *(*__libc_memmem)(const void *haystack, size_t haystack_len, + const void *needle, size_t needle_len); +#endif + +/* Mapping data and such */ + +#define MAX_MAPPINGS 1024 + +static struct mapping { void *st, *en; } __tokencap_ro[MAX_MAPPINGS]; + +static u32 __tokencap_ro_cnt; +static u8 __tokencap_ro_loaded; +static int __tokencap_out_file = -1; +static pid_t __tokencap_pid = -1; + +/* Identify read-only regions in memory. Only parameters that fall into these + ranges are worth dumping when passed to strcmp() and so on. Read-write + regions are far more likely to contain user input instead. */ + +static void __tokencap_load_mappings(void) { + +#if defined __linux__ + + u8 buf[MAX_LINE]; + FILE *f = fopen("/proc/self/maps", "r"); + + __tokencap_ro_loaded = 1; + + if (!f) return; + + while (fgets(buf, MAX_LINE, f)) { + + u8 rf, wf; + void *st, *en; + + if (sscanf(buf, "%p-%p %c%c", &st, &en, &rf, &wf) != 4) continue; + if (wf == 'w' || rf != 'r') continue; + + __tokencap_ro[__tokencap_ro_cnt].st = (void *)st; + __tokencap_ro[__tokencap_ro_cnt].en = (void *)en; + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + + fclose(f); + +#elif defined __APPLE__ + + struct vm_region_submap_info_64 region; + mach_msg_type_number_t cnt = VM_REGION_SUBMAP_INFO_COUNT_64; + vm_address_t base = 0; + vm_size_t size = 0; + natural_t depth = 0; + + __tokencap_ro_loaded = 1; + + while (1) { + + if (vm_region_recurse_64(mach_task_self(), &base, &size, &depth, + (vm_region_info_64_t)®ion, + &cnt) != KERN_SUCCESS) + break; + + if (region.is_submap) { + + depth++; + + } else { + + /* We only care of main map addresses and the read only kinds */ + if ((region.protection & VM_PROT_READ) && + !(region.protection & VM_PROT_WRITE)) { + + __tokencap_ro[__tokencap_ro_cnt].st = (void *)base; + __tokencap_ro[__tokencap_ro_cnt].en = (void *)(base + size); + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + + base += size; + size = 0; + + } + + } + +#elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ + + #if defined __FreeBSD__ + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; + #elif defined __OpenBSD__ + int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid}; + #elif defined __NetBSD__ + int mib[] = {CTL_VM, VM_PROC, VM_PROC_MAP, __tokencap_pid, + sizeof(struct kinfo_vmentry)}; + #endif + char * buf, *low, *high; + size_t miblen = sizeof(mib) / sizeof(mib[0]); + size_t len; + + if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1) return; + + #if defined __FreeBSD__ || defined __NetBSD__ + len = len * 4 / 3; + #elif defined __OpenBSD__ + len -= len % sizeof(struct kinfo_vmentry); + #endif + + buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) return; + + if (sysctl(mib, miblen, buf, &len, NULL, 0) == -1) { + + munmap(buf, len); + return; + + } + + low = buf; + high = low + len; + + __tokencap_ro_loaded = 1; + + while (low < high) { + + struct kinfo_vmentry *region = (struct kinfo_vmentry *)low; + + #if defined __FreeBSD__ || defined __NetBSD__ + + #if defined __FreeBSD__ + size_t size = region->kve_structsize; + + if (size == 0) break; + #elif defined __NetBSD__ + size_t size = sizeof(*region); + #endif + + /* We go through the whole mapping of the process and track read-only + * addresses */ + if ((region->kve_protection & KVME_PROT_READ) && + !(region->kve_protection & KVME_PROT_WRITE)) { + + #elif defined __OpenBSD__ + + size_t size = sizeof(*region); + + /* We go through the whole mapping of the process and track read-only + * addresses */ + if ((region->kve_protection & KVE_PROT_READ) && + !(region->kve_protection & KVE_PROT_WRITE)) { + + #endif + __tokencap_ro[__tokencap_ro_cnt].st = (void *)region->kve_start; + __tokencap_ro[__tokencap_ro_cnt].en = (void *)region->kve_end; + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + + low += size; + + } + + munmap(buf, len); +#elif defined __HAIKU__ + image_info ii; + int32_t group = 0; + + __tokencap_ro_loaded = 1; + + while (get_next_image_info(0, &group, &ii) == B_OK) { + + __tokencap_ro[__tokencap_ro_cnt].st = ii.text; + __tokencap_ro[__tokencap_ro_cnt].en = ((char *)ii.text) + ii.text_size; + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + +#elif defined __sun + prmap_t *c, *map; + char path[PATH_MAX]; + ssize_t r; + size_t hint; + int fd; + + snprintf(path, sizeof(path), "/proc/%ld/map", getpid()); + fd = open(path, O_RDONLY); + hint = (1 << 20); + map = malloc(hint); + + __tokencap_ro_loaded = 1; + + for (; (r = pread(fd, map, hint, 0)) == hint;) { + + hint <<= 1; + map = realloc(map, hint); + + } + + for (c = map; r > 0; c++, r -= sizeof(prmap_t)) { + + __tokencap_ro[__tokencap_ro_cnt].st = (void *)c->pr_vaddr; + __tokencap_ro[__tokencap_ro_cnt].en = (void *)(c->pr_vaddr + c->pr_size); + + if (++__tokencap_ro_cnt == MAX_MAPPINGS) break; + + } + + free(map); + close(fd); +#endif + +} + +/* Check an address against the list of read-only mappings. */ + +static u8 __tokencap_is_ro(const void *ptr) { + + u32 i; + + if (!__tokencap_ro_loaded) __tokencap_load_mappings(); + + for (i = 0; i < __tokencap_ro_cnt; i++) + if (ptr >= __tokencap_ro[i].st && ptr <= __tokencap_ro[i].en) return 1; + + return 0; + +} + +/* Dump an interesting token to output file, quoting and escaping it + properly. */ + +static void __tokencap_dump(const u8 *ptr, size_t len, u8 is_text) { + + u8 buf[MAX_AUTO_EXTRA * 4 + 1]; + u32 i; + u32 pos = 0; + + if (len < MIN_AUTO_EXTRA || len > MAX_AUTO_EXTRA || __tokencap_out_file == -1) + return; + + for (i = 0; i < len; i++) { + + if (is_text && !ptr[i]) break; + + switch (ptr[i]) { + + case 0 ... 31: + case 127 ... 255: + case '\"': + case '\\': + + sprintf(buf + pos, "\\x%02x", ptr[i]); + pos += 4; + break; + + default: + buf[pos++] = ptr[i]; + + } + + } + + buf[pos] = 0; + + int wrt_ok = (1 == write(__tokencap_out_file, "\"", 1)); + wrt_ok &= (pos == write(__tokencap_out_file, buf, pos)); + wrt_ok &= (2 == write(__tokencap_out_file, "\"\n", 2)); + +} + +/* Replacements for strcmp(), memcmp(), and so on. Note that these will be used + only if the target is compiled with -fno-builtins and linked dynamically. */ + +#undef strcmp + +int strcmp(const char *str1, const char *str2) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); + +#ifdef RTLD_NEXT + if (__libc_strcmp) return __libc_strcmp(str1, str2); +#endif + + while (1) { + + const unsigned char c1 = *str1, c2 = *str2; + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; + str2++; + + } + +} + +#undef strncmp + +int strncmp(const char *str1, const char *str2, size_t len) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); + +#ifdef RTLD_NEXT + if (__libc_strncmp) return __libc_strncmp(str1, str2, len); +#endif + + while (len--) { + + unsigned char c1 = *str1, c2 = *str2; + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; + str2++; + + } + + return 0; + +} + +#undef strcasecmp + +int strcasecmp(const char *str1, const char *str2) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, strlen(str1), 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, strlen(str2), 1); + +#ifdef RTLD_NEXT + if (__libc_strcasecmp) return __libc_strcasecmp(str1, str2); +#endif + + while (1) { + + const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2); + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; + str2++; + + } + +} + +#undef strncasecmp + +int strncasecmp(const char *str1, const char *str2, size_t len) { + + if (__tokencap_is_ro(str1)) __tokencap_dump(str1, len, 1); + if (__tokencap_is_ro(str2)) __tokencap_dump(str2, len, 1); + +#ifdef RTLD_NEXT + if (__libc_strncasecmp) return __libc_strncasecmp(str1, str2, len); +#endif + + while (len--) { + + const unsigned char c1 = tolower((int)*str1), c2 = tolower((int)*str2); + + if (c1 != c2) return (c1 > c2) ? 1 : -1; + if (!c1) return 0; + str1++; + str2++; + + } + + return 0; + +} + +#undef memcmp + +int memcmp(const void *mem1, const void *mem2, size_t len) { + + if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); + if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); + +#ifdef RTLD_NEXT + if (__libc_memcmp) return __libc_memcmp(mem1, mem2, len); +#endif + + const char *strmem1 = (const char *)mem1; + const char *strmem2 = (const char *)mem2; + + while (len--) { + + const unsigned char c1 = *strmem1, c2 = *strmem2; + if (c1 != c2) return (c1 > c2) ? 1 : -1; + strmem1++; + strmem2++; + + } + + return 0; + +} + +#undef bcmp + +int bcmp(const void *mem1, const void *mem2, size_t len) { + + if (__tokencap_is_ro(mem1)) __tokencap_dump(mem1, len, 0); + if (__tokencap_is_ro(mem2)) __tokencap_dump(mem2, len, 0); + +#ifdef RTLD_NEXT + if (__libc_bcmp) return __libc_bcmp(mem1, mem2, len); +#endif + + const char *strmem1 = (const char *)mem1; + const char *strmem2 = (const char *)mem2; + + while (len--) { + + int diff = *strmem1 ^ *strmem2; + if (diff != 0) return 1; + strmem1++; + strmem2++; + + } + + return 0; + +} + +#undef strstr + +char *strstr(const char *haystack, const char *needle) { + + if (__tokencap_is_ro(haystack)) + __tokencap_dump(haystack, strlen(haystack), 1); + + if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); + +#ifdef RTLD_NEXT + if (__libc_strstr) return __libc_strstr(haystack, needle); +#endif + + do { + + const char *n = needle; + const char *h = haystack; + + while (*n && *h && *n == *h) + n++, h++; + + if (!*n) return (char *)haystack; + + } while (*(haystack++)); + + return 0; + +} + +#undef strcasestr + +char *strcasestr(const char *haystack, const char *needle) { + + if (__tokencap_is_ro(haystack)) + __tokencap_dump(haystack, strlen(haystack), 1); + + if (__tokencap_is_ro(needle)) __tokencap_dump(needle, strlen(needle), 1); + +#ifdef RTLD_NEXT + if (__libc_strcasestr) return __libc_strcasestr(haystack, needle); +#endif + + do { + + const char *n = needle; + const char *h = haystack; + + while (*n && *h && tolower((int)*n) == tolower((int)*h)) + n++, h++; + + if (!*n) return (char *)haystack; + + } while (*(haystack++)); + + return 0; + +} + +#undef memmem + +void *memmem(const void *haystack, size_t haystack_len, const void *needle, + size_t needle_len) { + + if (__tokencap_is_ro(haystack)) __tokencap_dump(haystack, haystack_len, 1); + + if (__tokencap_is_ro(needle)) __tokencap_dump(needle, needle_len, 1); + +#ifdef RTLD_NEXT + if (__libc_memmem) + return __libc_memmem(haystack, haystack_len, needle, needle_len); +#endif + + const char *n = (const char *)needle; + const char *h = (const char *)haystack; + if (haystack_len < needle_len) return 0; + if (needle_len == 0) return (void *)haystack; + if (needle_len == 1) return memchr(haystack, *n, haystack_len); + + const char *end = h + (haystack_len - needle_len); + + do { + + if (*h == *n) { + + if (memcmp(h, n, needle_len) == 0) return (void *)h; + + } + + } while (h++ <= end); + + return 0; + +} + +/* Common libraries wrappers (from honggfuzz) */ + +/* + * Apache's httpd wrappers + */ +int ap_cstr_casecmp(const char *s1, const char *s2) { + + return strcasecmp(s1, s2); + +} + +int ap_cstr_casecmpn(const char *s1, const char *s2, size_t n) { + + return strncasecmp(s1, s2, n); + +} + +const char *ap_strcasestr(const char *s1, const char *s2) { + + return strcasestr(s1, s2); + +} + +int apr_cstr_casecmp(const char *s1, const char *s2) { + + return strcasecmp(s1, s2); + +} + +int apr_cstr_casecmpn(const char *s1, const char *s2, size_t n) { + + return strncasecmp(s1, s2, n); + +} + +/* + * *SSL wrappers + */ +int CRYPTO_memcmp(const void *m1, const void *m2, size_t len) { + + return memcmp(m1, m2, len); + +} + +int OPENSSL_memcmp(const void *m1, const void *m2, size_t len) { + + return memcmp(m1, m2, len); + +} + +int OPENSSL_strcasecmp(const char *s1, const char *s2) { + + return strcasecmp(s1, s2); + +} + +int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t len) { + + return strncasecmp(s1, s2, len); + +} + +int32_t memcmpct(const void *s1, const void *s2, size_t len) { + + return memcmp(s1, s2, len); + +} + +/* + * libXML wrappers + */ +int xmlStrncmp(const char *s1, const char *s2, int len) { + + if (len <= 0) { return 0; } + if (s1 == s2) { return 0; } + if (s1 == NULL) { return -1; } + if (s2 == NULL) { return 1; } + return strncmp(s1, s2, (size_t)len); + +} + +int xmlStrcmp(const char *s1, const char *s2) { + + if (s1 == s2) { return 0; } + if (s1 == NULL) { return -1; } + if (s2 == NULL) { return 1; } + return strcmp(s1, s2); + +} + +int xmlStrEqual(const char *s1, const char *s2) { + + if (s1 == s2) { return 1; } + if (s1 == NULL) { return 0; } + if (s2 == NULL) { return 0; } + if (strcmp(s1, s2) == 0) { return 1; } + return 0; + +} + +int xmlStrcasecmp(const char *s1, const char *s2) { + + if (s1 == s2) { return 0; } + if (s1 == NULL) { return -1; } + if (s2 == NULL) { return 1; } + return strcasecmp(s1, s2); + +} + +int xmlStrncasecmp(const char *s1, const char *s2, int len) { + + if (len <= 0) { return 0; } + if (s1 == s2) { return 0; } + if (s1 == NULL) { return -1; } + if (s2 == NULL) { return 1; } + return strncasecmp(s1, s2, (size_t)len); + +} + +const char *xmlStrstr(const char *haystack, const char *needle) { + + if (haystack == NULL) { return NULL; } + if (needle == NULL) { return NULL; } + return strstr(haystack, needle); + +} + +const char *xmlStrcasestr(const char *haystack, const char *needle) { + + if (haystack == NULL) { return NULL; } + if (needle == NULL) { return NULL; } + return strcasestr(haystack, needle); + +} + +/* + * Samba wrappers + */ +int memcmp_const_time(const void *s1, const void *s2, size_t n) { + + return memcmp(s1, s2, n); + +} + +bool strcsequal(const void *s1, const void *s2) { + + if (s1 == s2) { return true; } + if (!s1 || !s2) { return false; } + return (strcmp(s1, s2) == 0); + +} + +/* bcmp/memcmp BSD flavors, similar to CRYPTO_memcmp */ + +int timingsafe_bcmp(const void *mem1, const void *mem2, size_t len) { + + return bcmp(mem1, mem2, len); + +} + +int timingsafe_memcmp(const void *mem1, const void *mem2, size_t len) { + + return memcmp(mem1, mem2, len); + +} + +/* Init code to open the output file (or default to stderr). */ + +__attribute__((constructor)) void __tokencap_init(void) { + + u8 *fn = getenv("AFL_TOKEN_FILE"); + if (fn) __tokencap_out_file = open(fn, O_RDWR | O_CREAT | O_APPEND, 0655); + if (__tokencap_out_file == -1) __tokencap_out_file = STDERR_FILENO; + __tokencap_pid = getpid(); + +#ifdef RTLD_NEXT + __libc_strcmp = dlsym(RTLD_NEXT, "strcmp"); + __libc_strncmp = dlsym(RTLD_NEXT, "strncmp"); + __libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp"); + __libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp"); + __libc_memcmp = dlsym(RTLD_NEXT, "memcmp"); + __libc_bcmp = dlsym(RTLD_NEXT, "bcmp"); + __libc_strstr = dlsym(RTLD_NEXT, "strstr"); + __libc_strcasestr = dlsym(RTLD_NEXT, "strcasestr"); + __libc_memmem = dlsym(RTLD_NEXT, "memmem"); +#endif + +} + +/* closing as best as we can the tokens file */ +__attribute__((destructor)) void __tokencap_shutdown(void) { + + if (__tokencap_out_file != STDERR_FILENO) close(__tokencap_out_file); + +} + diff --git a/utils/qbdi_mode/README.md b/utils/qbdi_mode/README.md new file mode 100755 index 00000000..641a6e85 --- /dev/null +++ b/utils/qbdi_mode/README.md @@ -0,0 +1,199 @@ +# qbdi-based binary-only instrumentation for afl-fuzz + +## 1) Introduction + +The code in ./qbdi_mode allows you to build a standalone feature that +using the QBDI framework to fuzz android native library. + + +## 2) Build + +First download the Android NDK + +``` +https://developer.android.com/ndk/downloads +https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip +``` + +Then unzip it and build the standalone-toolchain +For x86_64 standalone-toolchain + +``` +unzip android-ndk-r20-linux-x86_64.zip +cd android-ndk-r20/ +./build/tools/make_standalone_toolchain.py --arch x86_64 --api 21 --install-dir ../android-standalone-toolchain-x86_64 +``` + +For x86 standalone-toolchain + +``` +./build/tools/make_standalone_toolchain.py --arch x86 --api 21 --install-dir ../android-standalone-toolchain-x86 +``` + +In alternative you can also use the prebuilt toolchain, in that case make sure to set the proper CC and CXX env variables because there are many different compilers for each API version in the prebuilt toolchain. + +For example: + +``` +export STANDALONE_TOOLCHAIN_PATH=~/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/ +export CC=x86_64-linux-android21-clang +export CXX=x86_64-linux-android21-clang++ +``` + +Then download the QBDI SDK from website + +``` +https://qbdi.quarkslab.com/ +``` + +For Android x86_64 +``` +https://github.com/QBDI/QBDI/releases/download/v0.7.0/QBDI-0.7.0-android-X86_64.tar.gz +``` + +Then decompress the sdk + +``` +mkdir android-qbdi-sdk-x86_64 +cp QBDI-0.7.0-android-X86_64.tar.gz android-qbdi-sdk-x86_64/ +cd android-qbdi-sdk-x86_64/ +tar xvf QBDI-0.7.0-android-X86_64.tar.gz +``` + +Now set the `STANDALONE_TOOLCHAIN_PATH` to the path of standalone-toolchain + +``` +export STANDALONE_TOOLCHAIN_PATH=/home/hac425/workspace/android-standalone-toolchain-x86_64 +``` + +set the `QBDI_SDK_PATH` to the path of QBDI SDK + +``` +export QBDI_SDK_PATH=/home/hac425/workspace/AFLplusplus/qbdi_mode/android-qbdi-sdk-x86_64/ +``` + +Then run the build.sh + +``` +./build.sh x86_64 +``` + +this could build the afl-fuzz and also the qbdi template for android x86_64 + + +### Example + +The demo-so.c is an vulnerable library, it has a function for test + +```c +int target_func(char *buf, int size) { + + printf("buffer:%p, size:%p\n", buf, size); + switch (buf[0]) { + + case 1: + puts("222"); + if (buf[1] == '\x44') { + + puts("null ptr deference"); + *(char *)(0) = 1; + + } + + break; + case 0xff: + if (buf[2] == '\xff') { + + if (buf[1] == '\x44') { + + puts("crash...."); + *(char *)(0xdeadbeef) = 1; + + } + + } + + break; + default: puts("default action"); break; + + } + + return 1; + +} +``` + +This could be build to `libdemo.so`. + +Then we should load the library in template.cpp and find the `target` function address. +```c + void *handle = dlopen(lib_path, RTLD_LAZY); + .......................................... + .......................................... + .......................................... + p_target_func = (target_func)dlsym(handle, "target_func"); +``` + +then we read the data from file and call the function in `fuzz_func` + +```c +QBDI_NOINLINE int fuzz_func() { + + if (afl_setup()) { afl_forkserver(); } + + /* Read the input from file */ + unsigned long len = 0; + char * data = read_file(input_pathname, &len); + + /* Call the target function with the input data */ + p_target_func(data, len); + return 1; + +} +``` + +Just compile it +``` +./build.sh x86_64 +``` + +Then push the `afl-fuzz`, `loader`, `libdemo.so`, the `libQBDI.so` from the QBDI SDK and the `libc++_shared.so` from android-standalone-toolchain to android device + +``` +adb push afl-fuzz /data/local/tmp +adb push libdemo.so /data/local/tmp +adb push loader /data/local/tmp +adb push android-qbdi-sdk-x86_64/usr/local/lib/libQBDI.so /data/local/tmp +adb push ../../android-standalone-toolchain-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so +/data/local/tmp +``` + +In android adb shell, run the loader to test if it runs +``` +cd /data/local/tmp +export LD_LIBRARY_PATH=/data/local/tmp +mkdir in +echo 0000 > in/1 +./loader libdemo.so in/1 +p_target_func:0x716d96a98600 + offset:0x600 + offset:0x580 +buffer:0x716d96609050, size:0x5 + offset:0x628 + offset:0x646 + offset:0x64b + offset:0x65c + offset:0x6df + offset:0x590 +default action + offset:0x6eb +``` + +Now run `afl-fuzz` to fuzz the demo library + +``` +./afl-fuzz -i in -o out -- ./loader /data/local/tmp/libdemo.so @@ +``` + +![screen1](assets/screen1.png) + diff --git a/utils/qbdi_mode/assets/screen1.png b/utils/qbdi_mode/assets/screen1.png new file mode 100644 index 00000000..3cf1cb76 Binary files /dev/null and b/utils/qbdi_mode/assets/screen1.png differ diff --git a/utils/qbdi_mode/build.sh b/utils/qbdi_mode/build.sh new file mode 100755 index 00000000..2527bd26 --- /dev/null +++ b/utils/qbdi_mode/build.sh @@ -0,0 +1,57 @@ + +if [ -z ${STANDALONE_TOOLCHAIN_PATH} ]; then + echo "please set the android-standalone-toolchain path in STANDALONE_TOOLCHAIN_PATH environmental variable" + echo "for example: " + echo " export STANDALONE_TOOLCHAIN_PATH=/home/android-standalone-toolchain-21/" + exit +fi + +if [ -z ${QBDI_SDK_PATH} ]; then + echo "please set the qbdi sdk path in QBDI_SDK_PATH environmental variable" + echo "for example: " + echo " export QBDI_SDK_PATH=/home/QBDI-Android/" + exit +fi + + + +if [ "$1" = "x86" ]; then + echo "build x86 qbdi" + compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/" + if [ -z ${CC} ]; then + export CC=i686-linux-android-gcc + fi + if [ -z ${CXX} ]; then + export CXX=i686-linux-android-g++ + fi +elif [ "$1" = "x86_64" ]; then + echo "build x86_64 qbdi" + compiler_prefix="${STANDALONE_TOOLCHAIN_PATH}/bin/" + if [ -z ${CC} ]; then + export CC=x86_64-linux-android-gcc + fi + if [ -z ${CXX} ]; then + export CXX=x86_64-linux-android-g++ + fi +else + echo "usage: ./build.sh arch[x86, x86_64]" + exit +fi + + +CFLAGS="-I${QBDI_SDK_PATH}/usr/local/include/ -L${QBDI_SDK_PATH}/usr/local/lib/" + +echo "[+] Building the QBDI template" +# build the qbdi template +${compiler_prefix}${CXX} -o loader template.cpp -lQBDI -ldl -w -g ${CFLAGS} + +echo "[+] Building the demo library" +# build the demo share library +${compiler_prefix}${CC} -shared -o libdemo.so demo-so.c -w -g + +echo "[+] Building afl-fuzz for Android" +# build afl-fuzz +cd ../.. +${compiler_prefix}${CC} -DANDROID_DISABLE_FANCY=1 -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -I include/ -DAFL_PATH=\"/usr/local/lib/afl\" -DBIN_PATH=\"/usr/local/bin\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -Wno-unused-function src/afl-fuzz-*.c src/afl-fuzz.c src/afl-common.c src/afl-sharedmem.c src/afl-forkserver.c -o utils/qbdi_mode/afl-fuzz -ldl -w + +echo "[+] All done. Enjoy!" diff --git a/utils/qbdi_mode/demo-so.c b/utils/qbdi_mode/demo-so.c new file mode 100755 index 00000000..dd367036 --- /dev/null +++ b/utils/qbdi_mode/demo-so.c @@ -0,0 +1,41 @@ +#include + +// gcc -shared -o libdemo.so demo-so.c -w +int target_func(char *buf, int size) { + + printf("buffer:%p, size:%p\n", buf, size); + switch (buf[0]) { + + case 1: + puts("222"); + if (buf[1] == '\x44') { + + puts("null ptr deference"); + *(char *)(0) = 1; + + } + + break; + case 0xff: + if (buf[2] == '\xff') { + + if (buf[1] == '\x44') { + + puts("crash...."); + *(char *)(0xdeadbeef) = 1; + + } + + } + + break; + default: + puts("default action"); + break; + + } + + return 1; + +} + diff --git a/utils/qbdi_mode/template.cpp b/utils/qbdi_mode/template.cpp new file mode 100755 index 00000000..b2066cc8 --- /dev/null +++ b/utils/qbdi_mode/template.cpp @@ -0,0 +1,251 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __ANDROID__ + #include "../include/android-ashmem.h" +#endif + +#include +#include +#include "../config.h" + +#include + +/* NeverZero */ + +#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO) + #define INC_AFL_AREA(loc) \ + asm volatile( \ + "incb (%0, %1, 1)\n" \ + "adcb $0, (%0, %1, 1)\n" \ + : /* no out */ \ + : "r"(afl_area_ptr), "r"(loc) \ + : "memory", "eax") +#else + #define INC_AFL_AREA(loc) afl_area_ptr[loc]++ +#endif + +using namespace QBDI; + +typedef int (*target_func)(char *buf, int size); + +static const size_t STACK_SIZE = 0x100000; // 1MB +static const QBDI::rword FAKE_RET_ADDR = 0x40000; +target_func p_target_func = NULL; +rword module_base = 0; +rword module_end = 0; +static unsigned char + dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */ +unsigned char *afl_area_ptr = NULL; /* Exported for afl_gen_trace */ + +unsigned long afl_prev_loc = 0; + +char input_pathname[PATH_MAX]; + +/* Set up SHM region and initialize other stuff. */ + +int afl_setup(void) { + + char *id_str = getenv(SHM_ENV_VAR); + int shm_id; + if (id_str) { + + shm_id = atoi(id_str); + afl_area_ptr = (unsigned char *)shmat(shm_id, NULL, 0); + if (afl_area_ptr == (void *)-1) return 0; + memset(afl_area_ptr, 0, MAP_SIZE); + + } + + return 1; + +} + +/* Fork server logic, invoked once we hit _start. */ +static void afl_forkserver() { + + static unsigned char tmp[4]; + pid_t child_pid; + + if (write(FORKSRV_FD + 1, tmp, 4) != 4) return; + + while (1) { + + int status; + u32 was_killed; + // wait for afl-fuzz + if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2); + + child_pid = fork(); + if (child_pid < 0) exit(4); + + if (!child_pid) { + + // child return to execute code + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + return; + + } + + // write child pid to afl-fuzz + if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5); + + // wait child stop + if (waitpid(child_pid, &status, 0) < 0) exit(6); + + // send child stop status to afl-fuzz + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7); + + } + +} + +void afl_maybe_log(unsigned long cur_loc) { + + if (afl_area_ptr == NULL) { return; } + unsigned long afl_idx = cur_loc ^ afl_prev_loc; + afl_idx &= MAP_SIZE - 1; + INC_AFL_AREA(afl_idx); + afl_prev_loc = cur_loc >> 1; + +} + +char *read_file(char *path, unsigned long *length) { + + unsigned long len; + char * buf; + + FILE *fp = fopen(path, "rb"); + fseek(fp, 0, SEEK_END); + len = ftell(fp); + buf = (char *)malloc(len); + rewind(fp); + fread(buf, 1, len, fp); + fclose(fp); + *length = len; + return buf; + +} + +QBDI_NOINLINE int fuzz_func() { + + if (afl_setup()) { afl_forkserver(); } + + unsigned long len = 0; + char * data = read_file(input_pathname, &len); + + // printf("In fuzz_func\n"); + p_target_func(data, len); + return 1; + +} + +static QBDI::VMAction bbcallback(QBDI::VMInstanceRef vm, + const QBDI::VMState *state, + QBDI::GPRState * gprState, + QBDI::FPRState *fprState, void *data) { + + // errno = SAVED_ERRNO; + +#ifdef __x86_64__ + unsigned long pc = gprState->rip; +#elif defined(i386) + unsigned long pc = gprState->eip; +#elif defined(__arm__) + unsigned long pc = gprState->pc; +#endif + + // just log the module path + if (pc >= module_base && pc <= module_end) { + + unsigned long offset = pc - module_base; + printf("\toffset:%p\n", offset); + afl_maybe_log(offset); + + } + + return QBDI::VMAction::CONTINUE; + +} + +int main(int argc, char **argv) { + + if (argc < 3) { + + puts("usage: ./loader library_path input_file_path"); + exit(0); + + } + + const char *lib_path; + lib_path = argv[1]; + strcpy(input_pathname, argv[2]); + void *handle = dlopen(lib_path, RTLD_LAZY); + + if (handle == nullptr) { + + perror("Cannot load library"); + exit(EXIT_FAILURE); + + } + + const char *lib_name = lib_path; + if (strrchr(lib_name, '/') != nullptr) lib_name = strrchr(lib_name, '/') + 1; + + // printf("library name:%s\n", lib_name); + // load library module address for log path + for (MemoryMap &map : getCurrentProcessMaps()) { + + // printf("module:%s\n", map.name.c_str()); + if ((map.permission & PF_EXEC) && + strstr(map.name.c_str(), lib_name) != NULL) { + + module_base = map.range.start; + module_end = map.range.end; + + } + + } + + if (module_base == 0) { + + std::cerr << "Fail to find base address" << std::endl; + return -1; + + } + + // printf("module base:%p, module end:%p\n", module_base, module_end); + p_target_func = (target_func)dlsym(handle, "target_func"); + // p_target_func = (target_func)(module_base + 0x61a); + printf("p_target_func:%p\n", p_target_func); + + VM vm; + uint8_t *fakestack = nullptr; + + GPRState *state = vm.getGPRState(); + allocateVirtualStack(state, STACK_SIZE, &fakestack); + vm.addInstrumentedModuleFromAddr(module_base); + vm.addInstrumentedModuleFromAddr((rword)&main); + + vm.addVMEventCB(BASIC_BLOCK_ENTRY, bbcallback, nullptr); + + // QBDI::simulateCall(state, FAKE_RET_ADDR); + // vm.run((rword)&fuzz_func, (rword)FAKE_RET_ADDR); + + rword ret; + vm.call(&ret, (rword)&fuzz_func, {}); + + return 0; + +} + -- cgit 1.4.1 From 161c80014ec11e8ae4338da34428c20dcbe03962 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 10 Dec 2020 17:25:22 +0100 Subject: change search order for afl-showmin in afl-cmin* --- README.md | 2 ++ afl-cmin | 16 ++++++++++------ afl-cmin.bash | 19 +++++++++++++++---- docs/Changelog.md | 1 + 4 files changed, 28 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 8e7f99dc..dc009def 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ behaviours and defaults: `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). * examples/ got renamed to utils/ * libtokencap/ libdislocator/ and qdbi_mode/ were moved to utils/ + * afl-cmin/afl-cmin.bash now search first in PATH and last in AFL_PATH + ## Contents diff --git a/afl-cmin b/afl-cmin index 93174b8b..292d9d9d 100755 --- a/afl-cmin +++ b/afl-cmin @@ -120,7 +120,7 @@ function usage() { "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ -"AFL_PATH: path for the afl-showmap binary\n" \ +"AFL_PATH: path for the afl-showmap binary if not found anywhere else\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" exit 1 } @@ -313,14 +313,18 @@ BEGIN { close( stdin_file ) } - if (!ENVIRON["AFL_PATH"]) { - if (0 == system("test -f afl-cmin")) { + # First we look in PATH + if (0 == system("command -v afl-showmap >/dev/null 2>&1")) { + "command -v afl-showmap 2>/dev/null" | getline showmap + } else { + # then we look in the current directory + if (0 == system("test -x ./afl-showmap")) { showmap = "./afl-showmap" } else { - "command -v afl-showmap 2>/dev/null" | getline showmap + if (ENVIRON["AFL_PATH"]) { + showmap = ENVIRON["AFL_PATH"] "/afl-showmap" + } } - } else { - showmap = ENVIRON["AFL_PATH"] "/afl-showmap" } if (!showmap || 0 != system("test -x "showmap )) { diff --git a/afl-cmin.bash b/afl-cmin.bash index 637949bc..fb50f1fc 100755 --- a/afl-cmin.bash +++ b/afl-cmin.bash @@ -132,7 +132,7 @@ For additional tips, please consult README.md. Environment variables used: AFL_KEEP_TRACES: leave the temporary \.traces directory -AFL_PATH: path for the afl-showmap binary +AFL_PATH: last resort location to find the afl-showmap binary AFL_SKIP_BIN_CHECK: skip check for target binary _EOF_ exit 1 @@ -244,10 +244,21 @@ if [ ! "$STDIN_FILE" = "" ]; then touch "$STDIN_FILE" || exit 1 fi -if [ "$AFL_PATH" = "" ]; then - SHOWMAP="${0%/afl-cmin.bash}/afl-showmap" +SHOWMAP=`command -v afl-showmap 2>/dev/null` + +if [ -z "$SHOWMAP" ]; then + TMP="${0%/afl-cmin.bash}/afl-showmap" + if [ -x "$TMP" ]; then + SHOWMAP=$TMP + fi +fi + +if [ -z "$SHOWMAP" -a -x "./afl-showmap" ]; then + SHOWMAP="./afl-showmap" else - SHOWMAP="$AFL_PATH/afl-showmap" + if [ -n "$AFL_PATH" ]; then + SHOWMAP="$AFL_PATH/afl-showmap" + fi fi if [ ! -x "$SHOWMAP" ]; then diff --git a/docs/Changelog.md b/docs/Changelog.md index 1a47d18f..5094769d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -64,6 +64,7 @@ sending a mail to . - added INTROSPECTION support for custom modules - python fuzz function was not optional, fixed - some python mutator speed improvements + - afl-cmin/afl-cmin.bash now search first in PATH and last in AFL_PATH - unicornafl synced with upstream version 1.02 (fixes, better rust bindings) - renamed AFL_DEBUG_CHILD_OUTPUT to AFL_DEBUG_CHILD - added AFL_CRASH_EXITCODE env variable to treat a child exitcode as crash -- cgit 1.4.1 From 8a1acac559edb66e8e246e73508cec541a9fc530 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 11 Dec 2020 10:28:39 +0100 Subject: schedule improvements, new default is FAST --- docs/Changelog.md | 7 ++++--- src/afl-fuzz-queue.c | 2 +- src/afl-fuzz-state.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 5094769d..4470388e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,16 +16,17 @@ sending a mail to . - all compilers combined to afl-cc which emulates the previous ones - afl-llvm/gcc-rt.o merged into afl-compiler-rt.o - afl-fuzz - - memory limits are now disabled by default, set them with -m if required + - not specifying -M or -S will now auto-set "-S default" - deterministic fuzzing is now disabled by default and can be enabled with -D. It is still enabled by default for -M. - a new seed selection was implemented that uses weighted randoms based on a schedule performance score, which is much better that the previous walk the whole queue approach. Select the old mode with -Z (auto enabled with -M) - - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - Marcel Boehme submitted a patch that improves all AFFast schedules :) - - not specifying -M or -S will now auto-set "-S default" + - the default schedule is now FAST + - memory limits are now disabled by default, set them with -m if required + - rpc.statsd support, for stats and charts, by Edznux, thanks a lot! - reading testcases from -i now descends into subdirectories - allow the -x command line option up to 4 times - loaded extras now have a duplication protection diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 84092ff8..d74c07a1 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -47,7 +47,7 @@ double compute_weight(afl_state_t *afl, struct queue_entry *q, u32 hits; - if (likely(afl->schedule >= FAST && afl->schedule < RARE)) { + if (likely(afl->schedule >= FAST && afl->schedule <= RARE)) { hits = afl->n_fuzz[q->n_fuzz_entry]; if (hits == 0) { hits = 1; } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 73b94466..36da2730 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -87,7 +87,7 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) { afl->w_end = 0.3; afl->g_max = 5000; afl->period_pilot_tmp = 5000.0; - afl->schedule = EXPLORE; /* Power schedule (default: EXPLORE) */ + afl->schedule = FAST ; /* Power schedule (default: FAST) */ afl->havoc_max_mult = HAVOC_MAX_MULT; afl->clear_screen = 1; /* Window resized? */ -- cgit 1.4.1 From 54f59c7403502aa3be73bf860b08c38ae5a64bfa Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Sat, 12 Dec 2020 11:21:13 +0100 Subject: add an extra test for afl-gcc and check for -B support --- GNUmakefile | 18 ++++++++++++++---- docs/INSTALL.md | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index 3585543a..4d71e9fb 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -510,15 +510,25 @@ code-format: .PHONY: test_build ifndef AFL_NO_X86 -test_build: afl-cc afl-as afl-showmap - @echo "[*] Testing the CC wrapper and instrumentation output..." +test_build: afl-cc afl-gcc afl-as afl-showmap + @echo "[*] Testing the CC wrapper afl-cc and its instrumentation output..." @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_INST_RATIO=100 AFL_PATH=. ./afl-cc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-cc failed"; exit 1 ) ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr @rm -f test-instr - @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi + @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-cc does not seem to be behaving correctly!"; echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue."; echo; exit 1; fi + @echo + @echo "[+] All right, the instrumentation of afl-cc seems to be working!" + @echo "[*] Testing the CC wrapper afl-gcc and its instrumentation output..." + @unset AFL_MAP_SIZE AFL_USE_UBSAN AFL_USE_CFISAN AFL_USE_ASAN AFL_USE_MSAN AFL_CC; AFL_INST_RATIO=100 AFL_PATH=. ./afl-gcc test-instr.c -o test-instr 2>&1 || (echo "Oops, afl-gcc failed"; exit 1 ) + ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr0 ./test-instr < /dev/null + echo 1 | ASAN_OPTIONS=detect_leaks=0 ./afl-showmap -m none -q -o .test-instr1 ./test-instr + @rm -f test-instr + @cmp -s .test-instr0 .test-instr1; DR="$$?"; rm -f .test-instr0 .test-instr1; if [ "$$DR" = "0" ]; then echo; echo "Oops, the instrumentation of afl-gcc does not seem to be behaving correctly!"; \ + gcc -v 2>&1 | grep -q -- --with-as= && ( echo; echo "Gcc is configured not to use an external assembler with the -B option."; echo "See docs/INSTALL.md section 5 how to build a -B enabled gcc." ) || \ + ( echo; echo "Please post to https://github.com/AFLplusplus/AFLplusplus/issues to troubleshoot the issue." ); echo; exit 1; fi @echo - @echo "[+] All right, the instrumentation seems to be working!" + @echo "[+] All right, the instrumentation of afl-gcc seems to be working!" else test_build: afl-cc afl-as afl-showmap @echo "[!] Note: skipping build tests (you may need to use LLVM or QEMU mode)." diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 8e1e266f..e3c06c9d 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -4,7 +4,7 @@ issues for a variety of platforms. See README.md for the general instruction manual. -## 1) Linux on x86 +## 1. Linux on x86 --------------- This platform is expected to work well. Compile the program with: @@ -34,7 +34,7 @@ You may have to change several settings to get optimal results (most notably, disable crash reporting utilities and switch to a different CPU governor), but afl-fuzz will guide you through that if necessary. -## OpenBSD, FreeBSD, NetBSD on x86 +## 2. OpenBSD, FreeBSD, NetBSD on x86 Similarly to Linux, these platforms are expected to work well and are regularly tested. Compile everything with GNU make: -- cgit 1.4.1 From 149ec41e9039d79420088c6de7bfc7feba5fe937 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 09:38:26 +0100 Subject: v3.00c --- docs/Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 4470388e..d77c276b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.00a (develop) +### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ - examples/ renamed to utils/ - moved libdislocator, libtokencap and qdbi_mode to utils/ -- cgit 1.4.1 From 1fc0918ac0e79588f9c4aff0a274da0897fe1b88 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 09:55:33 +0100 Subject: v3.01a init --- README.md | 2 +- docs/Changelog.md | 4 ++++ include/config.h | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 68b64ce6..a0e7a7e4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.00a + Github Version: 3.01a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index d77c276b..a45eaad7 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,10 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++3.01a (release) + - ... + + ### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ - examples/ renamed to utils/ diff --git a/include/config.h b/include/config.h index 93249ed9..1eb6bc5e 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ /* Version string: */ // c = release, d = volatile github dev, e = experimental branch -#define VERSION "++3.00c" +#define VERSION "++3.01a" /****************************************************** * * -- cgit 1.4.1 From db76b06e0119b4fb780431a86fe7e197e8d20c0c Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 18:07:01 +0100 Subject: add dummy Makefile to instrumentation/ --- docs/Changelog.md | 2 +- instrumentation/Makefile | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 instrumentation/Makefile (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index a45eaad7..e25ae029 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,7 +10,7 @@ sending a mail to . ### Version ++3.01a (release) - - ... + - added dummy Makefile to instrumentation/ ### Version ++3.00c (release) diff --git a/instrumentation/Makefile b/instrumentation/Makefile new file mode 100644 index 00000000..6cdd1a07 --- /dev/null +++ b/instrumentation/Makefile @@ -0,0 +1,2 @@ +all: + @echo "no need to do make in the instrumentation/ directory :) - it is all done in the main one" -- cgit 1.4.1 From 6e0aeb983356b805d769903fc02945014e28c8c8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 15 Dec 2020 18:17:55 +0100 Subject: allow instrumenting LLVMFuzzerTestOneInput --- docs/Changelog.md | 1 + instrumentation/afl-gcc-pass.so.cc | 4 +++- instrumentation/afl-llvm-common.cc | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e25ae029..c2ed0a12 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,7 @@ sending a mail to . ### Version ++3.01a (release) - added dummy Makefile to instrumentation/ + - allow instrumenting LLVMFuzzerTestOneInput ### Version ++3.00c (release) diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index e116e7d1..c95ead8f 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -516,7 +516,9 @@ struct afl_pass : gimple_opt_pass { "__cmplog", "__sancov", "msan.", - "LLVMFuzzer", + "LLVMFuzzerM", + "LLVMFuzzerC", + "LLVMFuzzerI", "__decide_deferred", "maybe_duplicate_stderr", "discard_output", diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 21c4d204..557939fd 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -70,7 +70,9 @@ bool isIgnoreFunction(const llvm::Function *F) { "__cmplog", "__sancov", "msan.", - "LLVMFuzzer", + "LLVMFuzzerM", + "LLVMFuzzerC", + "LLVMFuzzerI", "__decide_deferred", "maybe_duplicate_stderr", "discard_output", -- cgit 1.4.1 From fd6bff727a860220bdd035952a7666c60f444b3f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 17 Dec 2020 22:57:28 +0100 Subject: fix crash for very fast targets --- docs/Changelog.md | 1 + src/afl-common.c | 5 +++++ src/afl-fuzz-stats.c | 2 ++ 3 files changed, 8 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c2ed0a12..ac75c68d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.01a (release) + - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani - added dummy Makefile to instrumentation/ - allow instrumenting LLVMFuzzerTestOneInput diff --git a/src/afl-common.c b/src/afl-common.c index 4df22394..6dc8abe0 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "debug.h" #include "alloc-inl.h" @@ -786,6 +787,10 @@ u8 *u_stringify_float(u8 *buf, double val) { sprintf(buf, "%0.01f", val); + } else if (unlikely(isnan(val) || isfinite(val))) { + + strcpy(buf, "999.9"); + } else { return u_stringify_int(buf, (u64)val); diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 321bbb35..50e2ef15 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -371,6 +371,8 @@ void show_stats(afl_state_t *afl) { if (!afl->stats_last_execs) { + if (unlikely(cur_ms == afl->start_time)) --afl->start_time; + afl->stats_avg_exec = ((double)afl->fsrv.total_execs) * 1000 / (cur_ms - afl->start_time); -- cgit 1.4.1 From 12ebb351dc2b5655b4174539e2b9ee59e4acf893 Mon Sep 17 00:00:00 2001 From: hexcoder- Date: Fri, 18 Dec 2020 21:10:39 +0100 Subject: apply nocolor changes --- docs/Changelog.md | 2 ++ docs/env_variables.md | 3 +++ include/config.h | 16 ++++++++++++ include/debug.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- include/envs.h | 4 +++ src/afl-fuzz-state.c | 16 ++++++++++++ src/afl-fuzz.c | 15 +++++++++++ 7 files changed, 122 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ac75c68d..ebc514f3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -52,6 +52,8 @@ sending a mail to . - somewhere we broke -n dumb fuzzing, fixed - added afl_custom_describe to the custom mutator API to allow for easy mutation reproduction on crashing inputs + - new env. var. AFL_NO_COLOR (or AFL_NO_COLOUR) to suppress colored + console output (when configured with USE_COLOR and not ALWAYS_COLORED) - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! diff --git a/docs/env_variables.md b/docs/env_variables.md index e203055f..74863d8d 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -381,6 +381,9 @@ checks or alter some of the more exotic semantics of the tool: some basic stats. This behavior is also automatically triggered when the output from afl-fuzz is redirected to a file or to a pipe. + - Setting `AFL_NO_COLOR` or `AFL_NO_COLOUR` will omit control sequences for + coloring console output when configured with USE_COLOR and not ALWAYS_COLORED. + - Setting `AFL_FORCE_UI` will force painting the UI on the screen even if no valid terminal was detected (for virtual consoles) diff --git a/include/config.h b/include/config.h index 1eb6bc5e..d57ef223 100644 --- a/include/config.h +++ b/include/config.h @@ -36,11 +36,27 @@ * * ******************************************************/ +/* console output colors: There are three ways to configure its behavior + * 1. default: colored outputs fixed on: defined USE_COLOR && defined ALWAYS_COLORED + * The env var. AFL_NO_COLOR will have no effect + * 2. defined USE_COLOR && !defined ALWAYS_COLORED + * -> depending on env var AFL_NO_COLOR=1 colors can be switched off + * at run-time. Default is to use colors. + * 3. colored outputs fixed off: !defined USE_COLOR + * The env var. AFL_NO_COLOR will have no effect +*/ + /* Comment out to disable terminal colors (note that this makes afl-analyze a lot less nice): */ #define USE_COLOR +#ifdef USE_COLOR +/* Comment in to always enable terminal colors */ +/* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR */ +#define ALWAYS_COLORED 1 +#endif + /* StatsD config Config can be adjusted via AFL_STATSD_HOST and AFL_STATSD_PORT environment variable. diff --git a/include/debug.h b/include/debug.h index 5512023c..7a1725b5 100644 --- a/include/debug.h +++ b/include/debug.h @@ -168,12 +168,72 @@ * Debug & error macros * ************************/ -/* Just print stuff to the appropriate stream. */ +#if defined USE_COLOR && !defined ALWAYS_COLORED +#include +#pragma GCC diagnostic ignored "-Wformat-security" +static inline const char * colorfilter(const char * x) { + static int once = 1; + static int disabled = 0; + + if (once) { + /* when there is no tty -> we always want filtering + * when AFL_NO_UI is set filtering depends on AFL_NO_COLOR + * otherwise we want always colors + */ + disabled = isatty(2) && (!getenv("AFL_NO_UI") || (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); + once = 0; + } + if (likely(disabled)) return x; + + static char monochromestring[4096]; + char *d = monochromestring; + int in_seq = 0; + + while(*x) { + if (in_seq && *x == 'm') { + in_seq = 0; + } else { + if (!in_seq && *x == '\x1b') { in_seq = 1; } + if (!in_seq) { + *d++ = *x; + } + } + ++x; + } + + *d = '\0'; + return monochromestring; +} +#else +#define colorfilter(x) x /* no filtering necessary */ +#endif +/* macro magic to transform the first parameter to SAYF + * through colorfilter which strips coloring */ +#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,\ +_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,\ +_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,\ +_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,\ +NAME,...) NAME + +#define SAYF(...) GET_MACRO(__VA_ARGS__, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ +SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) + +#define SAYF_1(x) MY_SAYF(colorfilter(x)) +#define SAYF_N(x,...) MY_SAYF(colorfilter(x), __VA_ARGS__) + +/* Just print stuff to the appropriate stream. */ #ifdef MESSAGES_TO_STDOUT - #define SAYF(x...) printf(x) + #define MY_SAYF(x...) printf(x) #else - #define SAYF(x...) fprintf(stderr, x) + #define MY_SAYF(x...) fprintf(stderr, x) #endif /* ^MESSAGES_TO_STDOUT */ /* Show a prefixed warning. */ @@ -222,7 +282,7 @@ do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ @@ -235,7 +295,7 @@ do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ abort(); \ @@ -249,7 +309,7 @@ \ fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/include/envs.h b/include/envs.h index c0f41ca5..f16e61f0 100644 --- a/include/envs.h +++ b/include/envs.h @@ -103,6 +103,10 @@ static char *afl_environment_variables[] = { "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", +#if defined USE_COLOR && ! defined ALWAYS_COLORED + "AFL_NO_COLOR", + "AFL_NO_COLOUR", +#endif "AFL_NO_CPU_RED", "AFL_NO_FORKSRV", "AFL_NO_UI", diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 9c51a3ef..e863c4c7 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -401,6 +401,22 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_crash_exitcode = (u8 *)get_afl_env(afl_environment_variables[i]); +#if defined USE_COLOR && ! defined ALWAYS_COLORED + } else if (!strncmp(env, "AFL_NO_COLOR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); + + } else if (!strncmp(env, "AFL_NO_COLOUR", + + afl_environment_variable_len)) { + + afl->afl_env.afl_statsd_tags_flavor = + (u8 *)get_afl_env(afl_environment_variables[i]); +#endif + } } else { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 391d4c4f..e0d46f7e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -156,6 +156,12 @@ static void usage(u8 *argv0, int more_help) { if (more_help > 1) { +#if defined USE_COLOR && !defined ALWAYS_COLORED + #define DYN_COLOR "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" +#else + #define DYN_COLOR +#endif + SAYF( "Environment variables used:\n" "LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n" @@ -194,6 +200,9 @@ static void usage(u8 *argv0, int more_help) { "AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n" "AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n" "AFL_NO_UI: switch status screen off\n" + + DYN_COLOR + "AFL_PATH: path to AFL support binaries\n" "AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n" "AFL_QUIET: suppress forkserver status messages\n" @@ -298,6 +307,12 @@ int main(int argc, char **argv_orig, char **envp) { struct timeval tv; struct timezone tz; +#if defined USE_COLOR && defined ALWAYS_COLORED + if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) { + WARNF("Setting AFL_NO_COLOR has no effect (colors are configured on at compile time)"); + } +#endif + char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); -- cgit 1.4.1 From 7374503f14eb2de1ef298fb5f0a16ede1b7ead1b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 19 Dec 2020 09:55:51 +0100 Subject: add tinyinst to binary fuzzing tools --- docs/binaryonly_fuzzing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/binaryonly_fuzzing.md b/docs/binaryonly_fuzzing.md index 66734452..787d970d 100644 --- a/docs/binaryonly_fuzzing.md +++ b/docs/binaryonly_fuzzing.md @@ -174,7 +174,7 @@ Pintool and Dynamorio are dynamic instrumentation engines, and they can be used for getting basic block information at runtime. - Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows + Pintool is only available for Intel x32/x64 on Linux, Mac OS and Windows, whereas Dynamorio is additionally available for ARM and AARCH64. Dynamorio is also 10x faster than Pintool. @@ -182,7 +182,7 @@ Dynamorio has a speed decrease of 98-99% Pintool has a speed decrease of 99.5% - Hence Dynamorio is the option to go for if everything fails, and Pintool + Hence Dynamorio is the option to go for if everything else fails, and Pintool only if Dynamorio fails too. Dynamorio solutions: @@ -205,6 +205,7 @@ * QSYM: [https://github.com/sslab-gatech/qsym](https://github.com/sslab-gatech/qsym) * Manticore: [https://github.com/trailofbits/manticore](https://github.com/trailofbits/manticore) * S2E: [https://github.com/S2E](https://github.com/S2E) + * Tinyinst [https://github.com/googleprojectzero/TinyInst](https://github.com/googleprojectzero/TinyInst) (Mac/Windows only) * ... please send me any missing that are good -- cgit 1.4.1 From bc9f956c84dacdf34e7cfaf8fc6552b1ae4b8417 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 11:30:11 +0100 Subject: update changelog --- docs/Changelog.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ac75c68d..ec231f4e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,7 +10,10 @@ sending a mail to . ### Version ++3.01a (release) - - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - afl-fuzz + - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - switched to a faster RNG + - added hghwng's patch for faster trace map analysis - added dummy Makefile to instrumentation/ - allow instrumenting LLVMFuzzerTestOneInput -- cgit 1.4.1 From 98ee17bc47b1f1d4664be9955a72727fff8e51fa Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 14:30:06 +0100 Subject: fix endless loop in afl-cc allow/blocklists starting a line with a comment --- docs/Changelog.md | 8 +++- include/config.h | 13 ++++--- include/debug.h | 76 ++++++++++++++++++++++---------------- include/envs.h | 4 +- instrumentation/afl-gcc-pass.so.cc | 6 ++- instrumentation/afl-llvm-common.cc | 6 ++- src/afl-cc.c | 2 +- src/afl-fuzz-state.c | 3 +- src/afl-fuzz.c | 14 +++++-- 9 files changed, 80 insertions(+), 52 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ebc514f3..28b7e723 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,9 +10,13 @@ sending a mail to . ### Version ++3.01a (release) - - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - fix crash for very, very fast targets+systems (thanks to mhlakhani + for reporting) - added dummy Makefile to instrumentation/ - - allow instrumenting LLVMFuzzerTestOneInput + - afl-cc + - allow instrumenting LLVMFuzzerTestOneInput + - fixed endless loop for allow/blocklist lines starting with a + comment (thanks to Zherya for reporting) ### Version ++3.00c (release) diff --git a/include/config.h b/include/config.h index d57ef223..e8a49270 100644 --- a/include/config.h +++ b/include/config.h @@ -37,14 +37,14 @@ ******************************************************/ /* console output colors: There are three ways to configure its behavior - * 1. default: colored outputs fixed on: defined USE_COLOR && defined ALWAYS_COLORED - * The env var. AFL_NO_COLOR will have no effect + * 1. default: colored outputs fixed on: defined USE_COLOR && defined + * ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect * 2. defined USE_COLOR && !defined ALWAYS_COLORED * -> depending on env var AFL_NO_COLOR=1 colors can be switched off * at run-time. Default is to use colors. * 3. colored outputs fixed off: !defined USE_COLOR * The env var. AFL_NO_COLOR will have no effect -*/ + */ /* Comment out to disable terminal colors (note that this makes afl-analyze a lot less nice): */ @@ -52,9 +52,10 @@ #define USE_COLOR #ifdef USE_COLOR -/* Comment in to always enable terminal colors */ -/* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR */ -#define ALWAYS_COLORED 1 + /* Comment in to always enable terminal colors */ + /* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR + */ + #define ALWAYS_COLORED 1 #endif /* StatsD config diff --git a/include/debug.h b/include/debug.h index 7a1725b5..7f4a6be1 100644 --- a/include/debug.h +++ b/include/debug.h @@ -169,65 +169,77 @@ ************************/ #if defined USE_COLOR && !defined ALWAYS_COLORED -#include -#pragma GCC diagnostic ignored "-Wformat-security" -static inline const char * colorfilter(const char * x) { + #include + #pragma GCC diagnostic ignored "-Wformat-security" +static inline const char *colorfilter(const char *x) { + static int once = 1; static int disabled = 0; if (once) { + /* when there is no tty -> we always want filtering * when AFL_NO_UI is set filtering depends on AFL_NO_COLOR * otherwise we want always colors */ - disabled = isatty(2) && (!getenv("AFL_NO_UI") || (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); + disabled = + isatty(2) && (!getenv("AFL_NO_UI") || + (!getenv("AFL_NO_COLOR") && !getenv("AFL_NO_COLOUR"))); once = 0; + } + if (likely(disabled)) return x; static char monochromestring[4096]; - char *d = monochromestring; - int in_seq = 0; + char * d = monochromestring; + int in_seq = 0; + + while (*x) { - while(*x) { if (in_seq && *x == 'm') { + in_seq = 0; + } else { + if (!in_seq && *x == '\x1b') { in_seq = 1; } - if (!in_seq) { - *d++ = *x; - } + if (!in_seq) { *d++ = *x; } + } + ++x; + } *d = '\0'; return monochromestring; + } + #else -#define colorfilter(x) x /* no filtering necessary */ + #define colorfilter(x) x /* no filtering necessary */ #endif /* macro magic to transform the first parameter to SAYF * through colorfilter which strips coloring */ -#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,\ -_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,\ -_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,\ -_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,\ -NAME,...) NAME - -#define SAYF(...) GET_MACRO(__VA_ARGS__, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ -SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) - -#define SAYF_1(x) MY_SAYF(colorfilter(x)) -#define SAYF_N(x,...) MY_SAYF(colorfilter(x), __VA_ARGS__) +#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, \ + _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, \ + _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, \ + _39, _40, NAME, ...) \ + NAME + +#define SAYF(...) \ + GET_MACRO(__VA_ARGS__, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_N, \ + SAYF_N, SAYF_1) \ + (__VA_ARGS__) + +#define SAYF_1(x) MY_SAYF(colorfilter(x)) +#define SAYF_N(x, ...) MY_SAYF(colorfilter(x), __VA_ARGS__) /* Just print stuff to the appropriate stream. */ #ifdef MESSAGES_TO_STDOUT @@ -282,7 +294,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ exit(1); \ @@ -295,7 +307,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) do { \ \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ __FILE__, __LINE__); \ abort(); \ @@ -309,7 +321,7 @@ SAYF_N, SAYF_N, SAYF_N, SAYF_N, SAYF_1)(__VA_ARGS__) \ fflush(stdout); \ SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/include/envs.h b/include/envs.h index f16e61f0..e4e49c4d 100644 --- a/include/envs.h +++ b/include/envs.h @@ -78,8 +78,8 @@ static char *afl_environment_variables[] = { "AFL_LLVM_CTX", "AFL_LLVM_DICT2FILE", "AFL_LLVM_DOCUMENT_IDS", - "AFL_LLVM_INSTRUMENT", "AFL_LLVM_INSTRIM_LOOPHEAD", + "AFL_LLVM_INSTRUMENT", "AFL_LLVM_LTO_AUTODICTIONARY", "AFL_LLVM_AUTODICTIONARY", "AFL_LLVM_SKIPSINGLEBLOCK", @@ -103,7 +103,7 @@ static char *afl_environment_variables[] = { "AFL_NO_ARITH", "AFL_NO_AUTODICT", "AFL_NO_BUILTIN", -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED "AFL_NO_COLOR", "AFL_NO_COLOUR", #endif diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index c95ead8f..25437609 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -622,10 +622,11 @@ struct afl_pass : gimple_opt_pass { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -696,10 +697,11 @@ struct afl_pass : gimple_opt_pass { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 557939fd..a27c4069 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -168,10 +168,11 @@ void initInstrumentList() { allowListFiles.push_back(line); else allowListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) @@ -242,10 +243,11 @@ void initInstrumentList() { denyListFiles.push_back(line); else denyListFunctions.push_back(line); - getline(fileStream, line); } + getline(fileStream, line); + } if (debug) diff --git a/src/afl-cc.c b/src/afl-cc.c index 2aeb2178..8593f9b8 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1432,7 +1432,7 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " "functions\n" - " AFL_LLVM_INSTRUMENT_ALLOW/AFL_LLVM_INSTRUMENT_DENY: enable " + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " "instrument allow/\n" " deny listing (selective instrumentation)\n"); diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index e863c4c7..7053572b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -401,7 +401,8 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_crash_exitcode = (u8 *)get_afl_env(afl_environment_variables[i]); -#if defined USE_COLOR && ! defined ALWAYS_COLORED +#if defined USE_COLOR && !defined ALWAYS_COLORED + } else if (!strncmp(env, "AFL_NO_COLOR", afl_environment_variable_len)) { diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e0d46f7e..2af374f2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -157,7 +157,8 @@ static void usage(u8 *argv0, int more_help) { if (more_help > 1) { #if defined USE_COLOR && !defined ALWAYS_COLORED - #define DYN_COLOR "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" + #define DYN_COLOR \ + "AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n" #else #define DYN_COLOR #endif @@ -307,11 +308,16 @@ int main(int argc, char **argv_orig, char **envp) { struct timeval tv; struct timezone tz; -#if defined USE_COLOR && defined ALWAYS_COLORED + #if defined USE_COLOR && defined ALWAYS_COLORED if (getenv("AFL_NO_COLOR") || getenv("AFL_NO_COLOUR")) { - WARNF("Setting AFL_NO_COLOR has no effect (colors are configured on at compile time)"); + + WARNF( + "Setting AFL_NO_COLOR has no effect (colors are configured on at " + "compile time)"); + } -#endif + + #endif char **argv = argv_cpy_dup(argc, argv_orig); -- cgit 1.4.1 From 2e3cf10070681375a6c0e63ad39e7ce04ff22684 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 20 Dec 2020 22:53:41 +0100 Subject: document AFL_LLVM_INSTRUMENT option NATIVE --- docs/Changelog.md | 2 ++ docs/env_variables.md | 2 ++ instrumentation/README.llvm.md | 21 +-------------------- src/afl-cc.c | 3 +++ 4 files changed, 8 insertions(+), 20 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 28b7e723..a26a4e0e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,8 @@ sending a mail to . - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) + - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support + (less performant than our own) ### Version ++3.00c (release) diff --git a/docs/env_variables.md b/docs/env_variables.md index 74863d8d..c1693748 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -113,6 +113,8 @@ Then there are a few specific features that are only available in instrumentatio - `AFL_LLVM_INSTRUMENT` - this configures the instrumentation mode. Available options: + PCGUARD - our own pcgard based instrumentation (default) + NATIVE - clang's original pcguard based instrumentation CLASSIC - classic AFL (map[cur_loc ^ prev_loc >> 1]++) (default) CFG - InsTrim instrumentation (see below) LTO - LTO instrumentation (see below) diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 07636970..2705ce0d 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -168,26 +168,7 @@ This is the most powerful and effective fuzzing you can do. Please see [README.persistent_mode.md](README.persistent_mode.md) for a full explanation. -## 7) Bonus feature: 'trace-pc-guard' mode - -LLVM is shipping with a built-in execution tracing feature -that provides AFL with the necessary tracing data without the need to -post-process the assembly or install any compiler plugins. See: - - http://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards - -If you have not an outdated compiler and want to give it a try, build -targets this way: - -``` -AFL_LLVM_INSTRUMENT=PCGUARD make -``` - -Note that this is currently the default if you use LLVM >= 7, as it is the best -mode. Recommended is LLVM >= 9. -If you have llvm 11+ and compiled afl-clang-lto - this is the only better mode. - -## 8) Bonus feature: 'dict2file' pass +## 7) Bonus feature: 'dict2file' pass Just specify `AFL_LLVM_DICT2FILE=/absolute/path/file.txt` and during compilation all constant string compare parameters will be written to this file to be diff --git a/src/afl-cc.c b/src/afl-cc.c index 8593f9b8..6f4801de 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1346,6 +1346,9 @@ int main(int argc, char **argv, char **envp) { "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " "available)\n" " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" +#if LLVM_MAJOR > 10 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + " NATIVE: use llvm's native PCGUARD instrumentation (less performant)\n" +#endif " CLASSIC: decision target instrumentation (README.llvm.md)\n" " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " -- cgit 1.4.1 From ed85d5374b83c414e6219f7a3927cd8c160a7f37 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 21 Dec 2020 11:56:01 +0100 Subject: switch to github actions --- .github/workflows/ci.yml | 21 +++++++++++++++++ .travis.yml | 59 ------------------------------------------------ docs/Changelog.md | 9 ++++---- 3 files changed, 26 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml (limited to 'docs') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..469ee6d0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI +'on': + push: + branches: + - stable, dev + pull_request: + branches: + - stable, dev +jobs: + Build: + runs-on: '${{ matrix.os }}' + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-18.04] + steps: + - uses: actions/checkout@v2 + - run: sudo apt install -y git libtool libtool-bin automake bison libglib2.0-0 build-essential clang gcc gcc-plugin-dev libc++-dev findutils libcmocka-dev python3-setuptools + - run: gcc -v + - run: clang -v + - run: sudo -E ./afl-system-config ; sudo sysctl -w kernel.shmmax=10000000000 ; export AFL_SKIP_CPUFREQ=1 ; make distrib ASAN_BUILD=1 + - run: make tests diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b8b36e6b..00000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -language: c - -sudo: required - -branches: - only: - - stable - - dev - -matrix: - include: - #- os: linux # again disabled because fetching packages times out very often :( - # dist: focal - # env: NAME="focal-amd64" MODERN="yes" GCC="9" - - os: linux - dist: bionic - env: NAME="bionic-amd64" MODERN="yes" GCC="7" - - os: linux - dist: xenial - env: NAME="xenial-amd64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" -# - os: linux # disabled: fatal: unable to access 'https://git.qemu.org/git/capstone/': gnutls_handshake() failed: Handshake failed -# dist: trusty -# env: NAME="trusty-amd64" MODERN="no" GCC="4.8" - - os: linux # until travis can fix this! - dist: xenial - arch: arm64 - env: NAME="xenial-arm64" MODERN="no" GCC="5" EXTRA="libtool-bin clang-6.0" AFL_NO_X86="1" CPU_TARGET="aarch64" -# - os: osx -# osx_image: xcode11.2 -# env: NAME="osx" HOMEBREW_NO_ANALYTICS="1" LINK="http://releases.llvm.org/9.0.0/" NAME="clang+llvm-9.0.0-x86_64-darwin-apple" - -jobs: - allow_failures: - - os: osx - - arch: arm64 - -env: - - AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 - # - AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 AFL_EXIT_WHEN_DONE=1 - # TODO: test AFL_BENCH_UNTIL_CRASH once we have a target that crashes - # - AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_NO_UI=1 AFL_BENCH_JUST_ONE=1 - -before_install: - # export LLVM_DIR=${TRAVIS_BUILD_DIR}/${LLVM_PACKAGE} - - echo Testing on $NAME - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then wget "$LINK""$NAME".tar.xz ; export LLVM_CONFIG=`pwd`/"$NAME" ; tar xJf "$NAME".tar.xz ; fi - - if [ "$MODERN" = "yes" ]; then sudo apt update ; sudo apt upgrade ; sudo apt install -y git libtool libtool-bin automake bison libglib2.0-0 build-essential clang gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-"$GCC"-dev findutils libcmocka-dev python3-setuptools ; fi - - if [ "$MODERN" = "no" ]; then sudo apt update ; sudo apt install -y git libtool $EXTRA libpixman-1-dev automake bison libglib2.0 build-essential gcc-"$GCC" gcc-"$GCC"-plugin-dev libc++-dev findutils libcmocka-dev python3-setuptools ; fi - -script: - - gcc -v - - clang -v - - sudo -E ./afl-system-config - - sudo sysctl -w kernel.shmmax=10000000000 - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export LLVM_CONFIG=`pwd`/"$NAME" ; make source-only ASAN_BUILD=1 ; fi - - if [ "$TRAVIS_OS_NAME" = "linux" -a "$TRAVIS_CPU_ARCH" = "amd64" ]; then make distrib ASAN_BUILD=1 ; fi - - if [ "$TRAVIS_CPU_ARCH" = "arm64" ] ; then export LLVM_CONFIG=llvm-config-6.0 ; make ASAN_BUILD=1 ; cd qemu_mode && sh ./build_qemu_support.sh ; cd .. ; fi - - make tests -# - travis_terminate 0 diff --git a/docs/Changelog.md b/docs/Changelog.md index 0652a295..e36e4e9f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,16 +11,17 @@ sending a mail to . ### Version ++3.01a (release) - afl-fuzz - - fix crash for very, very fast targets+systems, thanks for reporting @mhlakhani + - fix crash for very, very fast targets+systems (thanks to mhlakhani + for reporting) - switched to a faster RNG - added hghwng's patch for faster trace map analysis - - added dummy Makefile to instrumentation/ - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support - (less performant than our own) + - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard + support (less performant than our own) + - added dummy Makefile to instrumentation/ ### Version ++3.00c (release) -- cgit 1.4.1 From 27b9ba45026397ee0605dd88aab359c4c1dea4cc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 22 Dec 2020 10:51:40 +0100 Subject: better gcc and clang support for afl-cc --- docs/Changelog.md | 3 +- docs/env_variables.md | 2 + src/afl-cc.c | 134 ++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 123 insertions(+), 16 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e36e4e9f..cf9bfbe1 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,7 +20,8 @@ sending a mail to . - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard - support (less performant than our own) + support (less performant than our own), GCC for old afl-gcc and + CLANG for old afl-clang - added dummy Makefile to instrumentation/ diff --git a/docs/env_variables.md b/docs/env_variables.md index c1693748..e6b9381b 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -120,6 +120,8 @@ Then there are a few specific features that are only available in instrumentatio LTO - LTO instrumentation (see below) CTX - context sensitive instrumentation (see below) NGRAM-x - deeper previous location coverage (from NGRAM-2 up to NGRAM-16) + GCC - outdated gcc instrumentation + CLANG - outdated clang instrumentation In CLASSIC (default) and CFG/INSTRIM you can also specify CTX and/or NGRAM, seperate the options with a comma "," then, e.g.: `AFL_LLVM_INSTRUMENT=CFG,CTX,NGRAM-4` diff --git a/src/afl-cc.c b/src/afl-cc.c index 3b8092a9..a18f87db 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -62,7 +62,7 @@ u8 use_stdin; /* dummy */ enum { - INSTURMENT_DEFAULT = 0, + INSTRUMENT_DEFAULT = 0, INSTRUMENT_CLASSIC = 1, INSTRUMENT_AFL = 1, INSTRUMENT_PCGUARD = 2, @@ -70,6 +70,8 @@ enum { INSTRUMENT_CFG = 3, INSTRUMENT_LTO = 4, INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, INSTRUMENT_OPT_CTX = 8, INSTRUMENT_OPT_NGRAM = 16 @@ -77,9 +79,24 @@ enum { char instrument_mode_string[18][18] = { - "DEFAULT", "CLASSIC", "PCGUARD", "CFG", "LTO", "", "PCGUARD-NATIVE", - "", "CTX", "", "", "", "", "", - "", "", "NGRAM", "" + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" }; @@ -89,14 +106,15 @@ enum { LTO = 1, LLVM = 2, GCC_PLUGIN = 3, - GCC = 4 + GCC = 4, + CLANG = 5 }; -char compiler_mode_string[6][12] = { +char compiler_mode_string[7][12] = { "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", - "GCC", "" + "GCC", "CLANG", "" }; @@ -324,6 +342,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cxx = clang_mode ? "clang++" : "g++"; + } else if (compiler_mode == CLANG) { + + alt_cxx = "clang++"; + } else { alt_cxx = "g++"; @@ -357,6 +379,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { alt_cc = clang_mode ? "clang" : "gcc"; + } else if (compiler_mode == CLANG) { + + alt_cc = "clang"; + } else { alt_cc = "gcc"; @@ -380,12 +406,16 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (compiler_mode == GCC) { + if (compiler_mode == GCC || compiler_mode == CLANG) { cc_params[cc_par_cnt++] = "-B"; cc_params[cc_par_cnt++] = obj_path; - if (clang_mode) { cc_params[cc_par_cnt++] = "-no-integrated-as"; } + if (clang_mode || compiler_mode == CLANG) { + + cc_params[cc_par_cnt++] = "-no-integrated-as"; + + } } @@ -996,12 +1026,14 @@ int main(int argc, char **argv, char **envp) { } else if (strncmp(callname, "afl-gcc", 7) == 0 || - strncmp(callname, "afl-g++", 7) == 0 || - - strncmp(callname, "afl-clang", 9) == 0) { + strncmp(callname, "afl-g++", 7) == 0) { compiler_mode = GCC; + } else if (strncmp(callname, "afl-clang", 9) == 0) { + + compiler_mode = CLANG; + } if ((ptr = getenv("AFL_CC_COMPILER"))) { @@ -1045,6 +1077,7 @@ int main(int argc, char **argv, char **envp) { if (strncmp(callname, "afl-clang", 9) == 0) { clang_mode = 1; + compiler_mode = CLANG; if (strncmp(callname, "afl-clang++", 11) == 0) { plusplus_mode = 1; } @@ -1072,6 +1105,34 @@ int main(int argc, char **argv, char **envp) { compiler_mode = LLVM; + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + + strncasecmp(ptr, "PC-GUARD", 8) == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_PCGUARD; + + } else if (strcasecmp(ptr, "INSTRIM") == 0 || + + strcasecmp(ptr, "CFG") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CFG; + + } else if (strcasecmp(ptr, "AFL") == 0 || + + strcasecmp(ptr, "CLASSIC") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_CLASSIC; + + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + + strcasecmp(ptr, "LLVM-NATIVE") == 0) { + + compiler_mode = LLVM; + instrument_mode = INSTRUMENT_LLVMNATIVE; + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || strncasecmp(ptr, "GCC-P", 5) == 0 || @@ -1083,6 +1144,10 @@ int main(int argc, char **argv, char **envp) { compiler_mode = GCC; + } else if (strcasecmp(ptr, "CLANG") == 0) { + + compiler_mode = CLANG; + } else FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); @@ -1212,6 +1277,28 @@ int main(int argc, char **argv, char **envp) { } + if (strcasecmp(ptr, "gcc") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) + instrument_mode = INSTRUMENT_GCC; + else if (instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = GCC; + + } + + if (strcasecmp(ptr, "clang") == 0) { + + if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) + instrument_mode = INSTRUMENT_CLANG; + else if (instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_string[instrument_mode]); + compiler_mode = CLANG; + + } + if (strncasecmp(ptr, "ctx", strlen("ctx")) == 0) { instrument_opt_mode |= INSTRUMENT_OPT_CTX; @@ -1270,6 +1357,22 @@ int main(int argc, char **argv, char **envp) { } + if (compiler_mode == GCC) { + + if (clang_mode) { + + instrument_mode = CLANG; + + } else { + + instrument_mode = GCC; + + } + + } + + if (compiler_mode == CLANG) { instrument_mode = CLANG; } + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { printf("afl-cc" VERSION @@ -1316,7 +1419,7 @@ int main(int argc, char **argv, char **envp) { " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes yes no no no " " yes\n" - " [GCC] simple gcc: %s%s\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" " CLASSIC DEFAULT no no no no no no " " no\n\n", have_lto ? "AVAILABLE" : "unavailable!", @@ -1328,7 +1431,7 @@ int main(int argc, char **argv, char **envp) { have_gcc_plugin ? "AVAILABLE" : "unavailable!", compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", have_gcc ? "AVAILABLE" : "unavailable!", - compiler_mode == GCC ? " [SELECTED]" : ""); + (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : ""); SAYF( "Modes:\n" @@ -1445,7 +1548,8 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, CTX, NGRAM-2 ... NGRAM-16\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " + "NGRAM-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" -- cgit 1.4.1 From 1c79687dfe4dc3260b8141fe9be7c9763679ba80 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 23 Dec 2020 15:56:25 +0100 Subject: faster llvm compile, update README --- GNUmakefile | 6 +++--- README.md | 46 ++++++++++++++++++++-------------------------- docs/Changelog.md | 2 ++ 3 files changed, 25 insertions(+), 29 deletions(-) (limited to 'docs') diff --git a/GNUmakefile b/GNUmakefile index a1af1fd5..b9c806c3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -307,7 +307,7 @@ all: test_x86 test_shm test_python ready $(PROGS) afl-as llvm gcc_plugin test_bu .PHONY: llvm llvm: - -$(MAKE) -f GNUmakefile.llvm + -$(MAKE) -j4 -f GNUmakefile.llvm @test -e afl-cc || { echo "[-] Compiling afl-cc failed. You seem not to have a working compiler." ; exit 1; } .PHONY: gcc_plugin @@ -579,7 +579,7 @@ deepclean: clean .PHONY: distrib distrib: all - -$(MAKE) -f GNUmakefile.llvm + -$(MAKE) -j4 -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap @@ -602,7 +602,7 @@ binary-only: test_shm test_python ready $(PROGS) .PHONY: source-only source-only: all - -$(MAKE) -f GNUmakefile.llvm + -$(MAKE) -j4 -f GNUmakefile.llvm -$(MAKE) -f GNUmakefile.gcc_plugin $(MAKE) -C utils/libdislocator $(MAKE) -C utils/libtokencap diff --git a/README.md b/README.md index a0e7a7e4..6ea06140 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,8 @@ With afl++ 3.0 we introduced changes that break some previous afl and afl++ behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is - only one compiler: afl-cc. All previous compilers now symlink to this one - compiler. All instrumentation source code is now in the `instrumentation/` - folder. + only one compiler: afl-cc. All previous compilers now symlink to this. + All instrumentation source code is now in the `instrumentation/` folder. * The gcc_plugin was replaced with a new version submitted by AdaCore that supports more features. thank you! * qemu_mode got upgraded to QEMU 5.1, but to be able to build this a current @@ -41,8 +40,9 @@ behaviours and defaults: qemu_mode also got new options like snapshotting, instrumenting specific shared libraries, etc. Additionally QEMU 5.1 supports more CPU targets so this is really worth it. - * When instrumenting targets, afl-cc will not supersede optimizations. This - allows to fuzz targets as same as they are built for debug or release. + * When instrumenting targets, afl-cc will not supersede optimizations anymore + if any were given. This allows to fuzz targets as same as they are built + for debug or release. * afl-fuzz: * if neither -M or -S is specified, `-S default` is assumed, so more fuzzers can easily be added later @@ -88,7 +88,7 @@ behaviours and defaults: | Ngram prev_loc Coverage | | x(6) | | | | | Context Coverage | | x(6) | | | | | Auto Dictionary | | x(7) | | | | - | Snapshot LKM Support | | x | x | (x)(5) | | + | Snapshot LKM Support | | x(8) | x(8) | (x)(5) | | 1. default for LLVM >= 9.0, env var for older version due an efficiency bug in llvm <= 8 2. GCC creates non-performant code, hence it is disabled in gcc_plugin @@ -97,6 +97,7 @@ behaviours and defaults: 5. upcoming, development in the branch 6. not compatible with LTO instrumentation and needs at least LLVM >= 4.1 7. automatic in LTO mode with LLVM >= 11, an extra pass for all LLVM version that writes to a file to use with afl-fuzz' `-x` + 8. the snapshot LKM is currently unmaintained due to too many kernel changes coming too fast :-( Among others, the following features and patches have been integrated: @@ -139,9 +140,6 @@ behaviours and defaults: ## Help wanted -We were happy to be part of [Google Summer of Code 2020](https://summerofcode.withgoogle.com/organizations/5100744400699392/) -and we will try to participate again in 2021! - We have several ideas we would like to see in AFL++ to make it even better. However, we already work on so many things that we do not have the time for all the big ideas. @@ -206,7 +204,7 @@ These build targets exist: afl++ binaries by passing the STATIC=1 argument to make: ```shell -make all STATIC=1 +make STATIC=1 ``` These build options exist: @@ -283,9 +281,9 @@ anything below 9 is not recommended. | v +--------------------------------+ - | if you want to instrument only | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast) - | parts of the target | see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and - +--------------------------------+ [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) + | gcc 5+ is available | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast) + +--------------------------------+ see [instrumentation/README.gcc_plugin.md](instrumentation/README.gcc_plugin.md) and + [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) | | if not, or if you do not have a gcc with plugin support | @@ -298,17 +296,17 @@ Clickable README links for the chosen compiler: * [LTO mode - afl-clang-lto](instrumentation/README.lto.md) * [LLVM mode - afl-clang-fast](instrumentation/README.llvm.md) * [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md) - * GCC mode (afl-gcc) has no README as it has no own features + * GCC/CLANG mode (afl-gcc/afl-clant) have no README as they have no own features You can select the mode for the afl-cc compiler by: - 1. passing --afl-MODE command line options to the compiler via CFLAGS/CXXFLAGS/CPPFLAGS - 2. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, + 1. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, afl-clang-fast, afl-clang-fast++, afl-clang-lto, afl-clang-lto++, - afl-gcc-fast, afl-g++-fast - 3. using the environment variable AFL_CC_COMPILER with MODE + afl-gcc-fast, afl-g++-fast (recommended!) + 2. using the environment variable AFL_CC_COMPILER with MODE + 3. passing --afl-MODE command line options to the compiler via CFLAGS/CXXFLAGS/CPPFLAGS MODE can be one of: LTO (afl-clang-lto*), LLVM (afl-clang-fast*), GCC_PLUGIN -(afl-g*-fast) or GCC (afl-gcc/afl-g++). +(afl-g*-fast) or GCC (afl-gcc/afl-g++) or CLANG(afl-clang/afl-clang++). Because no afl specific command-line options are accepted (beside the --afl-MODE command), the compile-time tools make fairly broad use of environment @@ -338,14 +336,14 @@ The following options are available when you instrument with LTO mode (afl-clang You can read more about this in [instrumentation/README.cmplog.md](instrumentation/README.cmplog.md) If you use LTO, LLVM or GCC_PLUGIN mode (afl-clang-fast/afl-clang-lto/afl-gcc-fast) - you have the option to selectively only instrument parts of the target that you +you have the option to selectively only instrument parts of the target that you are interested in: * To instrument only those parts of the target that you are interested in create a file with all the filenames of the source code that should be instrumented. - For afl-clang-lto and afl-gcc-fast - or afl-clang-fast if either the clang - version is below 7 or the CLASSIC instrumentation is used - just put one + For afl-clang-lto and afl-gcc-fast - or afl-clang-fast if a mode other than + DEFAULT/PCGUARD is used or you have llvm > 10.0.0 - just put one filename or function per line (no directory information necessary for filenames9, and either set `export AFL_LLVM_ALLOWLIST=allowlist.txt` **or** `export AFL_LLVM_DENYLIST=denylist.txt` - depending on if you want per @@ -353,10 +351,6 @@ are interested in: unless requested (ALLOWLIST). **NOTE:** During optimization functions might be inlined and then would not match! See [instrumentation/README.instrument_list.md](instrumentation/README.instrument_list.md) - For afl-clang-fast > 6.0 or if PCGUARD instrumentation is used then use the - llvm sancov allow-list feature: [http://clang.llvm.org/docs/SanitizerCoverage.html](http://clang.llvm.org/docs/SanitizerCoverage.html) - The llvm sancov format works with the allowlist/denylist feature of afl++ - however afl++'s format is more flexible. There are many more options and modes available however these are most of the time less effective. See: diff --git a/docs/Changelog.md b/docs/Changelog.md index cf9bfbe1..c18e8bc1 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,8 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang + - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was + already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ -- cgit 1.4.1 From 849b8cd0840ade686c58c309810ad89b276b9755 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 23 Dec 2020 18:38:12 +0100 Subject: typo --- README.md | 2 +- docs/Changelog.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/README.md b/README.md index 6ea06140..9f74329c 100644 --- a/README.md +++ b/README.md @@ -296,7 +296,7 @@ Clickable README links for the chosen compiler: * [LTO mode - afl-clang-lto](instrumentation/README.lto.md) * [LLVM mode - afl-clang-fast](instrumentation/README.llvm.md) * [GCC_PLUGIN mode - afl-gcc-fast](instrumentation/README.gcc_plugin.md) - * GCC/CLANG mode (afl-gcc/afl-clant) have no README as they have no own features + * GCC/CLANG mode (afl-gcc/afl-clang) have no README as they have no own features You can select the mode for the afl-cc compiler by: 1. use a symlink to afl-cc: afl-gcc, afl-g++, afl-clang, afl-clang++, diff --git a/docs/Changelog.md b/docs/Changelog.md index c18e8bc1..c1cd2d5a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -22,6 +22,7 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang + - warn on any _AFL and __AFL env var - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ -- cgit 1.4.1 From 2c4b51b437b6b75abc9fde1963daf4f1eea25e5f Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 26 Dec 2020 22:01:49 +0100 Subject: utils/afl_frida is not 5% faster --- docs/Changelog.md | 1 + utils/afl_frida/afl-frida.c | 81 +-------------------------------------------- 2 files changed, 2 insertions(+), 80 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c1cd2d5a..a8b8f0af 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,7 @@ sending a mail to . - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ + - Updated utils/afl_frida to be 5% faster ### Version ++3.00c (release) diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c index 31bf8f25..e4d442a9 100644 --- a/utils/afl_frida/afl-frida.c +++ b/utils/afl_frida/afl-frida.c @@ -61,55 +61,12 @@ static void *(*o_function)(uint8_t *, int); #include "frida-gum.h" -G_BEGIN_DECLS - -#define GUM_TYPE_FAKE_EVENT_SINK (gum_fake_event_sink_get_type()) -G_DECLARE_FINAL_TYPE(GumFakeEventSink, gum_fake_event_sink, GUM, - FAKE_EVENT_SINK, GObject) - -struct _GumFakeEventSink { - - GObject parent; - GumEventType mask; - -}; - -GumEventSink *gum_fake_event_sink_new(void); -void gum_fake_event_sink_reset(GumFakeEventSink *self); - -G_END_DECLS - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data); -static void gum_fake_event_sink_finalize(GObject *obj); -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink); -static void gum_fake_event_sink_process(GumEventSink *sink, const GumEvent *ev); void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, gpointer user_data); void afl_setup(void); void afl_start_forkserver(void); int __afl_persistent_loop(unsigned int max_cnt); -static void gum_fake_event_sink_class_init(GumFakeEventSinkClass *klass) { - - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = gum_fake_event_sink_finalize; - -} - -static void gum_fake_event_sink_iface_init(gpointer g_iface, - gpointer iface_data) { - - GumEventSinkInterface *iface = (GumEventSinkInterface *)g_iface; - iface->query_mask = gum_fake_event_sink_query_mask; - iface->process = gum_fake_event_sink_process; - -} - -G_DEFINE_TYPE_EXTENDED(GumFakeEventSink, gum_fake_event_sink, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE(GUM_TYPE_EVENT_SINK, - gum_fake_event_sink_iface_init)) - #include "../../config.h" // Shared memory fuzzing. @@ -183,34 +140,6 @@ void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, } -static void gum_fake_event_sink_init(GumFakeEventSink *self) { - -} - -static void gum_fake_event_sink_finalize(GObject *obj) { - - G_OBJECT_CLASS(gum_fake_event_sink_parent_class)->finalize(obj); - -} - -GumEventSink *gum_fake_event_sink_new(void) { - - GumFakeEventSink *sink; - sink = (GumFakeEventSink *)g_object_new(GUM_TYPE_FAKE_EVENT_SINK, NULL); - return GUM_EVENT_SINK(sink); - -} - -void gum_fake_event_sink_reset(GumFakeEventSink *self) { - -} - -static GumEventType gum_fake_event_sink_query_mask(GumEventSink *sink) { - - return 0; - -} - typedef struct library_list { uint8_t *name; @@ -402,11 +331,6 @@ library_list_t *find_library(char *name) { } -static void gum_fake_event_sink_process(GumEventSink * sink, - const GumEvent *ev) { - -} - /* Because this CAN be called more than once, it will return the LAST range */ static int enumerate_ranges(const GumRangeDetails *details, gpointer user_data) { @@ -484,8 +408,6 @@ int main() { gum_stalker_transformer_make_from_callback(instr_basic_block, &instr_range, NULL); - GumEventSink *event_sink = gum_fake_event_sink_new(); - // to ensure that the signatures are not optimized out memcpy(__afl_area_ptr, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT) + 1); memcpy(__afl_area_ptr + 32, (void *)AFL_DEFER_FORKSVR, @@ -497,7 +419,7 @@ int main() { // - put that here // - gum_stalker_follow_me(stalker, transformer, event_sink); + gum_stalker_follow_me(stalker, transformer, NULL); while (__afl_persistent_loop(UINT32_MAX) != 0) { @@ -533,7 +455,6 @@ int main() { g_object_unref(stalker); g_object_unref(transformer); - g_object_unref(event_sink); gum_deinit_embedded(); return 0; -- cgit 1.4.1 From 688f4ffb89ebf41a497070e8fcf2927510b66874 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 28 Dec 2020 14:01:48 +0100 Subject: added corpus introspection --- docs/Changelog.md | 1 + include/afl-fuzz.h | 5 +++++ src/afl-fuzz-queue.c | 30 ++++++++++++++++++++++++++++++ src/afl-fuzz-run.c | 4 ++++ 4 files changed, 40 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index a8b8f0af..71846535 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.01a (release) + - Mac OS ARM64 support - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index e2fb0344..57b0e6cc 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -174,6 +174,10 @@ struct queue_entry { u8 *trace_mini; /* Trace bytes, if kept */ u32 tc_ref; /* Trace bytes ref count */ +#ifdef INTROSPECTION + u32 bitsmap_size; +#endif + double perf_score, /* performance score */ weight; @@ -734,6 +738,7 @@ typedef struct afl_state { char mutation[8072]; char m_tmp[4096]; FILE *introspection_file; + u32 bitsmap_size; #endif } afl_state_t; diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 54afa17c..5dc2d70b 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -190,6 +190,32 @@ void create_alias_table(afl_state_t *afl) { while (nS) afl->alias_probability[S[--nS]] = 1; +#ifdef INTROSPECTION + u8 fn[PATH_MAX]; + snprintf(fn, PATH_MAX, "%s/introspection_corpus.txt", afl->out_dir); + FILE *f = fopen(fn, "a"); + if (f) { + + for (i = 0; i < n; i++) { + + struct queue_entry *q = afl->queue_buf[i]; + fprintf( + f, + "entry=%u name=%s variable=%s disabled=%s len=%u exec_us=%u " + "bitmap_size=%u bitsmap_size=%u tops=%u weight=%f perf_score=%f\n", + i, q->fname, q->var_behavior ? "true" : "false", + q->disabled ? "true" : "false", q->len, (u32)q->exec_us, + q->bitmap_size, q->bitsmap_size, q->tc_ref, q->weight, q->perf_score); + + } + + fprintf(f, "\n"); + fclose(f); + + } + +#endif + /* fprintf(stderr, " entry alias probability perf_score filename\n"); for (u32 i = 0; i < n; ++i) @@ -398,6 +424,10 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) { q->trace_mini = NULL; q->testcase_buf = NULL; +#ifdef INTROSPECTION + q->bitsmap_size = afl->bitsmap_size; +#endif + if (q->depth > afl->max_depth) { afl->max_depth = q->depth; } if (afl->queue_top) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index d53ba546..339fb9c3 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -380,6 +380,10 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, } +#ifdef INTROSPECTION + if (unlikely(!q->bitsmap_size)) q->bitsmap_size = afl->bitsmap_size; +#endif + classify_counts(&afl->fsrv); cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); if (q->exec_cksum != cksum) { -- cgit 1.4.1 From c423aebb1a6987d8ca58175aee23bc08b611d4c8 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 4 Jan 2021 12:57:29 +0100 Subject: FP support for cmplog --- docs/Changelog.md | 1 + instrumentation/cmplog-instructions-pass.cc | 71 +++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 71846535..88dc74d2 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,6 +20,7 @@ sending a mail to . - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) + - cmplog/redqueen now also tracks floats/doubles - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 9921de0c..7ab38cf4 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -186,16 +186,19 @@ bool CmpLogInstructions::hookInstrs(Module &M) { selectcmpInst->getPredicate() == CmpInst::ICMP_UGE || selectcmpInst->getPredicate() == CmpInst::ICMP_SGE || selectcmpInst->getPredicate() == CmpInst::ICMP_ULE || - selectcmpInst->getPredicate() == CmpInst::ICMP_SLE) { - - auto op0 = selectcmpInst->getOperand(0); - auto op1 = selectcmpInst->getOperand(1); - - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); - - /* this is probably not needed but we do it anyway */ - if (!intTyOp0 || !intTyOp1) { continue; } + selectcmpInst->getPredicate() == CmpInst::ICMP_SLE || + selectcmpInst->getPredicate() == CmpInst::FCMP_OGE || + selectcmpInst->getPredicate() == CmpInst::FCMP_UGE || + selectcmpInst->getPredicate() == CmpInst::FCMP_OLE || + selectcmpInst->getPredicate() == CmpInst::FCMP_ULE || + selectcmpInst->getPredicate() == CmpInst::FCMP_OGT || + selectcmpInst->getPredicate() == CmpInst::FCMP_UGT || + selectcmpInst->getPredicate() == CmpInst::FCMP_OLT || + selectcmpInst->getPredicate() == CmpInst::FCMP_ULT || + selectcmpInst->getPredicate() == CmpInst::FCMP_UEQ || + selectcmpInst->getPredicate() == CmpInst::FCMP_OEQ || + selectcmpInst->getPredicate() == CmpInst::FCMP_UNE || + selectcmpInst->getPredicate() == CmpInst::FCMP_ONE) { icomps.push_back(selectcmpInst); @@ -221,16 +224,48 @@ bool CmpLogInstructions::hookInstrs(Module &M) { auto op0 = selectcmpInst->getOperand(0); auto op1 = selectcmpInst->getOperand(1); - IntegerType *intTyOp0 = dyn_cast(op0->getType()); - IntegerType *intTyOp1 = dyn_cast(op1->getType()); + IntegerType * intTyOp0 = NULL; + IntegerType * intTyOp1 = NULL; + unsigned max_size = 0; + std::vector args; - unsigned max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() - ? intTyOp0->getBitWidth() - : intTyOp1->getBitWidth(); + if (selectcmpInst->getOpcode() == Instruction::FCmp) { - std::vector args; - args.push_back(op0); - args.push_back(op1); + auto ty0 = op0->getType(); + if (ty0->isHalfTy() || ty0->isBFloatTy()) + max_size = 16; + else if (ty0->isFloatTy()) + max_size = 32; + else if (ty0->isDoubleTy()) + max_size = 64; + + if (max_size) { + + Value *V0 = IRB.CreateBitCast(op0, IntegerType::get(C, max_size)); + intTyOp0 = dyn_cast(V0->getType()); + Value *V1 = IRB.CreateBitCast(op1, IntegerType::get(C, max_size)); + intTyOp1 = dyn_cast(V1->getType()); + max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() + ? intTyOp0->getBitWidth() + : intTyOp1->getBitWidth(); + args.push_back(V0); + args.push_back(V1); + + } + + } else { + + intTyOp0 = dyn_cast(op0->getType()); + intTyOp1 = dyn_cast(op1->getType()); + max_size = intTyOp0->getBitWidth() > intTyOp1->getBitWidth() + ? intTyOp0->getBitWidth() + : intTyOp1->getBitWidth(); + args.push_back(op0); + args.push_back(op1); + + } + + if (max_size < 8 || max_size > 64 || !intTyOp0 || !intTyOp1) continue; switch (max_size) { -- cgit 1.4.1 From 6b54310452a1b743a90ad45fcc511f59dd7821ec Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 5 Jan 2021 12:30:26 +0100 Subject: selective instrumentation documented --- docs/Changelog.md | 5 ++- include/config.h | 4 ++- instrumentation/README.instrument_list.md | 53 ++++++++++++++++++++----------- src/afl-cc.c | 14 ++++++++ 4 files changed, 55 insertions(+), 21 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 88dc74d2..03b8e036 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,8 +9,11 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.01a (release) +### Version ++3.01a (dev) - Mac OS ARM64 support + - New selective instrumentation option with __AFL_COVERAGE_... commands + to be placed in the source code. + Check out instrumentation/README.instrument_list.md - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) diff --git a/include/config.h b/include/config.h index ba62d4c6..c0cd0ef1 100644 --- a/include/config.h +++ b/include/config.h @@ -186,7 +186,9 @@ #define TRIM_START_STEPS 16 #define TRIM_END_STEPS 1024 -/* Maximum size of input file, in bytes (keep under 100MB): */ +/* Maximum size of input file, in bytes (keep under 100MB, default 1MB): + (note that if this value is changed, several areas in afl-cc.c, afl-fuzz.c + and afl-fuzz-state.c have to be changed as well! */ #define MAX_FILE (1 * 1024 * 1024U) diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index 122be2b6..83197954 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -1,8 +1,9 @@ # Using afl++ with partial instrumentation - This file describes how to selectively instrument only source files - or functions that are of interest to you using the LLVM and GCC_PLUGIN - instrumentation provided by afl++. + This file describes two different mechanisms to selectively instrument + only specific parts in the target. + + Both mechanisms work for LLVM and GCC_PLUGIN, but not for afl-clang/afl-gcc. ## 1) Description and purpose @@ -12,28 +13,42 @@ the program, leaving the rest uninstrumented. This helps to focus the fuzzer on the important parts of the program, avoiding undesired noise and disturbance by uninteresting code being exercised. -For this purpose, a "partial instrumentation" support en par with llvm sancov -is provided by afl++ that allows to specify on a source file and function -level which function should be compiled with or without instrumentation. +For this purpose, "partial instrumentation" support is provided by afl++ that +allows to specify what should be instrumented and what not. + +Both mechanisms can be used together. + +## 2) Selective instrumentation with __AFL_COVERAGE_... directives + +In this mechanism the selective instrumentation is done in the source code. -Note: When using PCGUARD mode - and llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation +After the includes a special define has to be made, eg.: + +``` +#include +#include +// ... + +__AFL_COVERAGE(); // <- required for this feature to work +``` -The llvm sancov list format is fully supported by afl++, however afl++ has -more flexibility. +If you want to disable the coverage at startup until you specify coverage +should be started, then add `__AFL_COVERAGE_START_OFF();` at that position. -## 2a) Building the LLVM module +From here on out you have the following macros available that you can use +in any function where you want: -The new code is part of the existing afl++ LLVM module in the instrumentation/ -subdirectory. There is nothing specifically to do for the build :) + * `__AFL_COVERAGE_ON();` - enable coverage from this point onwards + * `__AFL_COVERAGE_OFF();` - disable coverage from this point onwards + * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point + * `__AFL_COVERAGE_ABORT();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. -## 2b) Building the GCC module +## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -The new code is part of the existing afl++ GCC_PLUGIN module in the -instrumentation/ subdirectory. There is nothing specifically to do for -the build :) +This feature is equivalent to llvm 12 sancov feature and allows to specify +on a filename and/or function name level to instrument these or skip them. -## 3) How to use the partial instrumentation mode +### 3a) How to use the partial instrumentation mode In order to build with partial instrumentation, you need to build with afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. @@ -90,7 +105,7 @@ fun: MallocFoo ``` Note that whitespace is ignored and comments (`# foo`) are supported. -## 4) UNIX-style pattern matching +### 3b) UNIX-style pattern matching You can add UNIX-style pattern matching in the "instrument file list" entries. See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/src/afl-cc.c b/src/afl-cc.c index db2dcd14..964df57f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -827,6 +827,20 @@ static void edit_params(u32 argc, char **argv, char **envp) { "extern unsigned char *__afl_fuzz_ptr;" "unsigned char __afl_fuzz_alt[1048576];" "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_abort();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " + "1;"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; + cc_params[cc_par_cnt++] = + "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; + cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ABORT()=__afl_coverage_abort()"; cc_params[cc_par_cnt++] = "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " "__afl_fuzz_alt_ptr)"; -- cgit 1.4.1 From a06b25538fd7b6eef6755094aa4678c2cb5333fd Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 7 Jan 2021 23:21:10 +0100 Subject: cleaned up AFL_KILL_SIGNAL --- afl-cmin | 1 + docs/Changelog.md | 2 +- docs/env_variables.md | 4 ++++ src/afl-forkserver.c | 45 +++++++-------------------------------------- src/afl-fuzz.c | 30 +++++++++++++++++++++++++++++- src/afl-showmap.c | 37 +++++++++++++++++++++++++++++++++---- src/afl-tmin.c | 29 +++++++++++++++++++++++++++++ 7 files changed, 104 insertions(+), 44 deletions(-) (limited to 'docs') diff --git a/afl-cmin b/afl-cmin index eef2b7ef..726e90ab 100755 --- a/afl-cmin +++ b/afl-cmin @@ -120,6 +120,7 @@ function usage() { "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" \ "AFL_FORKSRV_INIT_TMOUT: time the fuzzer waits for the target to come up, initially\n" \ "AFL_KEEP_TRACES: leave the temporary /.traces directory\n" \ +"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_PATH: path for the afl-showmap binary if not found anywhere else\n" \ "AFL_SKIP_BIN_CHECK: skip check for target binary\n" exit 1 diff --git a/docs/Changelog.md b/docs/Changelog.md index 03b8e036..81ac91b0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,7 +32,7 @@ sending a mail to . already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster - + - Added AFL_KILL_SIGNAL env variable for custom targets (thanks @v-p-b) ### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ diff --git a/docs/env_variables.md b/docs/env_variables.md index e6b9381b..26128b01 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -350,6 +350,10 @@ checks or alter some of the more exotic semantics of the tool: - Note that `AFL_POST_LIBRARY` is deprecated, use `AFL_CUSTOM_MUTATOR_LIBRARY` instead (see below). + - `AFL_KILL_SIGNAL`: Set the signal ID to be delivered to child processes on timeout. + Unless you implement your own targets or instrumentation, you likely don't have to set it. + By default, on timeout and on exit, `SIGKILL` (`AFL_KILL_SIGNAL=9`) will be delivered to the child. + - Setting `AFL_CUSTOM_MUTATOR_LIBRARY` to a shared library with afl_custom_fuzz() creates additional mutations through this library. If afl-fuzz is compiled with Python (which is autodetected during builing diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 70fb9572..b7aa87f8 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -84,6 +84,7 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->init_tmout = EXEC_TIMEOUT * FORK_WAIT_MULT; fsrv->mem_limit = MEM_LIMIT; fsrv->out_file = NULL; + fsrv->kill_signal = SIGKILL; /* exec related stuff */ fsrv->child_pid = -1; @@ -95,30 +96,6 @@ void afl_fsrv_init(afl_forkserver_t *fsrv) { fsrv->uses_asan = false; fsrv->init_child_func = fsrv_exec_child; - fsrv->kill_signal = SIGKILL; - - char *kill_signal_env = get_afl_env("AFL_KILL_SIGNAL"); - if (kill_signal_env) { - - char *endptr; - u8 signal_code; - signal_code = (u8)strtoul(kill_signal_env, &endptr, 10); - /* Did we manage to parse the full string? */ - if (*endptr != '\0' || endptr == kill_signal_env) { - - FATAL("Invalid kill signal value!"); - - } - - fsrv->kill_signal = signal_code; - - } else { - - /* Using hardcoded code for SIGKILL for the sake of simplicity */ - setenv("AFL_KILL_SIGNAL", "9", 1); - - } - list_append(&fsrv_list, fsrv); } @@ -139,6 +116,7 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->no_unlink = from->no_unlink; fsrv_to->uses_crash_exitcode = from->uses_crash_exitcode; fsrv_to->crash_exitcode = from->crash_exitcode; + fsrv_to->kill_signal = from->kill_signal; // These are forkserver specific. fsrv_to->out_dir_fd = -1; @@ -149,8 +127,6 @@ void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from) { fsrv_to->init_child_func = from->init_child_func; // Note: do not copy ->add_extra_func - fsrv_to->kill_signal = from->kill_signal; - list_append(&fsrv_list, fsrv_to); } @@ -1162,25 +1138,18 @@ fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, /* Report outcome to caller. */ - /* TODO We use SIGTERM here as an indicator of Xen mode, - although it's not equivalent! */ - if (fsrv->kill_signal == SIGTERM && !*stop_soon_p && - fsrv->last_run_timed_out) { + /* Did we timeout? */ + if (unlikely(fsrv->last_run_timed_out)) { + fsrv->last_kill_signal = fsrv->kill_signal; return FSRV_RUN_TMOUT; } - if (WIFSIGNALED(fsrv->child_status) && !*stop_soon_p) { + /* Did we crash? */ + if (unlikely(WIFSIGNALED(fsrv->child_status) && !*stop_soon_p)) { fsrv->last_kill_signal = WTERMSIG(fsrv->child_status); - - if (fsrv->last_run_timed_out && fsrv->last_kill_signal == SIGKILL) { - - return FSRV_RUN_TMOUT; - - } - return FSRV_RUN_CRASH; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 00625f2e..5c363c63 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -194,10 +194,11 @@ static void usage(u8 *argv0, int more_help) { "AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n" "AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n" "AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n" - "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n" @@ -986,6 +987,33 @@ int main(int argc, char **argv_orig, char **envp) { #endif + afl->fsrv.kill_signal = SIGKILL; + if (afl->afl_env.afl_kill_signal) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl->afl_env.afl_kill_signal, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == (char *)afl->afl_env.afl_kill_signal) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl->afl_env.afl_kill_signal); + + } + + afl->fsrv.kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + setup_signal_handlers(); check_asan_opts(afl); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index d50601fc..2c9c38ed 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -693,12 +693,13 @@ static void usage(u8 *argv0) { "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as " "crash\n" "AFL_DEBUG: enable extra developer output\n" - "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " - "size\n" - " the target was compiled for\n" - "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during " "startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, " + "etc. (default: SIGKILL)\n" + "AFL_MAP_SIZE: the shared memory size for that target. must be >= the " + "size the target was compiled for\n" + "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" "AFL_QUIET: do not print extra informational output\n", argv0, MEM_LIMIT, doc_path); @@ -1115,6 +1116,34 @@ int main(int argc, char **argv_orig, char **envp) { } + fsrv->kill_signal = SIGKILL; + char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + fsrv->kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + if (getenv("AFL_CRASH_EXITCODE")) { long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 09d97f58..342de9c8 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -855,6 +855,7 @@ static void usage(u8 *argv0) { "Environment variables used:\n" "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" + "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" " the target was compiled for\n" "AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n" @@ -1134,6 +1135,34 @@ int main(int argc, char **argv_orig, char **envp) { } + fsrv->kill_signal = SIGKILL; + char *afl_kill_signal_env = getenv("AFL_KILL_SIGNAL"); + if (afl_kill_signal_env && afl_kill_signal_env[0]) { + + char *endptr; + u8 signal_code; + signal_code = (u8)strtoul(afl_kill_signal_env, &endptr, 10); + /* Did we manage to parse the full string? */ + if (*endptr != '\0' || endptr == afl_kill_signal_env) { + + FATAL("Invalid AFL_KILL_SIGNAL: %s (expected unsigned int)", + afl_kill_signal_env); + + } + + fsrv->kill_signal = signal_code; + + } else { + + char *sigstr = alloc_printf("%d", (int)SIGKILL); + if (!sigstr) { FATAL("Failed to alloc mem for signal buf"); } + + /* Set the env for signal handler */ + setenv("AFL_KILL_SIGNAL", sigstr, 1); + free(sigstr); + + } + if (getenv("AFL_CRASH_EXITCODE")) { long exitcode = strtol(getenv("AFL_CRASH_EXITCODE"), NULL, 10); -- cgit 1.4.1 From 53c7aaa57bf5473c5d6a4553c0178410fef17cbc Mon Sep 17 00:00:00 2001 From: murx- Date: Thu, 14 Jan 2021 13:46:03 +0100 Subject: Update documentation to reflect new default power schedule --- README.md | 2 +- docs/power_schedules.md | 4 ++-- docs/status_screen.md | 7 ++++++- src/afl-fuzz.c | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index bea673f9..a5cb6393 100644 --- a/README.md +++ b/README.md @@ -591,7 +591,7 @@ For every secondary fuzzer there should be a variation, e.g.: All other secondaries should be used like this: * A third to a half with the MOpt mutator enabled: `-L 0` * run with a different power schedule, available are: - `explore (default), fast, coe, lin, quad, exploit, mmopt, rare, seek` + `fast (default), explore, coe, lin, quad, exploit, mmopt, rare, seek` which you can set with e.g. `-p seek` You can also use different fuzzers. diff --git a/docs/power_schedules.md b/docs/power_schedules.md index 06fefa12..493f9609 100644 --- a/docs/power_schedules.md +++ b/docs/power_schedules.md @@ -13,8 +13,8 @@ We find that AFL's exploitation-based constant schedule assigns **too much energ | AFL flag | Power Schedule | | ------------- | -------------------------- | -| `-p explore` (default)| ![EXPLORE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D) | -| `-p fast` | ![FAST](http://latex.codecogs.com/gif.latex?p(i)=\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) | +| `-p explore` | ![EXPLORE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D) | +| `-p fast` (default)| ![FAST](http://latex.codecogs.com/gif.latex?p(i)=\\min\\left(\\frac{\\alpha(i)}{\\beta}\\cdot\\frac{2^{s(i)}}{f(i)},M\\right)) | | `-p coe` | ![COE](http://latex.codecogs.com/gif.latex?p%28i%29%3D%5Cbegin%7Bcases%7D%200%20%26%20%5Ctext%7B%20if%20%7D%20f%28i%29%20%3E%20%5Cmu%5C%5C%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%202%5E%7Bs%28i%29%7D%2C%20M%5Cright%29%20%26%20%5Ctext%7B%20otherwise.%7D%20%5Cend%7Bcases%7D) | | `-p quad` | ![QUAD](http://latex.codecogs.com/gif.latex?p%28i%29%20%3D%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%5Cfrac%7Bs%28i%29%5E2%7D%7Bf%28i%29%7D%2CM%5Cright%29) | | `-p lin` | ![LIN](http://latex.codecogs.com/gif.latex?p%28i%29%20%3D%20%5Cmin%5Cleft%28%5Cfrac%7B%5Calpha%28i%29%7D%7B%5Cbeta%7D%5Ccdot%5Cfrac%7Bs%28i%29%7D%7Bf%28i%29%7D%2CM%5Cright%29) | diff --git a/docs/status_screen.md b/docs/status_screen.md index 0cede6ff..0329d960 100644 --- a/docs/status_screen.md +++ b/docs/status_screen.md @@ -29,13 +29,18 @@ With that out of the way, let's talk about what's actually on the screen... ### The status bar +``` +american fuzzy lop ++3.01a (default) [fast] {0} +``` + The top line shows you which mode afl-fuzz is running in (normal: "american fuzy lop", crash exploration mode: "peruvian rabbit mode") and the version of afl++. Next to the version is the banner, which, if not set with -T by hand, will either show the binary name being fuzzed, or the -M/-S main/secondary name for parallel fuzzing. -Finally, the last item is the power schedule mode being run (default: explore). +Second to last is the power schedule mode being run (default: fast). +Finally, the last item is the CPU id. ### Process timing diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 37f8db8a..6a2b28cf 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,8 +103,8 @@ static void usage(u8 *argv0, int more_help) { "Execution control settings:\n" " -p schedule - power schedules compute a seed's performance score:\n" - " -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" -- cgit 1.4.1 From 7ba17d182f7010c616fe9ecf4ba291cd772f52bc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 14 Jan 2021 21:26:46 +0100 Subject: more sync in deterministic mode --- docs/Changelog.md | 2 ++ src/afl-fuzz.c | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 81ac91b0..e0f8e9bf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -17,6 +17,8 @@ sending a mail to . - afl-fuzz - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) + - if determinstic mode is active (-D, or -M without -d) then we sync + after every queue entry as this can take very long time otherwise - switched to a faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 6a2b28cf..bb2674f0 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1781,15 +1781,27 @@ int main(int argc, char **argv_orig, char **envp) { } while (skipped_fuzz && afl->queue_cur && !afl->stop_soon); - if (!afl->stop_soon && afl->sync_id) { + if (likely(!afl->stop_soon && afl->sync_id)) { - if (unlikely(afl->is_main_node)) { + if (likely(afl->skip_deterministic)) { - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { sync_fuzzers(afl); } + if (unlikely(afl->is_main_node)) { + + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + + sync_fuzzers(afl); + + } + + } else { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } } else { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + sync_fuzzers(afl); } -- cgit 1.4.1 From e91f3b0de65376b001d45892cc6bdd2fcafde949 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 19 Jan 2021 10:41:42 +0100 Subject: codeql fix --- docs/Changelog.md | 1 + src/afl-fuzz-run.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e0f8e9bf..60f09ca5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -19,6 +19,7 @@ sending a mail to . for reporting) - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise + - better detection if a target needs a large shared map - switched to a faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index b597488b..17c305ed 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -424,7 +424,7 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem, if (unlikely(afl->fixed_seed)) { - diff_us = (afl->fsrv.exec_tmout - 1) * afl->stage_max; + diff_us = (u64)(afl->fsrv.exec_tmout - 1) * (u64)afl->stage_max; } else { -- cgit 1.4.1 From 1ee0946f692927eaaed9c8db1d006d8eb38631c3 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 12:11:12 +0100 Subject: update changelog --- docs/Changelog.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 60f09ca5..daae7c47 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,22 +11,27 @@ sending a mail to . ### Version ++3.01a (dev) - Mac OS ARM64 support - - New selective instrumentation option with __AFL_COVERAGE_... commands + - Android support fixed and updated by Joey Jiaojg - thanks! + - New selective instrumentation option with __AFL_COVERAGE_* commands to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz + - upgraded cmplog/redqueen: solving for floating point, solving + transformations (e.g. toupper, tolower, to/from hex, xor, + arithmetics, etc.). this is costly hence new command line option + -l that sets the intensity (values 1 to 3). recommended is 1 or 2. - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map - - switched to a faster RNG + - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - - cmplog/redqueen now also tracks floats/doubles + - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang @@ -35,7 +40,7 @@ sending a mail to . already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster - - Added AFL_KILL_SIGNAL env variable for custom targets (thanks @v-p-b) + - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) ### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ -- cgit 1.4.1 From e8c1b43a3de7ae3d173c749c3be4e58a95d3372b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 21 Jan 2021 15:24:34 +0100 Subject: fix docs --- docs/env_variables.md | 2 +- src/afl-fuzz.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 26128b01..baf69e6a 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -519,7 +519,7 @@ The corpus minimization script offers very little customization: a modest security risk on multi-user systems with rogue users, but should be safe on dedicated fuzzing boxes. -# #6) Settings for afl-tmin +## 7) Settings for afl-tmin Virtually nothing to play with. Well, in QEMU mode (`-Q`), `AFL_PATH` will be searched for afl-qemu-trace. In addition to this, `TMPDIR` may be used if a diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 95bc0694..9a8159bd 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -123,7 +123,7 @@ static void usage(u8 *argv0, int more_help) { "it.\n" " if using QEMU, just use -c 0.\n" " -l cmplog_level - set the complexity/intensivity of CmpLog.\n" - " Values: 1 (default), 2 (intensive) and 3 (heavy)\n\n" + " Values: 1 (integer+string), 2 (+FP) and 3 (+transform)\n\n" "Fuzzing behavior settings:\n" " -Z - sequential queue selection instead of weighted " -- cgit 1.4.1 From 0a12d519f7d914f16ead8d31f426f814021ec3dc Mon Sep 17 00:00:00 2001 From: Edznux Date: Fri, 22 Jan 2021 00:18:56 +0100 Subject: Add better doc for AFL_STATSD_TAGS_FLAVOR --- docs/env_variables.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index baf69e6a..66d85749 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -431,13 +431,18 @@ checks or alter some of the more exotic semantics of the tool: normally done when starting up the forkserver and causes a pretty significant performance drop. - - Setting `AFL_STATSD` enable StatsD metrics collection. + - Setting `AFL_STATSD` enables StatsD metrics collection. By default AFL++ will send these metrics over UDP to 127.0.0.1:8125. - The host and port are configurable with `AFL_STATSD_HOST` and `AFL_STATSD_PORT` - respectively. - To get the most out of this, you should provide `AFL_STATSD_TAGS_FLAVOR` that - matches your StatsD server. - Available flavors are `dogstatsd`, `librato`, `signalfx` and `influxdb`. + The host and port are configurable with `AFL_STATSD_HOST` and `AFL_STATSD_PORT` respectively. + To enable tags (banner and afl_version) you should provide `AFL_STATSD_TAGS_FLAVOR` that matches + your StatsD server (see `AFL_STATSD_TAGS_FLAVOR`) + + - Setting `AFL_STATSD_TAGS_FLAVOR` to one of `dogstatsd`, `librato`, `signalfx` or `influxdb` + allows you to add tags to your fuzzing instances. This is especially useful when running + multiple instances (`-M/-S` for example). Applied tags are `banner` and `afl_version`. + `banner` corresponds to the name of the fuzzer provided through `-M/-S`. + `afl_version` corresponds to the currently running afl version (e.g `++3.0c`). + Default (empty/non present) will add no tags to the metrics. - Setting `AFL_CRASH_EXITCODE` sets the exit code afl treats as crash. For example, if `AFL_CRASH_EXITCODE='-1'` is set, each input resulting -- cgit 1.4.1 From ac21e4dd7304c3306a9acb5fa6ac051d3ab64b20 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 22 Jan 2021 00:41:23 +0100 Subject: typos --- README.md | 4 ++-- docs/Changelog.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index ce48f336..2806b734 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ ## Major changes in afl++ 3.0 -With afl++ 3.0 we introduced changes that breaks some previous afl and afl++ +With afl++ 3.0 we introduced changes that break some previous afl and afl++ behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is - only one compiler: afl-cc. All previous compilers now symlink to this. + only one compiler: afl-cc. All previous compilers now symlink to this one. All instrumentation source code is now in the `instrumentation/` folder. * The gcc_plugin was replaced with a new version submitted by AdaCore that supports more features. Thank you! diff --git a/docs/Changelog.md b/docs/Changelog.md index daae7c47..63e8e66e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,7 +35,8 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang - - warn on any _AFL and __AFL env var + - changed default: no memory limit for afl-cmin and afl-cmin.bash + - warn on any _AFL and __AFL env vars - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ -- cgit 1.4.1 From baf1ac2e69145d9e411f13df8ab4e7060f2f2685 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 22 Jan 2021 15:58:12 +0100 Subject: basic cmplog std::string support --- docs/Changelog.md | 2 + instrumentation/afl-compiler-rt.o.c | 4 +- instrumentation/cmplog-routines-pass.cc | 214 ++++++++++++++++++++++++++++++-- 3 files changed, 209 insertions(+), 11 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 63e8e66e..2b8b0e8d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,8 @@ sending a mail to . - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit + - cmplog/redqueen can now process basic libc++ and libstdc++ + std::string comparisons (though no position or length type variants) - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 322141ba..433a1d89 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1549,10 +1549,10 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { u32 i; if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; fprintf(stderr, "rtn arg0="); - for (i = 0; i < 8; i++) + for (i = 0; i < 24; i++) fprintf(stderr, "%02x", ptr1[i]); fprintf(stderr, " arg1="); - for (i = 0; i < 8; i++) + for (i = 0; i < 24; i++) fprintf(stderr, "%02x", ptr2[i]); fprintf(stderr, "\n"); */ diff --git a/instrumentation/cmplog-routines-pass.cc b/instrumentation/cmplog-routines-pass.cc index 8adf42d5..9d6999ca 100644 --- a/instrumentation/cmplog-routines-pass.cc +++ b/instrumentation/cmplog-routines-pass.cc @@ -87,7 +87,7 @@ char CmpLogRoutines::ID = 0; bool CmpLogRoutines::hookRtns(Module &M) { - std::vector calls; + std::vector calls, llvmStdStd, llvmStdC, gccStdStd, gccStdC; LLVMContext & C = M.getContext(); Type *VoidTy = Type::getVoidTy(C); @@ -112,6 +112,78 @@ bool CmpLogRoutines::hookRtns(Module &M) { FunctionCallee cmplogHookFn = c; #endif +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c1 = M.getOrInsertFunction("__cmplog_rtn_llvm_stdstring_stdstring", + VoidTy, i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogLlvmStdStd = cast(c1); +#else + FunctionCallee cmplogLlvmStdStd = c1; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c2 = M.getOrInsertFunction("__cmplog_rtn_llvm_stdstring_cstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogLlvmStdC = cast(c2); +#else + FunctionCallee cmplogLlvmStdC = c2; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c3 = M.getOrInsertFunction("__cmplog_rtn_gcc_stdstring_stdstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogGccStdStd = cast(c3); +#else + FunctionCallee cmplogGccStdStd = c3; +#endif + +#if LLVM_VERSION_MAJOR < 9 + Constant * +#else + FunctionCallee +#endif + c4 = M.getOrInsertFunction("__cmplog_rtn_gcc_stdstring_cstring", VoidTy, + i8PtrTy, i8PtrTy +#if LLVM_VERSION_MAJOR < 5 + , + NULL +#endif + ); +#if LLVM_VERSION_MAJOR < 9 + Function *cmplogGccStdC = cast(c4); +#else + FunctionCallee cmplogGccStdC = c4; +#endif + /* iterate over all functions, bbs and instruction and add suitable calls */ for (auto &F : M) { @@ -131,19 +203,67 @@ bool CmpLogRoutines::hookRtns(Module &M) { FunctionType *FT = Callee->getFunctionType(); - // _ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7compareEmmPKcm - // => libc++ => llvm => __cmplog_rtn_llvm_stdstring_cstring(u8 *stdstring1, u8 *stdstring2) - // _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc - // => libstdc++ => gcc => __cmplog_rtn_gcc_stdstring_cstring - bool isPtrRtn = FT->getNumParams() >= 2 && !FT->getReturnType()->isVoidTy() && FT->getParamType(0) == FT->getParamType(1) && FT->getParamType(0)->isPointerTy(); - if (!isPtrRtn) continue; - - calls.push_back(callInst); + bool isGccStdStringStdString = + Callee->getName().find("__is_charIT_EE7__value") != + std::string::npos && + Callee->getName().find( + "St7__cxx1112basic_stringIS2_St11char_traits") != + std::string::npos && + FT->getNumParams() >= 2 && + FT->getParamType(0) == FT->getParamType(1) && + FT->getParamType(0)->isPointerTy(); + + bool isGccStdStringCString = + Callee->getName().find( + "St7__cxx1112basic_stringIcSt11char_" + "traitsIcESaIcEE7compareEPK") != std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + bool isLlvmStdStringStdString = + Callee->getName().find("_ZNSt3__1eqINS") != std::string::npos && + Callee->getName().find("_12basic_stringIcNS_11char_traits") != + std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + bool isLlvmStdStringCString = + Callee->getName().find("ZNSt3__1eqIcNS") != std::string::npos && + Callee->getName().find("_12basic_stringI") != std::string::npos && + FT->getNumParams() >= 2 && FT->getParamType(0)->isPointerTy() && + FT->getParamType(1)->isPointerTy(); + + /* + fprintf(stderr, "F:%s C:%s argc:%u\n", + F.getName().str().c_str(), Callee->getName().str().c_str(), + FT->getNumParams()); fprintf(stderr, "ptr0:%u ptr1:%u ptr2:%u\n", + FT->getParamType(0)->isPointerTy(), + FT->getParamType(1)->isPointerTy(), + FT->getNumParams() > 2 ? FT->getParamType(2)->isPointerTy() + : 22 ); + */ + + if (isGccStdStringCString || isGccStdStringStdString || + isLlvmStdStringStdString || isLlvmStdStringCString) { + + isPtrRtn = false; + + fprintf(stderr, "%u %u %u %u\n", isGccStdStringCString, + isGccStdStringStdString, isLlvmStdStringStdString, + isLlvmStdStringCString); + + } + + if (isPtrRtn) { calls.push_back(callInst); } + if (isGccStdStringStdString) { gccStdStd.push_back(callInst); } + if (isGccStdStringCString) { gccStdC.push_back(callInst); } + if (isLlvmStdStringStdString) { llvmStdStd.push_back(callInst); } + if (isLlvmStdStringCString) { llvmStdC.push_back(callInst); } } @@ -179,6 +299,82 @@ bool CmpLogRoutines::hookRtns(Module &M) { } + for (auto &callInst : gccStdStd) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogGccStdStd, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : gccStdC) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogGccStdC, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : llvmStdStd) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogLlvmStdStd, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + + for (auto &callInst : llvmStdC) { + + Value *v1P = callInst->getArgOperand(0), *v2P = callInst->getArgOperand(1); + + IRBuilder<> IRB(callInst->getParent()); + IRB.SetInsertPoint(callInst); + + std::vector args; + Value * v1Pcasted = IRB.CreatePointerCast(v1P, i8PtrTy); + Value * v2Pcasted = IRB.CreatePointerCast(v2P, i8PtrTy); + args.push_back(v1Pcasted); + args.push_back(v2Pcasted); + + IRB.CreateCall(cmplogLlvmStdC, args); + + // errs() << callInst->getCalledFunction()->getName() << "\n"; + + } + return true; } -- cgit 1.4.1 From b0a8bc28d2465292c5d1aff2e1d87ffeb65a2324 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 23 Jan 2021 00:13:32 +0100 Subject: changelog --- docs/Changelog.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 63e8e66e..97f816b6 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,9 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang + - unicornafl + - Substential speed gains in python bindings for certain use cases + - Added a new example harness to compare python, c, and rust bindings - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was -- cgit 1.4.1 From afc15965c00b6b2646902d8073cf9c925862b853 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 25 Jan 2021 02:01:34 +0100 Subject: updated unicornalf, bindings --- docs/Changelog.md | 1 + unicorn_mode/UNICORNAFL_VERSION | 2 +- unicorn_mode/samples/speedtest/rust/src/main.rs | 5 +++-- unicorn_mode/unicornafl | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 062dd785..12f81571 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -39,6 +39,7 @@ sending a mail to . CLANG for old afl-clang - unicornafl - Substential speed gains in python bindings for certain use cases + - Improved rust bindings - Added a new example harness to compare python, c, and rust bindings - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION index f1fb7f18..a02531ec 100644 --- a/unicorn_mode/UNICORNAFL_VERSION +++ b/unicorn_mode/UNICORNAFL_VERSION @@ -1 +1 @@ -0dd17c58 +2a54500c diff --git a/unicorn_mode/samples/speedtest/rust/src/main.rs b/unicorn_mode/samples/speedtest/rust/src/main.rs index 8e31d2e2..1e35ff0b 100644 --- a/unicorn_mode/samples/speedtest/rust/src/main.rs +++ b/unicorn_mode/samples/speedtest/rust/src/main.rs @@ -194,18 +194,19 @@ fn fuzz(input_file: &str) -> Result<(), uc_error> { } let place_input_callback = - |mut uc: UnicornHandle<'_, _>, afl_input: &[u8], _persistent_round| { + |mut uc: UnicornHandle<'_, _>, afl_input: &mut [u8], _persistent_round| { // apply constraints to the mutated input if afl_input.len() > INPUT_MAX as usize { //println!("Skipping testcase with leng {}", afl_input.len()); return false; } - // TODO: afl_input[-1] = b'\0' + afl_input[afl_input.len() - 1] = b'\0'; uc.mem_write(INPUT_ADDRESS, afl_input).unwrap(); true }; + // return true if the last run should be counted as crash let crash_validation_callback = |_uc: UnicornHandle<'_, _>, result, _input: &[u8], _persistent_round| { result != uc_error::OK diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index 0dd17c58..2a54500c 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit 0dd17c58d51ed6dc69a367adbe8d2dca4d224c4d +Subproject commit 2a54500c08758d29caacd1ccf47d034d9faa4a18 -- cgit 1.4.1 From 52af7caf8af97c170f13a2c6a9131fccf3a417b9 Mon Sep 17 00:00:00 2001 From: Edouard SCHWEISGUTH Date: Mon, 25 Jan 2021 02:07:51 +0100 Subject: Add StatsD readme (#702) * Adding draft notes for statsd readme * Add statsd dashboard json template & image. --- docs/rpc_statsd.md | 143 +++ docs/statsd/grafana-afl++.json | 1816 +++++++++++++++++++++++++++++++++ docs/visualization/StatsD-grafana.png | Bin 0 -> 163646 bytes 3 files changed, 1959 insertions(+) create mode 100644 docs/rpc_statsd.md create mode 100644 docs/statsd/grafana-afl++.json create mode 100644 docs/visualization/StatsD-grafana.png (limited to 'docs') diff --git a/docs/rpc_statsd.md b/docs/rpc_statsd.md new file mode 100644 index 00000000..26544ff5 --- /dev/null +++ b/docs/rpc_statsd.md @@ -0,0 +1,143 @@ +# Remote monitoring with StatsD + +StatsD allows you to receive and aggregate metrics from a wide range of application and retransmit them to the backend of your choice. +This enables you to create nice and readable dashboards containing all the information you need on your fuzzer instances. +No need to write your own statistics parsing system, deploy and maintain it to all your instances, sync with your graph rendering system... + +The available metrics are : +- cycle_done +- cycles_wo_finds +- execs_done +- execs_per_sec +- paths_total +- paths_favored +- paths_found +- paths_imported +- max_depth +- cur_path +- pending_favs +- pending_total +- variable_paths +- unique_crashes +- unique_hangs +- total_crashes +- slowest_exec_ms +- edges_found +- var_byte_count +- havoc_expansion + +Compared to the default integrated UI, these metrics give you the opportunity to visualize trends and fuzzing state over time. +By doing so, you might be able to see when the fuzzing process has reached a state of no progress, visualize what are the "best strategies" +(according to your own criteria) for your targets, etc. And doing so without requiring to log into each instance manually. + +An example visualisation may look like the following: +![StatsD Grafana](./visualization/StatsD-grafana.png) + +*Notes: The exact same dashboard can be imported with [this JSON template](./statsd/grafana-afl++.json).* + +## How to use + +To enable the StatsD reporting on your fuzzer instances, you need to set the environment variable `AFL_STATSD=1`. + +Setting `AFL_STATSD_TAGS_FLAVOR` to the provider of your choice will assign tags / labels to each metric based on their format. +The possible values are `dogstatsd`, `librato`, `signalfx` or `influxdb`. +For more information on these env vars, check out `docs/env_variables.md`. + +The simplest way of using this feature is to use any metric provider and change the host/port of your StatsD daemon, +with `AFL_STATSD_HOST` and `AFL_STATSD_PORT`, if required (defaults are `localhost` and port `8125`). +To get started, here are some instruction with free and open source tools. +The following setup is based on Prometheus, statsd_exporter and Grafana. +Grafana here is not mandatory, but gives you some nice graphs and features. + +Depending on your setup and infrastructure, you may want to run these applications not on your fuzzer instances. +Only one instance of these 3 application is required for all your fuzzers. + +To simplify everything, we will use Docker and docker-compose. +Make sure you have them both installed. On most common Linux distributions, it's as simple as: + +```sh +curl -fsSL https://get.docker.com -o get-docker.sh +sh get-docker.sh +``` + +Once that's done, we can create the infrastructure. +Create and move into the directory of your choice. This will store all the configurations files required. + +First, create a `docker-compose.yml` containing the following: +```yml +version: '3' + +networks: + statsd-net: + driver: bridge + +services: + prometheus: + image: prom/prometheus + container_name: prometheus + volumes: + - ./prometheus.yml:/prometheus.yml + command: + - '--config.file=/prometheus.yml' + restart: unless-stopped + ports: + - "9090:9090" + networks: + - statsd-net + + statsd_exporter: + image: prom/statsd-exporter + container_name: statsd_exporter + volumes: + - ./statsd_mapping.yml:/statsd_mapping.yml + command: + - "--statsd.mapping-config=/statsd_mapping.yml" + ports: + - "9102:9102/tcp" + - "8125:9125/udp" + networks: + - statsd-net + + grafana: + image: grafana/grafana + container_name: grafana + restart: unless-stopped + ports: + - "3000:3000" + networks: + - statsd-net +``` + +Then `prometheus.yml` +```yml +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'fuzzing_metrics' + static_configs: + - targets: ['statsd_exporter:9102'] +``` + +And finally `statsd_mapping.yml` +```yml +mappings: +- match: "fuzzing.*" + name: "fuzzing" + labels: + type: "$1" +``` + +Run `docker-compose up -d`. + +Everything should be now setup, you are now able to run your fuzzers with + +``` +AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -M test-fuzzer-1 -i i -o o ./bin/my-application @@ +AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -S test-fuzzer-2 -i i -o o ./bin/my-application @@ +... +``` + +This setup may be modified before use in production environment. Depending on your needs: addind passwords, creating volumes for storage, +tweaking the metrics gathering to get host metrics (CPU, RAM ...). \ No newline at end of file diff --git a/docs/statsd/grafana-afl++.json b/docs/statsd/grafana-afl++.json new file mode 100644 index 00000000..96e824de --- /dev/null +++ b/docs/statsd/grafana-afl++.json @@ -0,0 +1,1816 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 16, + "title": "Row title", + "type": "row" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 500 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Slow exec per sec", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"execs_per_sec\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 500 + } + ], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Exec/s", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 10, + "y": 1 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"total_crashes\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total Crashes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 4, + "x": 20, + "y": 1 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"var_byte_count\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Var Byte Count", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 0, + "y": 7 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"unique_crashes\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Unique Crashes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 10, + "x": 10, + "y": 7 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"unique_hangs\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Unique Hangs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 13 + }, + "hiddenSeries": false, + "id": 23, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"slowest_exec_ms\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Slowest Exec Ms", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 13 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"cycle_done\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Cycles dones", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 10, + "y": 13 + }, + "hiddenSeries": false, + "id": 13, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"execs_done\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Total Execs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 15, + "y": 13 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"cur_path\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Curent path", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"cycles_wo_finds\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Cycles done without find", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 18 + }, + "hiddenSeries": false, + "id": 25, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"paths_favored\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Path Favored", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 10, + "y": 18 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"havoc_expansion\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Havoc Expansion", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 15, + "y": 18 + }, + "hiddenSeries": false, + "id": 17, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"edges_found\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Edges Found", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 23 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"paths_imported\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Path Imported", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 23 + }, + "hiddenSeries": false, + "id": 21, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"pending_total\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Pending Total", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 10, + "y": 23 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"pending_favs\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Pending favs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 5, + "w": 5, + "x": 15, + "y": 23 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.7", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "fuzzing{type=\"max_depth\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [ + { + "colorMode": "background6", + "fill": true, + "fillColor": "rgba(234, 112, 112, 0.12)", + "line": false, + "lineColor": "rgba(237, 46, 24, 0.60)", + "op": "time" + } + ], + "timeShift": null, + "title": "Max Depth", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": false, + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Fuzzing", + "uid": "sRI6PCfGz", + "version": 2 +} \ No newline at end of file diff --git a/docs/visualization/StatsD-grafana.png b/docs/visualization/StatsD-grafana.png new file mode 100644 index 00000000..1bdc1722 Binary files /dev/null and b/docs/visualization/StatsD-grafana.png differ -- cgit 1.4.1 From 9a7531942dd02797c69dd23ee8e13f504a8a30a7 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 25 Jan 2021 10:04:21 +0100 Subject: fix rpc_stats.md --- docs/Changelog.md | 2 ++ docs/rpc_statsd.md | 6 +++--- docs/visualization/StatsD-grafana.png | Bin 163646 -> 0 bytes docs/visualization/statsd-grafana.png | Bin 0 -> 163646 bytes 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 docs/visualization/StatsD-grafana.png create mode 100644 docs/visualization/statsd-grafana.png (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 12f81571..5c6b0663 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -48,6 +48,8 @@ sending a mail to . - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) + - @Edznux added a nice documentation on how to use rpc.statsd with + afl++ in docs/rpc_statsd.md, thanks! ### Version ++3.00c (release) - llvm_mode/ and gcc_plugin/ moved to instrumentation/ diff --git a/docs/rpc_statsd.md b/docs/rpc_statsd.md index 26544ff5..02f72be6 100644 --- a/docs/rpc_statsd.md +++ b/docs/rpc_statsd.md @@ -31,9 +31,9 @@ By doing so, you might be able to see when the fuzzing process has reached a sta (according to your own criteria) for your targets, etc. And doing so without requiring to log into each instance manually. An example visualisation may look like the following: -![StatsD Grafana](./visualization/StatsD-grafana.png) +![StatsD Grafana](visualization/statsd-grafana.png) -*Notes: The exact same dashboard can be imported with [this JSON template](./statsd/grafana-afl++.json).* +*Notes: The exact same dashboard can be imported with [this JSON template](statsd/grafana-afl++.json).* ## How to use @@ -140,4 +140,4 @@ AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -S test-fuzzer-2 -i i -o ``` This setup may be modified before use in production environment. Depending on your needs: addind passwords, creating volumes for storage, -tweaking the metrics gathering to get host metrics (CPU, RAM ...). \ No newline at end of file +tweaking the metrics gathering to get host metrics (CPU, RAM ...). diff --git a/docs/visualization/StatsD-grafana.png b/docs/visualization/StatsD-grafana.png deleted file mode 100644 index 1bdc1722..00000000 Binary files a/docs/visualization/StatsD-grafana.png and /dev/null differ diff --git a/docs/visualization/statsd-grafana.png b/docs/visualization/statsd-grafana.png new file mode 100644 index 00000000..1bdc1722 Binary files /dev/null and b/docs/visualization/statsd-grafana.png differ -- cgit 1.4.1 From aaec45b6528e41a217de95ca3db1173fb2539672 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 29 Jan 2021 18:51:44 +0100 Subject: changelog --- docs/Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 5c6b0663..dd160b14 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,11 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang + - qemuafl + - solved some persistent mode bugs (thanks Dil4rd) + - solved an issue when dumping the memory maps (thanks wizche) + - ported QASan to qemuafl + - ported the QASan runtime adding support for Android - unicornafl - Substential speed gains in python bindings for certain use cases - Improved rust bindings -- cgit 1.4.1 From 2f96f1e9204f60d0a1b91a01f5da34b64b29cf9b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 30 Jan 2021 10:28:34 +0100 Subject: afl-frida faster for x86_x64 --- docs/Changelog.md | 6 ++-- utils/afl_frida/afl-frida.c | 87 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index dd160b14..329b7520 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -38,10 +38,10 @@ sending a mail to . support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang - qemuafl + - ported QASan to qemuafl! see qemu_mode/libqasan/README.md - solved some persistent mode bugs (thanks Dil4rd) - solved an issue when dumping the memory maps (thanks wizche) - - ported QASan to qemuafl - - ported the QASan runtime adding support for Android + - Android support for QASan - unicornafl - Substential speed gains in python bindings for certain use cases - Improved rust bindings @@ -51,7 +51,7 @@ sending a mail to . - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ - - Updated utils/afl_frida to be 5% faster + - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) - @Edznux added a nice documentation on how to use rpc.statsd with afl++ in docs/rpc_statsd.md, thanks! diff --git a/utils/afl_frida/afl-frida.c b/utils/afl_frida/afl-frida.c index 85cf2e9d..711d8f33 100644 --- a/utils/afl_frida/afl-frida.c +++ b/utils/afl_frida/afl-frida.c @@ -94,6 +94,8 @@ typedef struct { GumAddress base_address; guint64 code_start, code_end; + GumAddress current_log_impl; + uint64_t afl_prev_loc; } range_t; @@ -109,12 +111,58 @@ inline static void afl_maybe_log(guint64 current_pc) { } +#if GUM_NATIVE_CPU == GUM_CPU_AMD64 + +static const guint8 afl_maybe_log_code[] = { + + 0x9c, // pushfq + 0x50, // push rax + 0x51, // push rcx + 0x52, // push rdx + 0x56, // push rsi + + 0x89, 0xf8, // mov eax, edi + 0xc1, 0xe0, 0x08, // shl eax, 8 + 0xc1, 0xef, 0x04, // shr edi, 4 + 0x31, 0xc7, // xor edi, eax + 0x0f, 0xb7, 0xc7, // movzx eax, di + 0x48, 0x8d, 0x0d, 0x30, 0x00, 0x00, 0x00, // lea rcx, sym._afl_area_ptr_ptr + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8b, 0x09, // mov rcx, qword [rcx] + 0x48, 0x8d, 0x15, 0x1b, 0x00, 0x00, 0x00, // lea rdx, sym._afl_prev_loc_ptr + 0x48, 0x8b, 0x32, // mov rsi, qword [rdx] + 0x48, 0x8b, 0x36, // mov rsi, qword [rsi] + 0x48, 0x31, 0xc6, // xor rsi, rax + 0xfe, 0x04, 0x31, // inc byte [rcx + rsi] + + 0x48, 0xd1, 0xe8, // shr rax, 1 + 0x48, 0x8b, 0x0a, // mov rcx, qword [rdx] + 0x48, 0x89, 0x01, // mov qword [rcx], rax + + 0x5e, // pop rsi + 0x5a, // pop rdx + 0x59, // pop rcx + 0x58, // pop rax + 0x9d, // popfq + + 0xc3, // ret + // Read-only data goes here: + // uint64_t* afl_prev_loc_ptr + // uint8_t** afl_area_ptr_ptr + // unsigned int afl_instr_rms + +}; + +#else + static void on_basic_block(GumCpuContext *context, gpointer user_data) { afl_maybe_log((guint64)user_data); } +#endif + void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, gpointer user_data) { @@ -129,8 +177,45 @@ void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, if (instr->address >= range->code_start && instr->address <= range->code_end) { +#if GUM_NATIVE_CPU == GUM_CPU_AMD64 + GumX86Writer *cw = output->writer.x86; + if (range->current_log_impl == 0 || + !gum_x86_writer_can_branch_directly_between( + cw->pc, range->current_log_impl) || + !gum_x86_writer_can_branch_directly_between( + cw->pc + 128, range->current_log_impl)) { + + gconstpointer after_log_impl = cw->code + 1; + + gum_x86_writer_put_jmp_near_label(cw, after_log_impl); + + range->current_log_impl = cw->pc; + gum_x86_writer_put_bytes(cw, afl_maybe_log_code, + sizeof(afl_maybe_log_code)); + + uint64_t *afl_prev_loc_ptr = &range->afl_prev_loc; + uint8_t **afl_area_ptr_ptr = &__afl_area_ptr; + gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, + sizeof(afl_prev_loc_ptr)); + gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr, + sizeof(afl_area_ptr_ptr)); + gum_x86_writer_put_label(cw, after_log_impl); + + } + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -GUM_RED_ZONE_SIZE); + gum_x86_writer_put_push_reg(cw, GUM_REG_RDI); + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, + GUM_ADDRESS(instr->address)); + gum_x86_writer_put_call_address(cw, range->current_log_impl); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + GUM_RED_ZONE_SIZE); +#else gum_stalker_iterator_put_callout(iterator, on_basic_block, (gpointer)instr->address, NULL); +#endif begin = FALSE; } @@ -228,7 +313,7 @@ int main(int argc, char **argv) { guint64 code_start = code_range.base_address; guint64 code_end = code_range.base_address + code_range.size; - range_t instr_range = {0, code_start, code_end}; + range_t instr_range = {0, code_start, code_end, 0, 0}; printf("Frida instrumentation: base=0x%lx instrumenting=0x%lx-%lx\n", base_address, code_start, code_end); -- cgit 1.4.1 From 9d08f0d098c91e69b5fe41674e4c5d05363af604 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 30 Jan 2021 15:39:47 +0100 Subject: added AFL_CMPLOG_ONLY_NEW feature --- docs/Changelog.md | 2 + docs/env_variables.md | 5 ++ include/afl-fuzz.h | 2 +- include/common.h | 2 +- include/envs.h | 1 + src/afl-analyze.c | 22 ++++---- src/afl-common.c | 6 +-- src/afl-fuzz-init.c | 3 ++ src/afl-fuzz-one.c | 5 +- src/afl-fuzz-redqueen.c | 135 +++++++++++++++++++++++++----------------------- src/afl-fuzz-state.c | 7 +++ src/afl-fuzz.c | 35 +++++++------ src/afl-showmap.c | 24 ++++----- src/afl-tmin.c | 24 ++++----- 14 files changed, 147 insertions(+), 126 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 329b7520..6e59961b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,6 +20,8 @@ sending a mail to . transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). this is costly hence new command line option -l that sets the intensity (values 1 to 3). recommended is 1 or 2. + - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from + `-i` or resumes (as these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - if determinstic mode is active (-D, or -M without -d) then we sync diff --git a/docs/env_variables.md b/docs/env_variables.md index 66d85749..4c3b1cfb 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -287,6 +287,11 @@ checks or alter some of the more exotic semantics of the tool: the target. This must be equal or larger than the size the target was compiled with. + - `AFL_CMPLOG_ONLY_NEW` will only perform the expensive cmplog feature for + newly found testcases and not for testcases that are loaded on startup + (`-i in`). This is an important feature to set when resuming a fuzzing + session. + - `AFL_TESTCACHE_SIZE` allows you to override the size of `#define TESTCASE_CACHE` in config.h. Recommended values are 50-250MB - or more if your fuzzing finds a huge amount of paths for large inputs. diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 12db9e4d..e8a21cb5 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -384,7 +384,7 @@ typedef struct afl_env_vars { afl_dumb_forksrv, afl_import_first, afl_custom_mutator_only, afl_no_ui, afl_force_ui, afl_i_dont_care_about_missing_crashes, afl_bench_just_one, afl_bench_until_crash, afl_debug_child, afl_autoresume, afl_cal_fast, - afl_cycle_schedules, afl_expand_havoc, afl_statsd; + afl_cycle_schedules, afl_expand_havoc, afl_statsd, afl_cmplog_only_new; u8 *afl_tmpdir, *afl_custom_mutator_library, *afl_python_module, *afl_path, *afl_hang_tmout, *afl_forksrv_init_tmout, *afl_skip_crashes, *afl_preload, diff --git a/include/common.h b/include/common.h index bdaa1735..bb8831f2 100644 --- a/include/common.h +++ b/include/common.h @@ -47,7 +47,7 @@ void argv_cpy_free(char **argv); char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv); char * get_afl_env(char *env); -u8 *get_libqasan_path(u8 *own_loc); +u8 * get_libqasan_path(u8 *own_loc); extern u8 be_quiet; extern u8 *doc_path; /* path to documentation dir */ diff --git a/include/envs.h b/include/envs.h index 926c9e27..210b34a6 100644 --- a/include/envs.h +++ b/include/envs.h @@ -28,6 +28,7 @@ static char *afl_environment_variables[] = { "AFL_CC", "AFL_CMIN_ALLOW_ANY", "AFL_CMIN_CRASHES_ONLY", + "AFL_CMPLOG_ONLY_NEW", "AFL_CODE_END", "AFL_CODE_START", "AFL_COMPCOV_BINNAME", diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 28598ba0..20aef2da 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -1079,28 +1079,28 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file) { usage(argv[0]); } if (qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } map_size = get_map_size(); diff --git a/src/afl-common.c b/src/afl-common.c index a69f2e97..235c4c05 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -364,11 +364,7 @@ u8 *get_libqasan_path(u8 *own_loc) { cp = alloc_printf("%s/libqasan.so", own_copy); ck_free(own_copy); - if (!access(cp, X_OK)) { - - return cp; - - } + if (!access(cp, X_OK)) { return cp; } } else { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 1808f0a1..2a7864f9 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -25,6 +25,7 @@ #include "afl-fuzz.h" #include +#include "cmplog.h" #ifdef HAVE_AFFINITY @@ -833,6 +834,8 @@ void perform_dry_run(afl_state_t *afl) { } + if (afl->afl_env.afl_cmplog_only_new) { q->colorized = CMPLOG_LVL_MAX; } + u8 *fn = strrchr(q->fname, '/') + 1; ACTF("Attempting dry run with '%s'...", fn); diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index ff766158..0cf889a8 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -26,6 +26,7 @@ #include "afl-fuzz.h" #include #include +#include "cmplog.h" /* MOpt */ @@ -553,7 +554,7 @@ u8 fuzz_one_original(afl_state_t *afl) { if (unlikely(len < 4)) { - afl->queue_cur->colorized = 0xff; + afl->queue_cur->colorized = CMPLOG_LVL_MAX; } else { @@ -2981,7 +2982,7 @@ static u8 mopt_common_fuzzing(afl_state_t *afl, MOpt_globals_t MOpt_globals) { if (unlikely(len < 4)) { - afl->queue_cur->colorized = 0xff; + afl->queue_cur->colorized = CMPLOG_LVL_MAX; } else { diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index fc620781..d7657c1d 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1118,7 +1118,11 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, #ifdef ARITHMETIC_LESSER_GREATER if (lvl < LVL3 || attr == IS_TRANSFORM) { return 0; } - if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { return 0; } + if (!(attr & (IS_GREATER | IS_LESSER)) || SHAPE_BYTES(h->shape) < 4) { + + return 0; + + } // transform >= to < and <= to > if ((attr & IS_EQUAL) && (attr & (IS_GREATER | IS_LESSER))) { @@ -1138,110 +1142,110 @@ static u8 cmp_extend_encoding(afl_state_t *afl, struct cmp_header *h, // lesser/greater FP comparison if (attr >= IS_FP && attr < IS_FP_MOD) { - u64 repl_new; - - if (attr & IS_GREATER) { + u64 repl_new; - if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { + if (attr & IS_GREATER) { - float *f = (float *)&repl; - float g = *f; - g += 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + if (SHAPE_BYTES(h->shape) == 4 && its_len >= 4) { - } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { + float *f = (float *)&repl; + float g = *f; + g += 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - double *f = (double *)&repl; - double g = *f; - g += 1.0; + } else if (SHAPE_BYTES(h->shape) == 8 && its_len >= 8) { - u64 *r = (u64 *)&g; - repl_new = *r; + double *f = (double *)&repl; + double g = *f; + g += 1.0; - } else { + u64 *r = (u64 *)&g; + repl_new = *r; - return 0; + } else { - } + return 0; - changed_val = repl_new; + } - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + changed_val = repl_new; - return 1; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - } + return 1; - } else { + } - if (SHAPE_BYTES(h->shape) == 4) { + } else { - float *f = (float *)&repl; - float g = *f; - g -= 1.0; - u32 *r = (u32 *)&g; - repl_new = (u32)*r; + if (SHAPE_BYTES(h->shape) == 4) { - } else if (SHAPE_BYTES(h->shape) == 8) { + float *f = (float *)&repl; + float g = *f; + g -= 1.0; + u32 *r = (u32 *)&g; + repl_new = (u32)*r; - double *f = (double *)&repl; - double g = *f; - g -= 1.0; - u64 *r = (u64 *)&g; - repl_new = *r; + } else if (SHAPE_BYTES(h->shape) == 8) { - } else { + double *f = (double *)&repl; + double g = *f; + g -= 1.0; + u64 *r = (u64 *)&g; + repl_new = *r; - return 0; + } else { - } + return 0; - changed_val = repl_new; + } - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + changed_val = repl_new; - return 1; + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { - } + return 1; } - // transform double to float, llvm likes to do that internally ... - if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + } - double *f = (double *)&repl; - float g = (float)*f; - repl_new = 0; + // transform double to float, llvm likes to do that internally ... + if (SHAPE_BYTES(h->shape) == 8 && its_len >= 4) { + + double *f = (double *)&repl; + float g = (float)*f; + repl_new = 0; #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) - memcpy((char *)&repl_new, (char *)&g, 4); + memcpy((char *)&repl_new, (char *)&g, 4); #else - memcpy(((char *)&repl_new) + 4, (char *)&g, 4); + memcpy(((char *)&repl_new) + 4, (char *)&g, 4); #endif - changed_val = repl_new; - h->shape = 3; // modify shape - - // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); + changed_val = repl_new; + h->shape = 3; // modify shape - if (unlikely(cmp_extend_encoding( - afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, - taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { + // fprintf(stderr, "DOUBLE2FLOAT %llx\n", repl_new); - h->shape = 7; // recover shape - return 1; - - } + if (unlikely(cmp_extend_encoding( + afl, h, pattern, repl_new, o_pattern, changed_val, 16, idx, + taint_len, orig_buf, buf, cbuf, len, 1, lvl, status))) { h->shape = 7; // recover shape + return 1; } + h->shape = 7; // recover shape + } + } + else if (attr < IS_FP) { // lesser/greater integer comparison @@ -1707,6 +1711,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, try_to_add_to_dictN(afl, s128_v1, SHAPE_BYTES(h->shape)); } else + #endif { diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 8423a3d1..5040e3ef 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -236,6 +236,13 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl->afl_env.afl_custom_mutator_only = get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_CMPLOG_ONLY_NEW", + + afl_environment_variable_len)) { + + afl->afl_env.afl_cmplog_only_new = + get_afl_env(afl_environment_variables[i]) ? 1 : 0; + } else if (!strncmp(env, "AFL_NO_UI", afl_environment_variable_len)) { afl->afl_env.afl_no_ui = diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 312d9424..9d9b0434 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -181,6 +181,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n" "AFL_BENCH_JUST_ONE: run the target just once\n" "AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n" + "AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)\n" "AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n" "AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n" "AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n" @@ -326,8 +327,9 @@ int main(int argc, char **argv_orig, char **envp) { "compile time)"); } + #endif - + char **argv = argv_cpy_dup(argc, argv_orig); afl_state_t *afl = calloc(1, sizeof(afl_state_t)); @@ -356,8 +358,7 @@ int main(int argc, char **argv_orig, char **envp) { while ((opt = getopt( argc, argv, - "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > - 0) { + "+b:B:c:CdDe:E:hi:I:f:F:l:L:m:M:nNo:p:RQs:S:t:T:UV:Wx:Z")) > 0) { switch (opt) { @@ -984,31 +985,31 @@ int main(int argc, char **argv_orig, char **envp) { usage(argv[0], show_help); } - + if (afl->fsrv.qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + afl->afl_env.afl_preload = (u8 *)getenv("AFL_PRELOAD"); ck_free(libqasan); - + } if (afl->fsrv.mem_limit && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260; @@ -1270,7 +1271,7 @@ int main(int argc, char **argv_orig, char **envp) { "instead of using AFL_PRELOAD?"); } - + if (afl->afl_env.afl_preload) { if (afl->fsrv.qemu_mode) { @@ -1322,7 +1323,7 @@ int main(int argc, char **argv_orig, char **envp) { FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); } - + save_cmdline(afl, argc, argv); fix_up_banner(afl, argv[optind]); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index f3cd5a90..62bf1021 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -942,30 +942,30 @@ int main(int argc, char **argv_orig, char **envp) { } if (optind == argc || !out_file) { usage(argv[0]); } - + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } if (in_dir) { diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 9e9e2d63..09b5211d 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -1074,30 +1074,30 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); - + if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { - - u8* preload = getenv("AFL_PRELOAD"); - u8* libqasan = get_libqasan_path(argv_orig[0]); - + + u8 *preload = getenv("AFL_PRELOAD"); + u8 *libqasan = get_libqasan_path(argv_orig[0]); + if (!preload) { - + setenv("AFL_PRELOAD", libqasan, 0); - + } else { - + u8 *result = ck_alloc(strlen(libqasan) + strlen(preload) + 2); strcpy(result, libqasan); strcat(result, " "); strcat(result, preload); - + setenv("AFL_PRELOAD", result, 1); ck_free(result); - + } - + ck_free(libqasan); - + } /* initialize cmplog_mode */ -- cgit 1.4.1 From 893cd47d9cdbfa44e43d03e7d40a56a0c2ad7936 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 13:03:00 +0100 Subject: disable trimming for -M --- README.md | 1 + docs/ideas.md | 15 ++------------- src/afl-fuzz.c | 3 ++- 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 2806b734..8c4aab93 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ behaviours and defaults: * a caching of testcases can now be performed and can be modified by editing config.h for TESTCASE_CACHE or by specifying the env variable `AFL_TESTCACHE_SIZE` (in MB). Good values are between 50-500 (default: 50). + * -M mains do not perform trimming * examples/ got renamed to utils/ * libtokencap/ libdislocator/ and qdbi_mode/ were moved to utils/ * afl-cmin/afl-cmin.bash now search first in PATH and last in AFL_PATH diff --git a/docs/ideas.md b/docs/ideas.md index aaa3eed1..7cbe60a5 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -16,6 +16,8 @@ test cases executed. It should be clickable which value is X and Y axis, zoom factor, log scaling on-off, etc. +Mentor: vanhauser-thc + ## WASM Instrumentation Currently, AFL++ can be used for source code fuzzing and traditional binaries. @@ -36,19 +38,6 @@ Either improve a single mutator thorugh learning of many different bugs Mentor: domenukk -## Collision-free Binary-Only Maps - -AFL++ supports collison-free maps using an LTO (link-time-optimization) pass. -This should be possible to implement for QEMU and Unicorn instrumentations. -As the forkserver parent caches just in time translated translation blocks, -adding a simple counter between jumps should be doable. - -Note: this is already in development for qemu by Andrea, so for people who -want to contribute it might make more sense to port his solution to unicorn. - -Mentor: andreafioraldi or domenukk -Issue/idea tracker: [https://github.com/AFLplusplus/AFLplusplus/issues/237](https://github.com/AFLplusplus/AFLplusplus/issues/237) - ## Your idea! Finally, we are open to proposals! diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9d9b0434..647a665e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -145,7 +145,7 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D and -Z (use -d to disable -D)\n" + " -M auto-sets -D, -Z (use -d to disable -D) and no trimming\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -502,6 +502,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { -- cgit 1.4.1 From 7a861498c27997cd7be01a5650d54cff3b87a02e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 15:04:40 +0100 Subject: added support for __afl_coverage_interesting --- docs/Changelog.md | 3 ++ instrumentation/README.instrument_list.md | 7 +++++ instrumentation/SanitizerCoverageLTO.so.cc | 20 ++++++++++++++ instrumentation/SanitizerCoveragePCGUARD.so.cc | 38 +++++++++++++++++++++++--- src/afl-cc.c | 16 ++++++----- src/afl-fuzz.c | 5 ++-- 6 files changed, 76 insertions(+), 13 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 6e59961b..99bc8b47 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -36,6 +36,9 @@ sending a mail to . - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - cmplog/redqueen can now process basic libc++ and libstdc++ std::string comparisons (though no position or length type variants) + - added support for __afl_coverage_interesting() for LTO and + and our own PCGUARD (llvm 10.0.1+), read more about this function + and selective coverage in instrumentation/README.instrument_list.md - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang diff --git a/instrumentation/README.instrument_list.md b/instrumentation/README.instrument_list.md index b47b50f6..b7dfb40c 100644 --- a/instrumentation/README.instrument_list.md +++ b/instrumentation/README.instrument_list.md @@ -43,6 +43,13 @@ in any function where you want: * `__AFL_COVERAGE_DISCARD();` - reset all coverage gathered until this point * `__AFL_COVERAGE_SKIP();` - mark this test case as unimportant. Whatever happens, afl-fuzz will ignore it. +A special function is `__afl_coverage_interesting`. +To use this, you must define `void __afl_coverage_interesting(u8 val, u32 id);`. +Then you can use this function globally, where the `val` parameter can be set +by you, the `id` parameter is for afl-fuzz and will be overwritten. +Note that useful parameters are for `val` are: 1, 2, 3, 4, 8, 16, 32, 64, 128. +A value of e.g. 33 will be seen as 32 for coverage purposes. + ## 3) Selective instrumenation with AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST This feature is equivalent to llvm 12 sancov feature and allows to specify diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 016ac71f..e3490847 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -1237,6 +1237,25 @@ void ModuleSanitizerCoverage::instrumentFunction( for (auto &BB : F) { + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + StringRef FuncName = Callee->getName(); + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + Value *val = ConstantInt::get(Int32Ty, ++afl_global_id); + callInst->setOperand(1, val); + + } + + } + if (shouldInstrumentBlock(F, &BB, DT, PDT, Options)) BlocksToInstrument.push_back(&BB); for (auto &Inst : BB) { @@ -1338,6 +1357,7 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, if (AllBlocks.empty()) return false; CreateFunctionLocalArrays(F, AllBlocks); + for (size_t i = 0, N = AllBlocks.size(); i < N; i++) { // afl++ START diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index ecd6bc9b..5b274770 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -311,7 +311,8 @@ class ModuleSanitizerCoverage { Function &F, Type *Ty, const char *Section); GlobalVariable *CreatePCArray(Function &F, ArrayRef AllBlocks); - void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks); + void CreateFunctionLocalArrays(Function &F, ArrayRef AllBlocks, + uint32_t special); void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx, bool IsLeafFunc = true); Function *CreateInitCallsForSections(Module &M, const char *CtorName, @@ -970,11 +971,11 @@ GlobalVariable *ModuleSanitizerCoverage::CreatePCArray( } void ModuleSanitizerCoverage::CreateFunctionLocalArrays( - Function &F, ArrayRef AllBlocks) { + Function &F, ArrayRef AllBlocks, uint32_t special) { if (Options.TracePCGuard) FunctionGuardArray = CreateFunctionLocalArrayInSection( - AllBlocks.size(), F, Int32Ty, SanCovGuardsSectionName); + AllBlocks.size() + special, F, Int32Ty, SanCovGuardsSectionName); if (Options.Inline8bitCounters) Function8bitCounterArray = CreateFunctionLocalArrayInSection( @@ -993,9 +994,38 @@ bool ModuleSanitizerCoverage::InjectCoverage(Function & F, bool IsLeafFunc) { if (AllBlocks.empty()) return false; - CreateFunctionLocalArrays(F, AllBlocks); + + uint32_t special = 0; + for (auto &BB : F) { + + for (auto &IN : BB) { + + CallInst *callInst = nullptr; + + if ((callInst = dyn_cast(&IN))) { + + Function *Callee = callInst->getCalledFunction(); + StringRef FuncName = Callee->getName(); + if (!Callee) continue; + if (callInst->getCallingConv() != llvm::CallingConv::C) continue; + if (FuncName.compare(StringRef("__afl_coverage_interesting"))) continue; + + uint32_t id = 1 + instr + (uint32_t)AllBlocks.size() + special++; + Value * val = ConstantInt::get(Int32Ty, id); + callInst->setOperand(1, val); + + } + + } + + } + + CreateFunctionLocalArrays(F, AllBlocks, special); for (size_t i = 0, N = AllBlocks.size(); i < N; i++) InjectCoverageAtBlock(F, *AllBlocks[i], i, IsLeafFunc); + + instr += special; + return true; } diff --git a/src/afl-cc.c b/src/afl-cc.c index b5dcb632..f513764a 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -572,7 +572,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - if (instrument_mode == INSTRUMENT_CFG) + if (instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) cc_params[cc_par_cnt++] = alloc_printf( "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); else @@ -1670,15 +1671,16 @@ int main(int argc, char **argv, char **envp) { if (compiler_mode == LTO) { if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || - instrument_mode == INSTRUMENT_CFG) { + instrument_mode == INSTRUMENT_CFG || + instrument_mode == INSTRUMENT_PCGUARD) { lto_mode = 1; - if (!instrument_mode) { + // force CFG + // if (!instrument_mode) { - instrument_mode = INSTRUMENT_CFG; - // ptr = instrument_mode_string[instrument_mode]; - - } + instrument_mode = INSTRUMENT_PCGUARD; + // ptr = instrument_mode_string[instrument_mode]; + // } } else if (instrument_mode == INSTRUMENT_LTO || diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 647a665e..82eff61f 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -145,7 +145,8 @@ static void usage(u8 *argv0, int more_help) { "Other stuff:\n" " -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n" - " -M auto-sets -D, -Z (use -d to disable -D) and no trimming\n" + " -M auto-sets -D, -Z (use -d to disable -D) and no " + "trimming\n" " -F path - sync to a foreign fuzzer queue directory (requires " "-M, can\n" " be specified up to %u times)\n" @@ -502,7 +503,7 @@ int main(int argc, char **argv_orig, char **envp) { afl->sync_id = ck_strdup(optarg); afl->skip_deterministic = 0; // force deterministic fuzzing afl->old_seed_selection = 1; // force old queue walking seed selection - afl->disable_trim = 1; // disable trimming + afl->disable_trim = 1; // disable trimming if ((c = strchr(afl->sync_id, ':'))) { -- cgit 1.4.1 From cc0210426a5a31d56d8a0e850dcc00d90833afcd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sun, 31 Jan 2021 17:32:24 +0100 Subject: code-format --- docs/Changelog.md | 1 + include/xxhash.h | 2 +- instrumentation/compare-transform-pass.so.cc | 2 +- src/afl-fuzz.c | 3 +++ utils/afl_untracer/afl-untracer.c | 2 +- utils/libtokencap/libtokencap.so.c | 10 +++++----- utils/persistent_mode/persistent_demo_new.c | 2 +- utils/persistent_mode/test-instr.c | 2 +- 8 files changed, 14 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 99bc8b47..ff69c949 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -27,6 +27,7 @@ sending a mail to . - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map + - fix for -Z - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/include/xxhash.h b/include/xxhash.h index 3bd56d13..006d3f3d 100644 --- a/include/xxhash.h +++ b/include/xxhash.h @@ -287,7 +287,7 @@ typedef uint32_t XXH32_hash_t; #else #include #if UINT_MAX == 0xFFFFFFFFUL -typedef unsigned int XXH32_hash_t; +typedef unsigned int XXH32_hash_t; #else #if ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long XXH32_hash_t; diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 932540a7..da5cf7e9 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -68,7 +68,7 @@ class CompareTransform : public ModulePass { const char *getPassName() const override { #else - StringRef getPassName() const override { + StringRef getPassName() const override { #endif return "transforms compare functions"; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 276074a4..f1f92717 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1632,8 +1632,11 @@ int main(int argc, char **argv_orig, char **envp) { afl->current_entry = 0; while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) { + ++afl->current_entry; + } + afl->queue_cur = afl->queue_buf[afl->current_entry]; if (unlikely(seek_to)) { diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index d2bad0b9..f3894a06 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -284,7 +284,7 @@ library_list_t *find_library(char *name) { // this seems to work for clang too. nice :) requires gcc 4.4+ #pragma GCC push_options #pragma GCC optimize("O0") -void breakpoint(void) { +void breakpoint(void) { if (debug) fprintf(stderr, "Breakpoint function \"breakpoint\" reached.\n"); diff --git a/utils/libtokencap/libtokencap.so.c b/utils/libtokencap/libtokencap.so.c index 26033b46..3629e804 100644 --- a/utils/libtokencap/libtokencap.so.c +++ b/utils/libtokencap/libtokencap.so.c @@ -161,8 +161,8 @@ static void __tokencap_load_mappings(void) { #elif defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; + #if defined __FreeBSD__ + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __OpenBSD__ int mib[] = {CTL_KERN, KERN_PROC_VMMAP, __tokencap_pid}; #elif defined __NetBSD__ @@ -177,7 +177,7 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ len = len * 4 / 3; - #elif defined __OpenBSD__ + #elif defined __OpenBSD__ len -= len % sizeof(struct kinfo_vmentry); #endif @@ -202,8 +202,8 @@ static void __tokencap_load_mappings(void) { #if defined __FreeBSD__ || defined __NetBSD__ - #if defined __FreeBSD__ - size_t size = region->kve_structsize; + #if defined __FreeBSD__ + size_t size = region->kve_structsize; if (size == 0) break; #elif defined __NetBSD__ diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index ca616236..7e694696 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -51,7 +51,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { diff --git a/utils/persistent_mode/test-instr.c b/utils/persistent_mode/test-instr.c index 2c6b6d77..6da511de 100644 --- a/utils/persistent_mode/test-instr.c +++ b/utils/persistent_mode/test-instr.c @@ -24,7 +24,7 @@ __AFL_FUZZ_INIT(); /* To ensure checks are not optimized out it is recommended to disable code optimization for the fuzzer harness main() */ #pragma clang optimize off -#pragma GCC optimize("O0") +#pragma GCC optimize("O0") int main(int argc, char **argv) { -- cgit 1.4.1 From 981ffb27a8a166b51a06d57fce044ed1eaf1aa62 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 1 Feb 2021 12:01:23 +0100 Subject: making AFL_MAP_SIZE obsolete --- afl-cmin | 4 +-- docs/Changelog.md | 2 ++ include/forkserver.h | 3 ++ include/sharedmem.h | 1 + src/afl-forkserver.c | 28 ++++++++++++++----- src/afl-fuzz-init.c | 14 ++++++---- src/afl-fuzz.c | 73 +++++++++++++++++++++++++++++++++++++++++++------ src/afl-sharedmem.c | 14 ++++++++-- src/afl-showmap.c | 41 +++++++++++++++++++++++++-- src/afl-tmin.c | 37 +++++++++++++++++++++++-- test-instr.c | 5 +++- test/test-basic.sh | 12 ++++---- test/test-gcc-plugin.sh | 10 +++---- test/test-llvm-lto.sh | 8 +++--- test/test-llvm.sh | 10 +++---- 15 files changed, 211 insertions(+), 51 deletions(-) (limited to 'docs') diff --git a/afl-cmin b/afl-cmin index ffefaead..31d7ddad 100755 --- a/afl-cmin +++ b/afl-cmin @@ -343,7 +343,7 @@ BEGIN { stat_format = "-f '%z %N'" # *BSD, MacOS } cmdline = "cd "in_dir" && find . \\( ! -name . -a -type d -prune \\) -o -type f -exec stat "stat_format" \\{\\} \\; | sort -k1n -k2r" - cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format") | sort -k1n -k2r" + cmdline = "ls "in_dir" | (cd "in_dir" && xargs stat "stat_format" 2>/dev/null) | sort -k1n -k2r" while (cmdline | getline) { sub(/^[0-9]+ (\.\/)?/,"",$0) infilesSmallToBig[i++] = $0 @@ -355,7 +355,7 @@ BEGIN { # Make sure that we're not dealing with a directory. if (0 == system("test -d "in_dir"/"first_file)) { - print "[-] Error: The input directory contains subdirectories - please fix." > "/dev/stderr" + print "[-] Error: The input directory is empty or contains subdirectories - please fix." > "/dev/stderr" exit 1 } diff --git a/docs/Changelog.md b/docs/Changelog.md index ff69c949..e9efdf38 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,6 +16,8 @@ sending a mail to . to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz + - Making AFL_MAP_SIZE obsolete - afl-fuzz now learns on start the + target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). this is costly hence new command line option diff --git a/include/forkserver.h b/include/forkserver.h index d2fcaa20..ac027f81 100644 --- a/include/forkserver.h +++ b/include/forkserver.h @@ -120,11 +120,14 @@ void afl_fsrv_init(afl_forkserver_t *fsrv); void afl_fsrv_init_dup(afl_forkserver_t *fsrv_to, afl_forkserver_t *from); void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, volatile u8 *stop_soon_p, u8 debug_child_output); +u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output); void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len); fsrv_run_result_t afl_fsrv_run_target(afl_forkserver_t *fsrv, u32 timeout, volatile u8 *stop_soon_p); void afl_fsrv_killall(void); void afl_fsrv_deinit(afl_forkserver_t *fsrv); +void afl_fsrv_kill(afl_forkserver_t *fsrv); #ifdef __APPLE__ #define MSG_FORK_ON_APPLE \ diff --git a/include/sharedmem.h b/include/sharedmem.h index b15d0535..fdc947f9 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 shmemfuzz_mode; struct cmp_map *cmp_map; } sharedmem_t; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index e59f0d11..9ee59822 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -682,11 +682,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if ((status & FS_OPT_AUTODICT) == FS_OPT_AUTODICT) { - if (ignore_autodict) { - - if (!be_quiet) { WARNF("Ignoring offered AUTODICT feature."); } - - } else { + if (!ignore_autodict) { if (fsrv->add_extra_func == NULL || fsrv->afl_ptr == NULL) { @@ -969,7 +965,9 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, } -static void afl_fsrv_kill(afl_forkserver_t *fsrv) { +/* Stop the forkserver and child */ + +void afl_fsrv_kill(afl_forkserver_t *fsrv) { if (fsrv->child_pid > 0) { kill(fsrv->child_pid, fsrv->kill_signal); } if (fsrv->fsrv_pid > 0) { @@ -979,13 +977,28 @@ static void afl_fsrv_kill(afl_forkserver_t *fsrv) { } + close(fsrv->fsrv_ctl_fd); + close(fsrv->fsrv_st_fd); + fsrv->fsrv_pid = -1; + fsrv->child_pid = -1; + +} + +/* Get the map size from the target forkserver */ + +u32 afl_fsrv_get_mapsize(afl_forkserver_t *fsrv, char **argv, + volatile u8 *stop_soon_p, u8 debug_child_output) { + + afl_fsrv_start(fsrv, argv, stop_soon_p, debug_child_output); + return fsrv->map_size; + } /* Delete the current testcase and write the buf to the testcase file */ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { - if (fsrv->shmem_fuzz) { + if (likely(fsrv->use_shmem_fuzz && fsrv->shmem_fuzz)) { if (unlikely(len > MAX_FILE)) len = MAX_FILE; @@ -1042,6 +1055,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) { } + // fprintf(stderr, "WRITE %d %u\n", fd, len); ck_write(fd, buf, len, fsrv->out_file); if (fsrv->use_stdin) { diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 56dae48c..40ba20c7 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -766,13 +766,16 @@ void read_testcases(afl_state_t *afl, u8 *directory) { } - if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { + /* + if (unlikely(afl->schedule >= FAST && afl->schedule <= RARE)) { - u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); - afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; - afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; + u64 cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, + HASH_CONST); afl->queue_top->n_fuzz_entry = cksum % N_FUZZ_SIZE; + afl->n_fuzz[afl->queue_top->n_fuzz_entry] = 1; - } + } + + */ } @@ -2490,6 +2493,7 @@ void setup_testcase_shmem(afl_state_t *afl) { // we need to set the non-instrumented mode to not overwrite the SHM_ENV_VAR u8 *map = afl_shm_init(afl->shm_fuzz, MAX_FILE + sizeof(u32), 1); + afl->shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index f1f92717..49733594 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -342,7 +342,6 @@ int main(int argc, char **argv_orig, char **envp) { afl->debug = debug; afl_fsrv_init(&afl->fsrv); if (debug) { afl->fsrv.debug = true; } - read_afl_environment(afl, envp); if (afl->shm.map_size) { afl->fsrv.map_size = afl->shm.map_size; } exit_1 = !!afl->afl_env.afl_bench_just_one; @@ -702,7 +701,6 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->in_bitmap) { FATAL("Multiple -B options not supported"); } afl->in_bitmap = optarg; - read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); break; case 'C': /* crash mode */ @@ -1369,13 +1367,6 @@ int main(int argc, char **argv_orig, char **envp) { set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); #endif - afl->fsrv.trace_bits = - afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); - - if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); } - memset(afl->virgin_tmout, 255, afl->fsrv.map_size); - memset(afl->virgin_crash, 255, afl->fsrv.map_size); - init_count_class16(); if (afl->is_main_node && check_main_node_exists(afl) == 1) { @@ -1542,6 +1533,70 @@ int main(int argc, char **argv_orig, char **envp) { } afl->argv = use_argv; + afl->fsrv.trace_bits = + afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode); + + if (!afl->non_instrumented_mode) { + + afl->fsrv.map_size = 4194304; // dummy temporary value + + u32 new_map_size = afl_fsrv_get_mapsize( + &afl->fsrv, afl->argv, &afl->stop_soon, afl->afl_env.afl_debug_child); + + if (new_map_size && new_map_size != 4194304) { + + // only reinitialize when it makes sense + if (map_size != new_map_size) { + + // if (map_size < new_map_size || + // (new_map_size > map_size && new_map_size - map_size > + // MAP_SIZE)) { + + OKF("Re-initializing maps to %u bytes", new_map_size); + + afl->virgin_bits = ck_realloc(afl->virgin_bits, map_size); + afl->virgin_tmout = ck_realloc(afl->virgin_tmout, map_size); + afl->virgin_crash = ck_realloc(afl->virgin_crash, map_size); + afl->var_bytes = ck_realloc(afl->var_bytes, map_size); + afl->top_rated = ck_realloc(afl->top_rated, map_size * sizeof(void *)); + afl->clean_trace = ck_realloc(afl->clean_trace, map_size); + afl->clean_trace_custom = ck_realloc(afl->clean_trace_custom, map_size); + afl->first_trace = ck_realloc(afl->first_trace, map_size); + afl->map_tmp_buf = ck_realloc(afl->map_tmp_buf, map_size); + + afl_shm_deinit(&afl->shm); + afl_fsrv_kill(&afl->fsrv); + afl->fsrv.map_size = new_map_size; + afl->fsrv.trace_bits = afl_shm_init(&afl->shm, afl->fsrv.map_size, + afl->non_instrumented_mode); + setenv("AFL_NO_AUTODICT", "1", 1); // loaded already + afl_fsrv_start(&afl->fsrv, afl->argv, &afl->stop_soon, + afl->afl_env.afl_debug_child); + + } + + map_size = new_map_size; + + } + + afl->fsrv.map_size = map_size; + + } + + // after we have the correct bitmap size we can read the bitmap -B option + // and set the virgin maps + if (!afl->in_bitmap) { + + memset(afl->virgin_bits, 255, afl->fsrv.map_size); + + } else { + + read_bitmap(afl->in_bitmap, afl->virgin_bits, afl->fsrv.map_size); + + } + + memset(afl->virgin_tmout, 255, afl->fsrv.map_size); + memset(afl->virgin_crash, 255, afl->fsrv.map_size); if (afl->cmplog_binary) { diff --git a/src/afl-sharedmem.c b/src/afl-sharedmem.c index fe641d0d..3241a130 100644 --- a/src/afl-sharedmem.c +++ b/src/afl-sharedmem.c @@ -66,9 +66,17 @@ static list_t shm_list = {.element_prealloc_count = 0}; void afl_shm_deinit(sharedmem_t *shm) { - if (shm == NULL) return; - + if (shm == NULL) { return; } list_remove(&shm_list, shm); + if (shm->shmemfuzz_mode) { + + unsetenv(SHM_FUZZ_ENV_VAR); + + } else { + + unsetenv(SHM_ENV_VAR); + + } #ifdef USEMMAP if (shm->map != NULL) { @@ -94,6 +102,8 @@ void afl_shm_deinit(sharedmem_t *shm) { if (shm->cmplog_mode) { + unsetenv(CMPLOG_SHM_ENV_VAR); + if (shm->cmp_map != NULL) { munmap(shm->cmp_map, shm->map_size); diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 62bf1021..56abe4f1 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -86,7 +86,8 @@ static u8 quiet_mode, /* Hide non-essential messages? */ remove_shm = 1, /* remove shmem? */ collect_coverage, /* collect coverage */ have_coverage, /* have coverage? */ - no_classify; /* do not classify counts */ + no_classify, /* do not classify counts */ + debug; /* debug mode */ static volatile u8 stop_soon, /* Ctrl-C pressed? */ child_crashed; /* Child crashed? */ @@ -743,6 +744,7 @@ int main(int argc, char **argv_orig, char **envp) { char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -991,14 +993,16 @@ int main(int argc, char **argv_orig, char **envp) { // if (afl->shmem_testcase_mode) { setup_testcase_shmem(afl); } + setenv("AFL_NO_AUTODICT", "1", 1); + /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); setup_signal_handlers(); set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); if (!quiet_mode) { @@ -1051,6 +1055,7 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -1063,6 +1068,38 @@ int main(int argc, char **argv_orig, char **envp) { fsrv->shmem_fuzz_len = (u32 *)map; fsrv->shmem_fuzz = map + sizeof(u32); + u32 save_be_quiet = be_quiet; + be_quiet = debug; + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = afl_fsrv_get_mapsize( + fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + be_quiet = save_be_quiet; + + if (new_map_size) { + + // only reinitialize when it makes sense + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + if (in_dir) { DIR * dir_in, *dir_out = NULL; diff --git a/src/afl-tmin.c b/src/afl-tmin.c index 09b5211d..799a4b87 100644 --- a/src/afl-tmin.c +++ b/src/afl-tmin.c @@ -79,7 +79,8 @@ static u8 crash_mode, /* Crash-centric mode? */ edges_only, /* Ignore hit counts? */ exact_mode, /* Require path match for crashes? */ remove_out_file, /* remove out_file on exit? */ - remove_shm = 1; /* remove shmem on exit? */ + remove_shm = 1, /* remove shmem on exit? */ + debug; /* debug mode */ static volatile u8 stop_soon; /* Ctrl-C pressed? */ @@ -878,6 +879,7 @@ int main(int argc, char **argv_orig, char **envp) { char **argv = argv_cpy_dup(argc, argv_orig); afl_forkserver_t fsrv_var = {0}; + if (getenv("AFL_DEBUG")) { debug = 1; } fsrv = &fsrv_var; afl_fsrv_init(fsrv); map_size = get_map_size(); @@ -1074,6 +1076,7 @@ int main(int argc, char **argv_orig, char **envp) { if (optind == argc || !in_file || !output_file) { usage(argv[0]); } check_environment_vars(envp); + setenv("AFL_NO_AUTODICT", "1", 1); if (fsrv->qemu_mode && getenv("AFL_USE_QASAN")) { @@ -1102,7 +1105,6 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm.cmplog_mode = 0; - fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); atexit(at_exit_handler); setup_signal_handlers(); @@ -1110,6 +1112,7 @@ int main(int argc, char **argv_orig, char **envp) { set_up_environment(fsrv); fsrv->target_path = find_binary(argv[optind]); + fsrv->trace_bits = afl_shm_init(&shm, map_size, 0); detect_file_args(argv + optind, out_file, &fsrv->use_stdin); if (fsrv->qemu_mode) { @@ -1181,6 +1184,7 @@ int main(int argc, char **argv_orig, char **envp) { /* initialize cmplog_mode */ shm_fuzz->cmplog_mode = 0; u8 *map = afl_shm_init(shm_fuzz, MAX_FILE + sizeof(u32), 1); + shm_fuzz->shmemfuzz_mode = 1; if (!map) { FATAL("BUG: Zero return from afl_shm_init."); } #ifdef USEMMAP setenv(SHM_FUZZ_ENV_VAR, shm_fuzz->g_shm_file_path, 1); @@ -1195,12 +1199,39 @@ int main(int argc, char **argv_orig, char **envp) { read_initial_file(); - afl_fsrv_start( + fsrv->map_size = 4194304; // dummy temporary value + u32 new_map_size = afl_fsrv_get_mapsize( fsrv, use_argv, &stop_soon, (get_afl_env("AFL_DEBUG_CHILD") || get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) ? 1 : 0); + if (new_map_size) { + + if (map_size < new_map_size || + (new_map_size > map_size && new_map_size - map_size > MAP_SIZE)) { + + if (!be_quiet) + ACTF("Aquired new map size for target: %u bytes\n", new_map_size); + + afl_shm_deinit(&shm); + afl_fsrv_kill(fsrv); + fsrv->map_size = new_map_size; + fsrv->trace_bits = afl_shm_init(&shm, new_map_size, 0); + afl_fsrv_start(fsrv, use_argv, &stop_soon, + (get_afl_env("AFL_DEBUG_CHILD") || + get_afl_env("AFL_DEBUG_CHILD_OUTPUT")) + ? 1 + : 0); + + } + + map_size = new_map_size; + + } + + fsrv->map_size = map_size; + if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz) shm_fuzz = deinit_shmem(fsrv, shm_fuzz); diff --git a/test-instr.c b/test-instr.c index 84ac0036..00799103 100644 --- a/test-instr.c +++ b/test-instr.c @@ -32,7 +32,8 @@ int main(int argc, char **argv) { } else { - if (argc >= 3 && strcmp(argv[1], "-f") == 0) + if (argc >= 3 && strcmp(argv[1], "-f") == 0) { + if ((fd = open(argv[2], O_RDONLY)) < 0) { fprintf(stderr, "Error: unable to open %s\n", argv[2]); @@ -40,6 +41,8 @@ int main(int argc, char **argv) { } + } + if (read(fd, buf, sizeof(buf)) < 1) { printf("Hum?\n"); diff --git a/test/test-basic.sh b/test/test-basic.sh index fcac8ca3..132610c0 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -11,8 +11,8 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" @@ -26,7 +26,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc } rm -f test-instr.plain.0 test-instr.plain.1 SKIP= - TUPLES=`echo 1|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 1|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 12 && { $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" } || { @@ -132,8 +132,8 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc AFL_HARDEN=1 ../${AFL_GCC} -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] ${AFL_GCC} compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] ${AFL_GCC} instrumentation should be different on different input but is not" @@ -146,7 +146,7 @@ test "$SYS" = "i686" -o "$SYS" = "x86_64" -o "$SYS" = "amd64" -o "$SYS" = "i86pc CODE=1 } rm -f test-instr.plain.0 test-instr.plain.1 - TUPLES=`echo 1|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 1|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 12 && { $ECHO "$GREEN[+] ${AFL_GCC} run reported $TUPLES instrumented locations which is fine" } || { diff --git a/test/test-gcc-plugin.sh b/test/test-gcc-plugin.sh index cce6336b..4c36b6c9 100755 --- a/test/test-gcc-plugin.sh +++ b/test/test-gcc-plugin.sh @@ -10,15 +10,15 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { AFL_HARDEN=1 ../afl-gcc-fast -o test-compcov.harden.gccpi test-compcov.c > /dev/null 2>&1 test -e test-instr.plain.gccpi && { $ECHO "$GREEN[+] gcc_plugin compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain.gccpi > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain.gccpi < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] gcc_plugin instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] gcc_plugin instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain.gccpi 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 9 && { $ECHO "$GREEN[+] gcc_plugin run reported $TUPLES instrumented locations which is fine" } || { @@ -87,7 +87,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { echo foobar.c > instrumentlist.txt AFL_GCC_INSTRUMENT_FILE=instrumentlist.txt ../afl-gcc-fast -o test-compcov test-compcov.c > /dev/null 2>&1 test -x test-compcov && test_compcov_binary_functionality ./test-compcov && { - echo 1 | ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 0 tuples" && { + echo 1 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o - -r -- ./test-compcov 2>&1 | grep -q "Captured 0 tuples" && { $ECHO "$GREEN[+] gcc_plugin instrumentlist feature works correctly" } || { $ECHO "$RED[!] gcc_plugin instrumentlist feature failed" @@ -100,7 +100,7 @@ test -e ../afl-gcc-fast -a -e ../afl-compiler-rt.o && { rm -f test-compcov test.out instrumentlist.txt ../afl-gcc-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] gcc_plugin persistent mode feature works correctly" } || { $ECHO "$RED[!] gcc_plugin persistent mode feature failed to work" diff --git a/test/test-llvm-lto.sh b/test/test-llvm-lto.sh index a931afb7..3e762acf 100755 --- a/test/test-llvm-lto.sh +++ b/test/test-llvm-lto.sh @@ -16,15 +16,15 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { ../afl-clang-lto -o test-instr.plain ../test-instr.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] llvm_mode LTO compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff -q test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] llvm_mode LTO instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] llvm_mode LTO instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 2 -a "$TUPLES" -lt 7 && { $ECHO "$GREEN[+] llvm_mode LTO run reported $TUPLES instrumented locations which is fine" } || { @@ -59,7 +59,7 @@ test -e ../afl-clang-lto -a -e ../afl-llvm-lto-instrumentation.so && { rm -f test-compcov test.out instrumentlist.txt ../afl-clang-lto -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m none -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode LTO persistent mode feature works correctly" } || { $ECHO "$RED[!] llvm_mode LTO persistent mode feature failed to work" diff --git a/test/test-llvm.sh b/test/test-llvm.sh index c968d5a9..156b8920 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -16,15 +16,15 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { AFL_HARDEN=1 ../afl-clang-fast -o test-compcov.harden test-compcov.c > /dev/null 2>&1 test -e test-instr.plain && { $ECHO "$GREEN[+] llvm_mode compilation succeeded" - echo 0 | ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 - ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 + echo 0 | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.0 -r -- ./test-instr.plain > /dev/null 2>&1 + AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o test-instr.plain.1 -r -- ./test-instr.plain < /dev/null > /dev/null 2>&1 test -e test-instr.plain.0 -a -e test-instr.plain.1 && { diff test-instr.plain.0 test-instr.plain.1 > /dev/null 2>&1 && { $ECHO "$RED[!] llvm_mode instrumentation should be different on different input but is not" CODE=1 } || { $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 2 -a "$TUPLES" -lt 8 && { $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" } || { @@ -128,7 +128,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { test -e ../libLLVMInsTrim.so && { AFL_LLVM_INSTRUMENT=CFG AFL_LLVM_INSTRIM_LOOPHEAD=1 ../afl-clang-fast -o test-instr.instrim ../test-instr.c > /dev/null 2>test.out test -e test-instr.instrim && { - TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` + TUPLES=`echo 0|AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.instrim 2>&1 | grep Captur | awk '{print$3}'` test "$TUPLES" -gt 1 -a "$TUPLES" -lt 5 && { $ECHO "$GREEN[+] llvm_mode InsTrim reported $TUPLES instrumented locations which is fine" } || { @@ -216,7 +216,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { rm -rf errors test-cmplog in core.* ../afl-clang-fast -o test-persistent ../utils/persistent_mode/persistent_demo.c > /dev/null 2>&1 test -e test-persistent && { - echo foo | ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { + echo foo | AFL_QUIET=1 ../afl-showmap -m ${MEM_LIMIT} -o /dev/null -q -r ./test-persistent && { $ECHO "$GREEN[+] llvm_mode persistent mode feature works correctly" } || { $ECHO "$RED[!] llvm_mode persistent mode feature failed to work" -- cgit 1.4.1 From 209c5ba4657b641bf261da7ac9ce7d3f809109c2 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 7 Feb 2021 05:33:02 +0100 Subject: larger map, stats reload fix, code format --- docs/Changelog.md | 2 + instrumentation/afl-compiler-rt.o.c | 2 +- instrumentation/afl-llvm-lto-instrumentation.so.cc | 3 +- qemu_mode/libqasan/dlmalloc.c | 5 ++ src/afl-fuzz-bitmap.c | 3 +- src/afl-fuzz-statsd.c | 63 ++++++++++++---------- utils/afl_untracer/afl-untracer.c | 10 ++-- 7 files changed, 52 insertions(+), 36 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e9efdf38..f2041917 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,8 @@ sending a mail to . `-i` or resumes (as these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) + - on restarts (-i)/autoresume (AFL_AUTORESUME) the stats are now + reloaded and used, thanks to Vimal Joseph for this PR! - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 65a5d3d2..059691ec 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -70,7 +70,7 @@ run. It will end up as .comm, so it shouldn't be too wasteful. */ #if MAP_SIZE <= 65536 - #define MAP_INITIAL_SIZE 1048576 + #define MAP_INITIAL_SIZE 2097152 #else #define MAP_INITIAL_SIZE MAP_SIZE #endif diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index fa494f44..841d52e5 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -69,7 +69,8 @@ class AFLLTOPass : public ModulePass { if (getenv("AFL_DEBUG")) debug = 1; if ((ptr = getenv("AFL_LLVM_LTO_STARTID")) != NULL) - if ((afl_global_id = (uint32_t)atoi(ptr)) < 0 || afl_global_id >= MAP_SIZE) + if ((afl_global_id = (uint32_t)atoi(ptr)) < 0 || + afl_global_id >= MAP_SIZE) FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %u\n", ptr, MAP_SIZE - 1); diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index 39ca4301..ce94451d 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3907,6 +3907,7 @@ static void internal_malloc_stats(mstate m) { clear_smallmap(M, I); \ \ } else if (RTCHECK(B == smallbin_at(M, I) || \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4117,6 +4118,7 @@ static void internal_malloc_stats(mstate m) { XP->child[1] = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4132,6 +4134,7 @@ static void internal_malloc_stats(mstate m) { C0->parent = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4143,11 +4146,13 @@ static void internal_malloc_stats(mstate m) { C1->parent = R; \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-fuzz-bitmap.c b/src/afl-fuzz-bitmap.c index 0c4a114e..4ed59364 100644 --- a/src/afl-fuzz-bitmap.c +++ b/src/afl-fuzz-bitmap.c @@ -325,7 +325,8 @@ u8 *describe_op(afl_state_t *afl, u8 new_bits, size_t max_description_len) { } - sprintf(ret + strlen(ret), ",time:%llu", get_cur_time() - afl->start_time); + sprintf(ret + strlen(ret), ",time:%llu", + get_cur_time() + afl->prev_run_time - afl->start_time); if (afl->current_custom_fuzz && afl->current_custom_fuzz->afl_custom_describe) { diff --git a/src/afl-fuzz-statsd.c b/src/afl-fuzz-statsd.c index 69cafd90..461bbbf6 100644 --- a/src/afl-fuzz-statsd.c +++ b/src/afl-fuzz-statsd.c @@ -1,3 +1,8 @@ +/* + * This implements rpc.statsd support, see docs/rpc_statsd.md + * + */ + #include #include #include @@ -226,37 +231,39 @@ int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) { */ if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_SUFFIX) { - snprintf(buff, bufflen, afl->statsd_metric_format, - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - tags, afl->queued_paths, tags, afl->queued_favored, tags, - afl->queued_discovered, tags, afl->queued_imported, tags, - afl->max_depth, tags, afl->current_entry, tags, - afl->pending_favored, tags, afl->pending_not_fuzzed, tags, - afl->queued_variable, tags, afl->unique_crashes, tags, - afl->unique_hangs, tags, afl->total_crashes, tags, - afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, afl->expand_havoc, tags); + snprintf( + buff, bufflen, afl->statsd_metric_format, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc, tags); } else if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_MID) { - snprintf(buff, bufflen, afl->statsd_metric_format, tags, - afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, - afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, - afl->fsrv.total_execs / - ((double)(get_cur_time() - afl->start_time) / 1000), - tags, afl->queued_paths, tags, afl->queued_favored, tags, - afl->queued_discovered, tags, afl->queued_imported, tags, - afl->max_depth, tags, afl->current_entry, tags, - afl->pending_favored, tags, afl->pending_not_fuzzed, tags, - afl->queued_variable, tags, afl->unique_crashes, tags, - afl->unique_hangs, tags, afl->total_crashes, tags, - afl->slowest_exec_ms, tags, - count_non_255_bytes(afl, afl->virgin_bits), tags, - afl->var_byte_count, tags, afl->expand_havoc); + snprintf( + buff, bufflen, afl->statsd_metric_format, tags, + afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags, + afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags, + afl->fsrv.total_execs / + ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) / + 1000), + tags, afl->queued_paths, tags, afl->queued_favored, tags, + afl->queued_discovered, tags, afl->queued_imported, tags, + afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored, + tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags, + afl->unique_crashes, tags, afl->unique_hangs, tags, afl->total_crashes, + tags, afl->slowest_exec_ms, tags, + count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count, + tags, afl->expand_havoc); } diff --git a/utils/afl_untracer/afl-untracer.c b/utils/afl_untracer/afl-untracer.c index 1f1a10ea..2baeb58d 100644 --- a/utils/afl_untracer/afl-untracer.c +++ b/utils/afl_untracer/afl-untracer.c @@ -480,9 +480,9 @@ void setup_trap_instrumentation(void) { // Index into the coverage bitmap for the current trap instruction. #ifdef __aarch64__ uint64_t bitmap_index = 0; -#ifdef __APPLE__ + #ifdef __APPLE__ pthread_jit_write_protect_np(0); -#endif + #endif #else uint32_t bitmap_index = 0; #endif @@ -627,13 +627,13 @@ static void sigtrap_handler(int signum, siginfo_t *si, void *context) { // Must re-execute the instruction, so decrement PC by one instruction. ucontext_t *ctx = (ucontext_t *)context; #if defined(__APPLE__) && defined(__LP64__) -#if defined(__x86_64__) + #if defined(__x86_64__) ctx->uc_mcontext->__ss.__rip -= 1; addr = ctx->uc_mcontext->__ss.__rip; -#else + #else ctx->uc_mcontext->__ss.__pc -= 4; addr = ctx->uc_mcontext->__ss.__pc; -#endif + #endif #elif defined(__linux__) #if defined(__x86_64__) || defined(__i386__) ctx->uc_mcontext.gregs[REG_RIP] -= 1; -- cgit 1.4.1 From 267b085f80074e61bdacf1e85e99014b6b2cdad2 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 10 Feb 2021 15:15:16 +0100 Subject: dlmalloc only for non glibc qasan and AFL_QEMU_FORCE_DFL --- docs/env_variables.md | 6 + qemu_mode/QEMUAFL_VERSION | 2 +- qemu_mode/libqasan/dlmalloc.c | 4769 ++++++++++++++++------------------------- qemu_mode/libqasan/malloc.c | 147 +- qemu_mode/qemuafl | 2 +- 5 files changed, 2003 insertions(+), 2923 deletions(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index 4c3b1cfb..ab56c178 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -514,6 +514,12 @@ The QEMU wrapper used to instrument binary-only code supports several settings: stack pointer in which QEMU can find the return address when `start addr` is hit. + - With `AFL_USE_QASAN` you can enable QEMU AddressSanitizer for dynamically + linked binaries. + + - With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered singal + handlers of the target. + ## 6) Settings for afl-cmin The corpus minimization script offers very little customization: diff --git a/qemu_mode/QEMUAFL_VERSION b/qemu_mode/QEMUAFL_VERSION index 97184973..d9f0ec33 100644 --- a/qemu_mode/QEMUAFL_VERSION +++ b/qemu_mode/QEMUAFL_VERSION @@ -1 +1 @@ -6ab6bf28de +47722f64e4 diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index 3c7dcea8..7e3cb159 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -1,3 +1,7 @@ +#include + +#ifndef __GLIBC__ + /* This is a version (aka dlmalloc) of malloc/free/realloc written by Doug Lea and released to the public domain, as explained at @@ -203,12 +207,9 @@ mspaces as thread-locals. For example: static __thread mspace tlms = 0; void* tlmalloc(size_t bytes) { - if (tlms == 0) tlms = create_mspace(0, 0); return mspace_malloc(tlms, bytes); - } - void tlfree(void* mem) { mspace_free(tlms, mem); } Unless FOOTERS is defined, each mspace is completely independent. @@ -528,198 +529,197 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP /* Version identifier to allow people to support multiple versions */ #ifndef DLMALLOC_VERSION - #define DLMALLOC_VERSION 20806 -#endif /* DLMALLOC_VERSION */ +#define DLMALLOC_VERSION 20806 +#endif /* DLMALLOC_VERSION */ #ifndef DLMALLOC_EXPORT - #define DLMALLOC_EXPORT extern +#define DLMALLOC_EXPORT extern #endif #ifndef WIN32 - #ifdef _WIN32 - #define WIN32 1 - #endif /* _WIN32 */ - #ifdef _WIN32_WCE - #define LACKS_FCNTL_H - #define WIN32 1 - #endif /* _WIN32_WCE */ -#endif /* WIN32 */ +#ifdef _WIN32 +#define WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define WIN32 1 +#endif /* _WIN32_WCE */ +#endif /* WIN32 */ #ifdef WIN32 - #define WIN32_LEAN_AND_MEAN - #include - #include - #define HAVE_MMAP 1 - #define HAVE_MORECORE 0 - #define LACKS_UNISTD_H - #define LACKS_SYS_PARAM_H - #define LACKS_SYS_MMAN_H - #define LACKS_STRING_H - #define LACKS_STRINGS_H - #define LACKS_SYS_TYPES_H - #define LACKS_ERRNO_H - #define LACKS_SCHED_H - #ifndef MALLOC_FAILURE_ACTION - #define MALLOC_FAILURE_ACTION - #endif /* MALLOC_FAILURE_ACTION */ - #ifndef MMAP_CLEARS - #ifdef _WIN32_WCE /* WINCE reportedly does not clear */ - #define MMAP_CLEARS 0 - #else - #define MMAP_CLEARS 1 - #endif /* _WIN32_WCE */ - #endif /*MMAP_CLEARS */ -#endif /* WIN32 */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#define LACKS_SCHED_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef MMAP_CLEARS +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /*MMAP_CLEARS */ +#endif /* WIN32 */ #if defined(DARWIN) || defined(_DARWIN) - /* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ - #ifndef HAVE_MORECORE - #define HAVE_MORECORE 0 - #define HAVE_MMAP 1 - /* OSX allocators provide 16 byte alignment */ - #ifndef MALLOC_ALIGNMENT - #define MALLOC_ALIGNMENT ((size_t)16U) - #endif - #endif /* HAVE_MORECORE */ -#endif /* DARWIN */ +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ #ifndef LACKS_SYS_TYPES_H - #include /* For size_t */ -#endif /* LACKS_SYS_TYPES_H */ +#include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ /* The maximum possible size_t value has all bits set */ -#define MAX_SIZE_T (~(size_t)0) - -#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ - #define USE_LOCKS \ - ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ - (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) -#endif /* USE_LOCKS */ - -#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ - #if ((defined(__GNUC__) && \ - ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ - defined(__i386__) || defined(__x86_64__))) || \ - (defined(_MSC_VER) && _MSC_VER >= 1310)) - #ifndef USE_SPIN_LOCKS - #define USE_SPIN_LOCKS 1 - #endif /* USE_SPIN_LOCKS */ - #elif USE_SPIN_LOCKS - #error "USE_SPIN_LOCKS defined without implementation" - #endif /* ... locks available... */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ +#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#endif /* USE_LOCKS */ + +#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ +#if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER>=1310)) +#ifndef USE_SPIN_LOCKS +#define USE_SPIN_LOCKS 1 +#endif /* USE_SPIN_LOCKS */ +#elif USE_SPIN_LOCKS +#error "USE_SPIN_LOCKS defined without implementation" +#endif /* ... locks available... */ #elif !defined(USE_SPIN_LOCKS) - #define USE_SPIN_LOCKS 0 -#endif /* USE_LOCKS */ +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS */ #ifndef ONLY_MSPACES - #define ONLY_MSPACES 0 -#endif /* ONLY_MSPACES */ +#define ONLY_MSPACES 0 +#endif /* ONLY_MSPACES */ #ifndef MSPACES - #if ONLY_MSPACES - #define MSPACES 1 - #else /* ONLY_MSPACES */ - #define MSPACES 0 - #endif /* ONLY_MSPACES */ -#endif /* MSPACES */ +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ #ifndef MALLOC_ALIGNMENT - #define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) -#endif /* MALLOC_ALIGNMENT */ +#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) +#endif /* MALLOC_ALIGNMENT */ #ifndef FOOTERS - #define FOOTERS 0 -#endif /* FOOTERS */ +#define FOOTERS 0 +#endif /* FOOTERS */ #ifndef ABORT - #define ABORT abort() -#endif /* ABORT */ +#define ABORT abort() +#endif /* ABORT */ #ifndef ABORT_ON_ASSERT_FAILURE - #define ABORT_ON_ASSERT_FAILURE 1 -#endif /* ABORT_ON_ASSERT_FAILURE */ +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ #ifndef PROCEED_ON_ERROR - #define PROCEED_ON_ERROR 0 -#endif /* PROCEED_ON_ERROR */ +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ #ifndef INSECURE - #define INSECURE 0 -#endif /* INSECURE */ +#define INSECURE 0 +#endif /* INSECURE */ #ifndef MALLOC_INSPECT_ALL - #define MALLOC_INSPECT_ALL 0 -#endif /* MALLOC_INSPECT_ALL */ +#define MALLOC_INSPECT_ALL 0 +#endif /* MALLOC_INSPECT_ALL */ #ifndef HAVE_MMAP - #define HAVE_MMAP 1 -#endif /* HAVE_MMAP */ +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ #ifndef MMAP_CLEARS - #define MMAP_CLEARS 1 -#endif /* MMAP_CLEARS */ +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ #ifndef HAVE_MREMAP - #ifdef linux - #define HAVE_MREMAP 1 - #define _GNU_SOURCE /* Turns on mremap() definition */ - #else /* linux */ - #define HAVE_MREMAP 0 - #endif /* linux */ -#endif /* HAVE_MREMAP */ +#ifdef linux +#define HAVE_MREMAP 1 +#define _GNU_SOURCE /* Turns on mremap() definition */ +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ #ifndef MALLOC_FAILURE_ACTION - #define MALLOC_FAILURE_ACTION errno = ENOMEM; -#endif /* MALLOC_FAILURE_ACTION */ +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ #ifndef HAVE_MORECORE - #if ONLY_MSPACES - #define HAVE_MORECORE 0 - #else /* ONLY_MSPACES */ - #define HAVE_MORECORE 1 - #endif /* ONLY_MSPACES */ -#endif /* HAVE_MORECORE */ +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ #if !HAVE_MORECORE - #define MORECORE_CONTIGUOUS 0 -#else /* !HAVE_MORECORE */ - #define MORECORE_DEFAULT sbrk - #ifndef MORECORE_CONTIGUOUS - #define MORECORE_CONTIGUOUS 1 - #endif /* MORECORE_CONTIGUOUS */ -#endif /* HAVE_MORECORE */ +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ #ifndef DEFAULT_GRANULARITY - #if (MORECORE_CONTIGUOUS || defined(WIN32)) - #define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ - #else /* MORECORE_CONTIGUOUS */ - #define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) - #endif /* MORECORE_CONTIGUOUS */ -#endif /* DEFAULT_GRANULARITY */ +#if (MORECORE_CONTIGUOUS || defined(WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ #ifndef DEFAULT_TRIM_THRESHOLD - #ifndef MORECORE_CANNOT_TRIM - #define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) - #else /* MORECORE_CANNOT_TRIM */ - #define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T - #endif /* MORECORE_CANNOT_TRIM */ -#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ #ifndef DEFAULT_MMAP_THRESHOLD - #if HAVE_MMAP - #define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) - #else /* HAVE_MMAP */ - #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T - #endif /* HAVE_MMAP */ -#endif /* DEFAULT_MMAP_THRESHOLD */ +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ #ifndef MAX_RELEASE_CHECK_RATE - #if HAVE_MMAP - #define MAX_RELEASE_CHECK_RATE 4095 - #else - #define MAX_RELEASE_CHECK_RATE MAX_SIZE_T - #endif /* HAVE_MMAP */ -#endif /* MAX_RELEASE_CHECK_RATE */ +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ #ifndef USE_BUILTIN_FFS - #define USE_BUILTIN_FFS 0 -#endif /* USE_BUILTIN_FFS */ +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ #ifndef USE_DEV_RANDOM - #define USE_DEV_RANDOM 0 -#endif /* USE_DEV_RANDOM */ +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ #ifndef NO_MALLINFO - #define NO_MALLINFO 0 -#endif /* NO_MALLINFO */ +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ #ifndef MALLINFO_FIELD_TYPE - #define MALLINFO_FIELD_TYPE size_t -#endif /* MALLINFO_FIELD_TYPE */ +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ #ifndef NO_MALLOC_STATS - #define NO_MALLOC_STATS 0 -#endif /* NO_MALLOC_STATS */ +#define NO_MALLOC_STATS 0 +#endif /* NO_MALLOC_STATS */ #ifndef NO_SEGMENT_TRAVERSAL - #define NO_SEGMENT_TRAVERSAL 0 -#endif /* NO_SEGMENT_TRAVERSAL */ +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ /* mallopt tuning options. SVID/XPG defines four standard parameter @@ -731,9 +731,9 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP #undef M_TRIM_THRESHOLD #undef M_GRANULARITY #undef M_MMAP_THRESHOLD -#define M_TRIM_THRESHOLD (-1) -#define M_GRANULARITY (-2) -#define M_MMAP_THRESHOLD (-3) +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) /* ------------------------ Mallinfo declarations ------------------------ */ @@ -762,32 +762,28 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP /* #define HAVE_USR_INCLUDE_MALLOC_H */ - #ifdef HAVE_USR_INCLUDE_MALLOC_H - #include "/usr/include/malloc.h" - #else /* HAVE_USR_INCLUDE_MALLOC_H */ - #ifndef STRUCT_MALLINFO_DECLARED - /* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is - * defined */ - #define _STRUCT_MALLINFO - #define STRUCT_MALLINFO_DECLARED 1 +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ +#define _STRUCT_MALLINFO +#define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { - - MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ - MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ - MALLINFO_FIELD_TYPE smblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblks; /* always 0 */ - MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ - MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ - MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ - MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ - MALLINFO_FIELD_TYPE fordblks; /* total free space */ - MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ - + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; - - #endif /* STRUCT_MALLINFO_DECLARED */ - #endif /* HAVE_USR_INCLUDE_MALLOC_H */ -#endif /* NO_MALLINFO */ +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ /* Try to persuade compilers to inline. The most critical functions for @@ -796,14 +792,14 @@ struct mallinfo { #ifndef FORCEINLINE #if defined(__GNUC__) - #define FORCEINLINE __inline __attribute__((always_inline)) +#define FORCEINLINE __inline __attribute__ ((always_inline)) #elif defined(_MSC_VER) #define FORCEINLINE __forceinline #endif #endif #ifndef NOINLINE #if defined(__GNUC__) - #define NOINLINE __attribute__((noinline)) + #define NOINLINE __attribute__ ((noinline)) #elif defined(_MSC_VER) #define NOINLINE __declspec(noinline) #else @@ -813,43 +809,42 @@ struct mallinfo { #ifdef __cplusplus extern "C" { - - #ifndef FORCEINLINE - #define FORCEINLINE inline - #endif -#endif /* __cplusplus */ #ifndef FORCEINLINE - #define FORCEINLINE + #define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE + #define FORCEINLINE #endif #if !ONLY_MSPACES /* ------------------- Declarations of public routines ------------------- */ - #ifndef USE_DL_PREFIX - #define dlcalloc calloc - #define dlfree free - #define dlmalloc malloc - #define dlmemalign memalign - #define dlposix_memalign posix_memalign - #define dlrealloc realloc - #define dlrealloc_in_place realloc_in_place - #define dlvalloc valloc - #define dlpvalloc pvalloc - #define dlmallinfo mallinfo - #define dlmallopt mallopt - #define dlmalloc_trim malloc_trim - #define dlmalloc_stats malloc_stats - #define dlmalloc_usable_size malloc_usable_size - #define dlmalloc_footprint malloc_footprint - #define dlmalloc_max_footprint malloc_max_footprint - #define dlmalloc_footprint_limit malloc_footprint_limit - #define dlmalloc_set_footprint_limit malloc_set_footprint_limit - #define dlmalloc_inspect_all malloc_inspect_all - #define dlindependent_calloc independent_calloc - #define dlindependent_comalloc independent_comalloc - #define dlbulk_free bulk_free - #endif /* USE_DL_PREFIX */ +#ifndef USE_DL_PREFIX +#define dlcalloc calloc +#define dlfree free +#define dlmalloc malloc +#define dlmemalign memalign +#define dlposix_memalign posix_memalign +#define dlrealloc realloc +#define dlrealloc_in_place realloc_in_place +#define dlvalloc valloc +#define dlpvalloc pvalloc +#define dlmallinfo mallinfo +#define dlmallopt mallopt +#define dlmalloc_trim malloc_trim +#define dlmalloc_stats malloc_stats +#define dlmalloc_usable_size malloc_usable_size +#define dlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define dlmalloc_footprint_limit malloc_footprint_limit +#define dlmalloc_set_footprint_limit malloc_set_footprint_limit +#define dlmalloc_inspect_all malloc_inspect_all +#define dlindependent_calloc independent_calloc +#define dlindependent_comalloc independent_comalloc +#define dlbulk_free bulk_free +#endif /* USE_DL_PREFIX */ /* malloc(size_t n) @@ -865,7 +860,7 @@ extern "C" { maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ -DLMALLOC_EXPORT void *dlmalloc(size_t); +DLMALLOC_EXPORT void* dlmalloc(size_t); /* free(void* p) @@ -874,14 +869,14 @@ DLMALLOC_EXPORT void *dlmalloc(size_t); It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cause the current program to abort. */ -DLMALLOC_EXPORT void dlfree(void *); +DLMALLOC_EXPORT void dlfree(void*); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ -DLMALLOC_EXPORT void *dlcalloc(size_t, size_t); +DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) @@ -905,7 +900,7 @@ DLMALLOC_EXPORT void *dlcalloc(size_t, size_t); The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ -DLMALLOC_EXPORT void *dlrealloc(void *, size_t); +DLMALLOC_EXPORT void* dlrealloc(void*, size_t); /* realloc_in_place(void* p, size_t n) @@ -920,7 +915,7 @@ DLMALLOC_EXPORT void *dlrealloc(void *, size_t); Returns p if successful; otherwise null. */ -DLMALLOC_EXPORT void *dlrealloc_in_place(void *, size_t); +DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); /* memalign(size_t alignment, size_t n); @@ -934,7 +929,7 @@ DLMALLOC_EXPORT void *dlrealloc_in_place(void *, size_t); Overreliance on memalign is a sure way to fragment space. */ -DLMALLOC_EXPORT void *dlmemalign(size_t, size_t); +DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); /* int posix_memalign(void** pp, size_t alignment, size_t n); @@ -944,14 +939,14 @@ DLMALLOC_EXPORT void *dlmemalign(size_t, size_t); returns EINVAL if the alignment is not a power of two (3) fails and returns ENOMEM if memory cannot be allocated. */ -DLMALLOC_EXPORT int dlposix_memalign(void **, size_t, size_t); +DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ -DLMALLOC_EXPORT void *dlvalloc(size_t); +DLMALLOC_EXPORT void* dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) @@ -1026,7 +1021,7 @@ DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); */ DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); - #if MALLOC_INSPECT_ALL +#if MALLOC_INSPECT_ALL /* malloc_inspect_all(void(*handler)(void *start, void *end, @@ -1048,23 +1043,19 @@ DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); than 1000, you could write: static int count = 0; void count_chunks(void* start, void* end, size_t used, void* arg) { - if (used >= 1000) ++count; - } - then: malloc_inspect_all(count_chunks, NULL); malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. */ -DLMALLOC_EXPORT void dlmalloc_inspect_all(void (*handler)(void *, void *, - size_t, void *), - void *arg); +DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); - #endif /* MALLOC_INSPECT_ALL */ +#endif /* MALLOC_INSPECT_ALL */ - #if !NO_MALLINFO +#if !NO_MALLINFO /* mallinfo() Returns (by copy) a struct containing various summary statistics: @@ -1088,7 +1079,7 @@ DLMALLOC_EXPORT void dlmalloc_inspect_all(void (*handler)(void *, void *, thus be inaccurate. */ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); - #endif /* NO_MALLINFO */ +#endif /* NO_MALLINFO */ /* independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); @@ -1126,7 +1117,6 @@ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); struct Node { int item; struct Node* next; }; struct Node* build_list() { - struct Node** pool; int n = read_number_of_nodes_needed(); if (n <= 0) return 0; @@ -1138,11 +1128,9 @@ DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); pool[i]->next = pool[i+1]; free(pool); // Can now free the array (or not, if it is needed later) return first; - } - */ -DLMALLOC_EXPORT void **dlindependent_calloc(size_t, size_t, void **); +DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); @@ -1181,7 +1169,6 @@ DLMALLOC_EXPORT void **dlindependent_calloc(size_t, size_t, void **); struct Foot { ... } void send_message(char* msg) { - int msglen = strlen(msg); size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; void* chunks[3]; @@ -1191,7 +1178,6 @@ DLMALLOC_EXPORT void **dlindependent_calloc(size_t, size_t, void **); char* body = (char*)(chunks[1]); struct Foot* foot = (struct Foot*)(chunks[2]); // ... - } In general though, independent_comalloc is worth using only for @@ -1202,7 +1188,7 @@ DLMALLOC_EXPORT void **dlindependent_calloc(size_t, size_t, void **); since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ -DLMALLOC_EXPORT void **dlindependent_comalloc(size_t, size_t *, void **); +DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); /* bulk_free(void* array[], size_t n_elements) @@ -1213,14 +1199,14 @@ DLMALLOC_EXPORT void **dlindependent_comalloc(size_t, size_t *, void **); is returned. For large arrays of pointers with poor locality, it may be worthwhile to sort this array before calling bulk_free. */ -DLMALLOC_EXPORT size_t dlbulk_free(void **, size_t n_elements); +DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ -DLMALLOC_EXPORT void *dlpvalloc(size_t); +DLMALLOC_EXPORT void* dlpvalloc(size_t); /* malloc_trim(size_t pad); @@ -1243,7 +1229,7 @@ DLMALLOC_EXPORT void *dlpvalloc(size_t); Malloc_trim returns 1 if it actually released any memory, else 0. */ -DLMALLOC_EXPORT int dlmalloc_trim(size_t); +DLMALLOC_EXPORT int dlmalloc_trim(size_t); /* malloc_stats(); @@ -1264,7 +1250,7 @@ DLMALLOC_EXPORT int dlmalloc_trim(size_t); malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ -DLMALLOC_EXPORT void dlmalloc_stats(void); +DLMALLOC_EXPORT void dlmalloc_stats(void); /* malloc_usable_size(void* p); @@ -1280,9 +1266,9 @@ DLMALLOC_EXPORT void dlmalloc_stats(void); p = malloc(n); assert(malloc_usable_size(p) >= 256); */ -size_t dlmalloc_usable_size(void *); +size_t dlmalloc_usable_size(void*); -#endif /* ONLY_MSPACES */ +#endif /* ONLY_MSPACES */ #if MSPACES @@ -1290,7 +1276,7 @@ size_t dlmalloc_usable_size(void *); mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ -typedef void *mspace; +typedef void* mspace; /* create_mspace creates and returns a new independent space with the @@ -1322,8 +1308,7 @@ DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ -DLMALLOC_EXPORT mspace create_mspace_with_base(void *base, size_t capacity, - int locked); +DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); /* mspace_track_large_chunks controls whether requests for large chunks @@ -1338,11 +1323,12 @@ DLMALLOC_EXPORT mspace create_mspace_with_base(void *base, size_t capacity, */ DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); + /* mspace_malloc behaves as malloc, but operates within the given space. */ -DLMALLOC_EXPORT void *mspace_malloc(mspace msp, size_t bytes); +DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within @@ -1352,7 +1338,7 @@ DLMALLOC_EXPORT void *mspace_malloc(mspace msp, size_t bytes); free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ -DLMALLOC_EXPORT void mspace_free(mspace msp, void *mem); +DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within @@ -1363,38 +1349,33 @@ DLMALLOC_EXPORT void mspace_free(mspace msp, void *mem); realloced chunks from any space are handled by their originating spaces. */ -DLMALLOC_EXPORT void *mspace_realloc(mspace msp, void *mem, size_t newsize); +DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ -DLMALLOC_EXPORT void *mspace_calloc(mspace msp, size_t n_elements, - size_t elem_size); +DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ -DLMALLOC_EXPORT void *mspace_memalign(mspace msp, size_t alignment, - size_t bytes); +DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ -DLMALLOC_EXPORT void **mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, - void * chunks[]); +DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ -DLMALLOC_EXPORT void **mspace_independent_comalloc(mspace msp, - size_t n_elements, - size_t sizes[], - void * chunks[]); +DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the @@ -1408,18 +1389,19 @@ DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); */ DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); - #if !NO_MALLINFO + +#if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); - #endif /* NO_MALLINFO */ +#endif /* NO_MALLINFO */ /* malloc_usable_size(void* p) behaves the same as malloc_usable_size; */ -DLMALLOC_EXPORT size_t mspace_usable_size(const void *mem); +DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); /* mspace_malloc_stats behaves as malloc_stats, but reports @@ -1438,13 +1420,11 @@ DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); */ DLMALLOC_EXPORT int mspace_mallopt(int, int); -#endif /* MSPACES */ +#endif /* MSPACES */ #ifdef __cplusplus - -} /* end of extern "C" */ - -#endif /* __cplusplus */ +} /* end of extern "C" */ +#endif /* __cplusplus */ /* ======================================================================== @@ -1459,207 +1439,195 @@ DLMALLOC_EXPORT int mspace_mallopt(int, int); /*------------------------------ internal #includes ---------------------- */ #ifdef _MSC_VER - #pragma warning(disable : 4146) /* no "unsigned" warnings */ -#endif /* _MSC_VER */ +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* _MSC_VER */ #if !NO_MALLOC_STATS - #include /* for printing in malloc_stats */ -#endif /* NO_MALLOC_STATS */ +#include /* for printing in malloc_stats */ +#endif /* NO_MALLOC_STATS */ #ifndef LACKS_ERRNO_H - #include /* for MALLOC_FAILURE_ACTION */ -#endif /* LACKS_ERRNO_H */ +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ #ifdef DEBUG - #if ABORT_ON_ASSERT_FAILURE - #undef assert - #define assert(x) \ - if (!(x)) ABORT - #else /* ABORT_ON_ASSERT_FAILURE */ - #include - #endif /* ABORT_ON_ASSERT_FAILURE */ -#else /* DEBUG */ - #ifndef assert - #define assert(x) - #endif - #define DEBUG 0 -#endif /* DEBUG */ +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ #if !defined(WIN32) && !defined(LACKS_TIME_H) - #include /* for magic initialization */ -#endif /* WIN32 */ +#include /* for magic initialization */ +#endif /* WIN32 */ #ifndef LACKS_STDLIB_H - #include /* for abort() */ -#endif /* LACKS_STDLIB_H */ +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ #ifndef LACKS_STRING_H - #include /* for memset etc */ -#endif /* LACKS_STRING_H */ +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ #if USE_BUILTIN_FFS - #ifndef LACKS_STRINGS_H - #include /* for ffs */ - #endif /* LACKS_STRINGS_H */ -#endif /* USE_BUILTIN_FFS */ +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ #if HAVE_MMAP - #ifndef LACKS_SYS_MMAN_H - /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ - #if (defined(linux) && !defined(__USE_GNU)) - #define __USE_GNU 1 - #include /* for mmap */ - #undef __USE_GNU - #else - #include /* for mmap */ - #endif /* linux */ - #endif /* LACKS_SYS_MMAN_H */ - #ifndef LACKS_FCNTL_H - #include - #endif /* LACKS_FCNTL_H */ -#endif /* HAVE_MMAP */ +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ #ifndef LACKS_UNISTD_H - #include /* for sbrk, sysconf */ -#else /* LACKS_UNISTD_H */ - #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) -extern void *sbrk(ptrdiff_t); - #endif /* FreeBSD etc */ -#endif /* LACKS_UNISTD_H */ +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ /* Declarations for locking */ #if USE_LOCKS - #ifndef WIN32 - #if defined(__SVR4) && defined(__sun) /* solaris */ - #include - #elif !defined(LACKS_SCHED_H) - #include - #endif /* solaris or LACKS_SCHED_H */ - #if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || \ - !USE_SPIN_LOCKS - #include - #endif /* USE_RECURSIVE_LOCKS ... */ - #elif defined(_MSC_VER) - #ifndef _M_AMD64 - /* These are already defined on AMD64 builds */ - #ifdef __cplusplus +#ifndef WIN32 +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#elif !defined(LACKS_SCHED_H) +#include +#endif /* solaris or LACKS_SCHED_H */ +#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS +#include +#endif /* USE_RECURSIVE_LOCKS ... */ +#elif defined(_MSC_VER) +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus extern "C" { - - #endif /* __cplusplus */ -LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, - LONG Comp); +#endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); - #ifdef __cplusplus - +#ifdef __cplusplus } - - #endif /* __cplusplus */ - #endif /* _M_AMD64 */ - #pragma intrinsic(_InterlockedCompareExchange) - #pragma intrinsic(_InterlockedExchange) - #define interlockedcompareexchange _InterlockedCompareExchange - #define interlockedexchange _InterlockedExchange - #elif defined(WIN32) && defined(__GNUC__) - #define interlockedcompareexchange(a, b, c) \ - __sync_val_compare_and_swap(a, c, b) - #define interlockedexchange __sync_lock_test_and_set - #endif /* Win32 */ -#else /* USE_LOCKS */ -#endif /* USE_LOCKS */ +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif defined(WIN32) && defined(__GNUC__) +#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) +#define interlockedexchange __sync_lock_test_and_set +#endif /* Win32 */ +#else /* USE_LOCKS */ +#endif /* USE_LOCKS */ #ifndef LOCK_AT_FORK - #define LOCK_AT_FORK 0 +#define LOCK_AT_FORK 0 #endif /* Declarations for bit scanning on win32 */ -#if defined(_MSC_VER) && _MSC_VER >= 1300 - #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ - #ifdef __cplusplus +#if defined(_MSC_VER) && _MSC_VER>=1300 +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus extern "C" { - - #endif /* __cplusplus */ +#endif /* __cplusplus */ unsigned char _BitScanForward(unsigned long *index, unsigned long mask); unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); - #ifdef __cplusplus - +#ifdef __cplusplus } +#endif /* __cplusplus */ - #endif /* __cplusplus */ - - #define BitScanForward _BitScanForward - #define BitScanReverse _BitScanReverse - #pragma intrinsic(_BitScanForward) - #pragma intrinsic(_BitScanReverse) - #endif /* BitScanForward */ -#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ #ifndef WIN32 - #ifndef malloc_getpagesize - #ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ - #ifndef _SC_PAGE_SIZE - #define _SC_PAGE_SIZE _SC_PAGESIZE - #endif - #endif - #ifdef _SC_PAGE_SIZE - #define malloc_getpagesize sysconf(_SC_PAGE_SIZE) - #else - #if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) -extern size_t getpagesize(); - #define malloc_getpagesize getpagesize() - #else - #ifdef WIN32 /* use supplied emulation of getpagesize */ - #define malloc_getpagesize getpagesize() - #else - #ifndef LACKS_SYS_PARAM_H - #include - #endif - #ifdef EXEC_PAGESIZE - #define malloc_getpagesize EXEC_PAGESIZE - #else - #ifdef NBPG - #ifndef CLSIZE - #define malloc_getpagesize NBPG - #else - #define malloc_getpagesize (NBPG * CLSIZE) - #endif - #else - #ifdef NBPC - #define malloc_getpagesize NBPC - #else - #ifdef PAGESIZE - #define malloc_getpagesize PAGESIZE - #else /* just guess */ - #define malloc_getpagesize ((size_t)4096U) - #endif - #endif - #endif - #endif - #endif - #endif - #endif - #endif +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif #endif /* ------------------- size_t and alignment properties -------------------- */ /* The byte and bit size of a size_t */ -#define SIZE_T_SIZE (sizeof(size_t)) -#define SIZE_T_BITSIZE (sizeof(size_t) << 3) +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) /* Some constants coerced to size_t */ /* Annoying but necessary to avoid errors on some platforms */ -#define SIZE_T_ZERO ((size_t)0) -#define SIZE_T_ONE ((size_t)1) -#define SIZE_T_TWO ((size_t)2) -#define SIZE_T_FOUR ((size_t)4) -#define TWO_SIZE_T_SIZES (SIZE_T_SIZE << 1) -#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE << 2) -#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES + TWO_SIZE_T_SIZES) -#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) /* The bit mask value corresponding to MALLOC_ALIGNMENT */ -#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) /* True if address a has acceptable alignment */ -#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) /* the number of bytes to offset an address to align it */ -#define align_offset(A) \ - ((((size_t)(A)&CHUNK_ALIGN_MASK) == 0) \ - ? 0 \ - : ((MALLOC_ALIGNMENT - ((size_t)(A)&CHUNK_ALIGN_MASK)) & \ - CHUNK_ALIGN_MASK)) +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) /* -------------------------- MMAP preliminaries ------------------------- */ @@ -1669,202 +1637,193 @@ extern size_t getpagesize(); using so many "#if"s. */ + /* MORECORE and MMAP must return MFAIL on failure */ -#define MFAIL ((void *)(MAX_SIZE_T)) -#define CMFAIL ((char *)(MFAIL)) /* defined for convenience */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ #if HAVE_MMAP - #ifndef WIN32 - #define MMAP_PROT (PROT_READ | PROT_WRITE) - #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) - #define MAP_ANONYMOUS MAP_ANON - #endif /* MAP_ANON */ - #ifdef MAP_ANONYMOUS - - #define MMAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS) +#ifndef WIN32 +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS -static FORCEINLINE void *unixmmap(size_t size) { +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) - void *result; +static FORCEINLINE void* unixmmap(size_t size) { + void* result; result = mmap(0, size, MMAP_PROT, MMAP_FLAGS, -1, 0); - if (result == MFAIL) return MFAIL; + if (result == MFAIL) + return MFAIL; return result; - } -static FORCEINLINE int unixmunmap(void *ptr, size_t size) { - +static FORCEINLINE int unixmunmap(void* ptr, size_t size) { int result; result = munmap(ptr, size); - if (result != 0) return result; + if (result != 0) + return result; return result; - } - #define MMAP_DEFAULT(s) unixmmap(s) - #define MUNMAP_DEFAULT(a, s) unixmunmap((a), (s)) +#define MMAP_DEFAULT(s) unixmmap(s) +#define MUNMAP_DEFAULT(a, s) unixmunmap((a), (s)) - #else /* MAP_ANONYMOUS */ - /* - Nearly all versions of mmap support MAP_ANONYMOUS, so the following - is unlikely to be needed, but is supplied just in case. - */ - #define MMAP_FLAGS (MAP_PRIVATE) -static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ - #define MMAP_DEFAULT(s) \ - ((dev_zero_fd < 0) \ - ? (dev_zero_fd = open("/dev/zero", O_RDWR), \ - mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) \ - : mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) - #define MUNMAP_DEFAULT(a, s) munmap((a), (s)) - #endif /* MAP_ANONYMOUS */ +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) +#endif /* MAP_ANONYMOUS */ - #define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) +#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) - #else /* WIN32 */ +#else /* WIN32 */ /* Win32 MMAP via VirtualAlloc */ -static FORCEINLINE void *win32mmap(size_t size) { - - void *ptr; +static FORCEINLINE void* win32mmap(size_t size) { + void* ptr; - ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - if (ptr == 0) return MFAIL; + ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + if (ptr == 0) + return MFAIL; return ptr; - } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ -static FORCEINLINE void *win32direct_mmap(size_t size) { - - void *ptr; +static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr; - ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, - PAGE_READWRITE); - if (ptr == 0) return MFAIL; + ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + if (ptr == 0) + return MFAIL; return ptr; - } /* This function supports releasing coalesed segments */ -static FORCEINLINE int win32munmap(void *ptr, size_t size) { - +static FORCEINLINE int win32munmap(void* ptr, size_t size) { MEMORY_BASIC_INFORMATION minfo; - char *cptr = (char *)ptr; + char* cptr = (char*)ptr; while (size) { - - if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) return -1; + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || minfo.State != MEM_COMMIT || minfo.RegionSize > size) return -1; - if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; cptr += minfo.RegionSize; size -= minfo.RegionSize; - } return 0; - } - #define MMAP_DEFAULT(s) win32mmap(s) - #define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) - #define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) - #endif /* WIN32 */ -#endif /* HAVE_MMAP */ +#define MMAP_DEFAULT(s) win32mmap(s) +#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ #if HAVE_MREMAP - #ifndef WIN32 - -static FORCEINLINE void *dlmremap(void *old_address, size_t old_size, - size_t new_size, int flags) { +#ifndef WIN32 - void *result; +static FORCEINLINE void* dlmremap(void* old_address, size_t old_size, size_t new_size, int flags) { + void* result; result = mremap(old_address, old_size, new_size, flags); - if (result == MFAIL) return MFAIL; + if (result == MFAIL) + return MFAIL; return result; - } - #define MREMAP_DEFAULT(addr, osz, nsz, mv) \ - dlmremap((addr), (osz), (nsz), (mv)) - #endif /* WIN32 */ -#endif /* HAVE_MREMAP */ +#define MREMAP_DEFAULT(addr, osz, nsz, mv) dlmremap((addr), (osz), (nsz), (mv)) +#endif /* WIN32 */ +#endif /* HAVE_MREMAP */ /** * Define CALL_MORECORE */ #if HAVE_MORECORE - #ifdef MORECORE - #define CALL_MORECORE(S) MORECORE(S) - #else /* MORECORE */ - #define CALL_MORECORE(S) MORECORE_DEFAULT(S) - #endif /* MORECORE */ -#else /* HAVE_MORECORE */ - #define CALL_MORECORE(S) MFAIL -#endif /* HAVE_MORECORE */ + #ifdef MORECORE + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ +#else /* HAVE_MORECORE */ + #define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ /** * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP */ #if HAVE_MMAP - #define USE_MMAP_BIT (SIZE_T_ONE) - - #ifdef MMAP - #define CALL_MMAP(s) MMAP(s) - #else /* MMAP */ - #define CALL_MMAP(s) MMAP_DEFAULT(s) - #endif /* MMAP */ - #ifdef MUNMAP - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) - #else /* MUNMAP */ - #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) - #endif /* MUNMAP */ - #ifdef DIRECT_MMAP - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #else /* DIRECT_MMAP */ - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) - #endif /* DIRECT_MMAP */ -#else /* HAVE_MMAP */ - #define USE_MMAP_BIT (SIZE_T_ZERO) - - #define MMAP(s) MFAIL - #define MUNMAP(a, s) (-1) - #define DIRECT_MMAP(s) MFAIL - #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) - #define CALL_MMAP(s) MMAP(s) - #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) -#endif /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ONE) + + #ifdef MMAP + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ + #ifdef MUNMAP + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ + #ifdef DIRECT_MMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ /** * Define CALL_MREMAP */ #if HAVE_MMAP && HAVE_MREMAP - #ifdef MREMAP - #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) - #else /* MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) \ - MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) - #endif /* MREMAP */ -#else /* HAVE_MMAP && HAVE_MREMAP */ - #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL -#endif /* HAVE_MMAP && HAVE_MREMAP */ + #ifdef MREMAP + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ /* mstate bit set if continguous morecore disabled or failed */ #define USE_NONCONTIGUOUS_BIT (4U) /* segment bit set in create_mspace_with_base */ -#define EXTERN_BIT (8U) +#define EXTERN_BIT (8U) + /* --------------------------- Lock preliminaries ------------------------ */ @@ -1897,284 +1856,247 @@ static FORCEINLINE void *dlmremap(void *old_address, size_t old_size, */ #if !USE_LOCKS - #define USE_LOCK_BIT (0U) - #define INITIAL_LOCK(l) (0) - #define DESTROY_LOCK(l) (0) - #define ACQUIRE_MALLOC_GLOBAL_LOCK() - #define RELEASE_MALLOC_GLOBAL_LOCK() +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) (0) +#define DESTROY_LOCK(l) (0) +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() #else - #if USE_LOCKS > 1 - /* ----------------------- User-defined locks ------------------------ */ - /* Define your own lock implementation here */ - /* #define INITIAL_LOCK(lk) ... */ - /* #define DESTROY_LOCK(lk) ... */ - /* #define ACQUIRE_LOCK(lk) ... */ - /* #define RELEASE_LOCK(lk) ... */ - /* #define TRY_LOCK(lk) ... */ - /* static MLOCK_T malloc_global_mutex = ... */ - - #elif USE_SPIN_LOCKS - - /* First, define CAS_LOCK and CLEAR_LOCK on ints */ - /* Note CAS_LOCK defined to return 0 on success */ - - #if defined(__GNUC__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) - #define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) - #define CLEAR_LOCK(sl) __sync_lock_release(sl) - - #elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ + +#elif USE_SPIN_LOCKS + +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) /* Custom spin locks for older gcc on x86 */ static FORCEINLINE int x86_cas_lock(int *sl) { - int ret; int val = 1; int cmp = 0; - __asm__ __volatile__("lock; cmpxchgl %1, %2" - : "=a"(ret) - : "r"(val), "m"(*(sl)), "0"(cmp) - : "memory", "cc"); + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); return ret; - } -static FORCEINLINE void x86_clear_lock(int *sl) { - +static FORCEINLINE void x86_clear_lock(int* sl) { assert(*sl != 0); int prev = 0; int ret; - __asm__ __volatile__("lock; xchgl %0, %1" - : "=r"(ret) - : "m"(*(sl)), "0"(prev) - : "memory"); - + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); } - #define CAS_LOCK(sl) x86_cas_lock(sl) - #define CLEAR_LOCK(sl) x86_clear_lock(sl) - - #else /* Win32 MSC */ - #define CAS_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)1) - #define CLEAR_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)0) - - #endif /* ... gcc spins locks ... */ - - /* How to yield for a spin lock */ - #define SPINS_PER_YIELD 63 - #if defined(_MSC_VER) - #define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ - #define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) - #elif defined(__SVR4) && defined(__sun) /* solaris */ - #define SPIN_LOCK_YIELD thr_yield(); - #elif !defined(LACKS_SCHED_H) - #define SPIN_LOCK_YIELD sched_yield(); - #else - #define SPIN_LOCK_YIELD - #endif /* ... yield ... */ - - #if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange((volatile LONG *)sl, (LONG)0) + +#endif /* ... gcc spins locks ... */ + +/* How to yield for a spin lock */ +#define SPINS_PER_YIELD 63 +#if defined(_MSC_VER) +#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ +#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) +#elif defined (__SVR4) && defined (__sun) /* solaris */ +#define SPIN_LOCK_YIELD thr_yield(); +#elif !defined(LACKS_SCHED_H) +#define SPIN_LOCK_YIELD sched_yield(); +#else +#define SPIN_LOCK_YIELD +#endif /* ... yield ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 /* Plain spin locks use single word (embedded in malloc_states) */ static int spin_acquire_lock(int *sl) { - int spins = 0; while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { - - if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } - + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } } - return 0; - } - #define MLOCK_T int - #define TRY_LOCK(sl) !CAS_LOCK(sl) - #define RELEASE_LOCK(sl) CLEAR_LOCK(sl) - #define ACQUIRE_LOCK(sl) (CAS_LOCK(sl) ? spin_acquire_lock(sl) : 0) - #define INITIAL_LOCK(sl) (*sl = 0) - #define DESTROY_LOCK(sl) (0) +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) +#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) static MLOCK_T malloc_global_mutex = 0; - #else /* USE_RECURSIVE_LOCKS */ - /* types for lock owners */ - #ifdef WIN32 - #define THREAD_ID_T DWORD - #define CURRENT_THREAD GetCurrentThreadId() - #define EQ_OWNER(X, Y) ((X) == (Y)) - #else - /* - Note: the following assume that pthread_t is a type that can be - initialized to (casted) zero. If this is not the case, you will need - to somehow redefine these or not use spin locks. - */ - #define THREAD_ID_T pthread_t - #define CURRENT_THREAD pthread_self() - #define EQ_OWNER(X, Y) pthread_equal(X, Y) - #endif +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else +/* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. +*/ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) +#endif struct malloc_recursive_lock { - - int sl; + int sl; unsigned int c; - THREAD_ID_T threadid; - + THREAD_ID_T threadid; }; - #define MLOCK_T struct malloc_recursive_lock -static MLOCK_T malloc_global_mutex = {0, 0, (THREAD_ID_T)0}; +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { - assert(lk->sl != 0); - if (--lk->c == 0) { CLEAR_LOCK(&lk->sl); } - + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); + } } static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { - THREAD_ID_T mythreadid = CURRENT_THREAD; - int spins = 0; + int spins = 0; for (;;) { - if (*((volatile int *)(&lk->sl)) == 0) { - if (!CAS_LOCK(&lk->sl)) { - lk->threadid = mythreadid; lk->c = 1; return 0; - } - - } else if (EQ_OWNER(lk->threadid, mythreadid)) { - + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { ++lk->c; return 0; - } - - if ((++spins & SPINS_PER_YIELD) == 0) { SPIN_LOCK_YIELD; } - + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } } - } static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { - THREAD_ID_T mythreadid = CURRENT_THREAD; if (*((volatile int *)(&lk->sl)) == 0) { - if (!CAS_LOCK(&lk->sl)) { - lk->threadid = mythreadid; lk->c = 1; return 1; - } - - } else if (EQ_OWNER(lk->threadid, mythreadid)) { - + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { ++lk->c; return 1; - } - return 0; - } - #define RELEASE_LOCK(lk) recursive_release_lock(lk) - #define TRY_LOCK(lk) recursive_try_lock(lk) - #define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) - #define INITIAL_LOCK(lk) \ - ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) - #define DESTROY_LOCK(lk) (0) - #endif /* USE_RECURSIVE_LOCKS */ - - #elif defined(WIN32) /* Win32 critical sections */ - #define MLOCK_T CRITICAL_SECTION - #define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) - #define RELEASE_LOCK(lk) LeaveCriticalSection(lk) - #define TRY_LOCK(lk) TryEnterCriticalSection(lk) - #define INITIAL_LOCK(lk) \ - (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000 | 4000)) - #define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) - #define NEED_GLOBAL_LOCK_INIT - -static MLOCK_T malloc_global_mutex; +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; static volatile LONG malloc_global_mutex_status; /* Use spin loop to initialize global lock */ static void init_malloc_global_mutex() { - for (;;) { - long stat = malloc_global_mutex_status; - if (stat > 0) return; + if (stat > 0) + return; /* transition to < 0 while initializing, then to > 0) */ - if (stat == 0 && interlockedcompareexchange(&malloc_global_mutex_status, - (LONG)-1, (LONG)0) == 0) { - + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { InitializeCriticalSection(&malloc_global_mutex); interlockedexchange(&malloc_global_mutex_status, (LONG)1); return; - } - SleepEx(0, FALSE); - } - } - #else /* pthreads-based locks */ - #define MLOCK_T pthread_mutex_t - #define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) - #define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) - #define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) - #define INITIAL_LOCK(lk) pthread_init_lock(lk) - #define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) - #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && \ - defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) /* Cope with old-style linux recursive lock initialization by adding */ /* skipped internal declaration from pthread.h */ -extern int pthread_mutexattr_setkind_np __P((pthread_mutexattr_t * __attr, - int __kind)); - #define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP - #define pthread_mutexattr_settype(x, y) pthread_mutexattr_setkind_np(x, y) - #endif /* USE_RECURSIVE_LOCKS ... */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; -static int pthread_init_lock(MLOCK_T *lk) { - +static int pthread_init_lock (MLOCK_T *lk) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) return 1; - #if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; - #endif +#endif if (pthread_mutex_init(lk, &attr)) return 1; if (pthread_mutexattr_destroy(&attr)) return 1; return 0; - } - #endif /* ... lock types ... */ +#endif /* ... lock types ... */ - /* Common code for all lock types */ - #define USE_LOCK_BIT (2U) +/* Common code for all lock types */ +#define USE_LOCK_BIT (2U) - #ifndef ACQUIRE_MALLOC_GLOBAL_LOCK - #define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); - #endif +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif - #ifndef RELEASE_MALLOC_GLOBAL_LOCK - #define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); - #endif +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif -#endif /* USE_LOCKS */ +#endif /* USE_LOCKS */ /* ----------------------- Chunk representations ------------------------ */ @@ -2314,56 +2236,56 @@ static int pthread_init_lock(MLOCK_T *lk) { */ struct malloc_chunk { - - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk *fd; /* double links -- used only if free. */ - struct malloc_chunk *bk; - + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; }; typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk *mchunkptr; -typedef struct malloc_chunk *sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ------------------- Chunks sizes and alignments ----------------------- */ -#define MCHUNK_SIZE (sizeof(mchunk)) +#define MCHUNK_SIZE (sizeof(mchunk)) #if FOOTERS - #define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) -#else /* FOOTERS */ - #define CHUNK_OVERHEAD (SIZE_T_SIZE) -#endif /* FOOTERS */ +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ /* MMapped chunks need a second word of overhead ... */ #define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) /* ... and additional padding for fake next-chunk at foot */ -#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) /* The smallest size we can malloc is an aligned minimal chunk */ -#define MIN_CHUNK_SIZE ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* conversion from malloc headers to user pointers, and back */ -#define chunk2mem(p) ((void *)((char *)(p) + TWO_SIZE_T_SIZES)) -#define mem2chunk(mem) ((mchunkptr)((char *)(mem)-TWO_SIZE_T_SIZES)) +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) /* chunk associated with aligned address A */ -#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) /* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) -#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) /* pad request bytes into a usable size */ #define pad_request(req) \ - (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request, checking for minimum (but not maximum) */ #define request2size(req) \ - (((req) < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(req)) + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + /* ------------------ Operations on head and foot fields ----------------- */ @@ -2375,60 +2297,61 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ FLAG4_BIT is not used by this malloc, but might be useful in extensions. */ -#define PINUSE_BIT (SIZE_T_ONE) -#define CINUSE_BIT (SIZE_T_TWO) -#define FLAG4_BIT (SIZE_T_FOUR) -#define INUSE_BITS (PINUSE_BIT | CINUSE_BIT) -#define FLAG_BITS (PINUSE_BIT | CINUSE_BIT | FLAG4_BIT) +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) /* Head value for fenceposts */ -#define FENCEPOST_HEAD (INUSE_BITS | SIZE_T_SIZE) +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) /* extraction of fields from head words */ -#define cinuse(p) ((p)->head & CINUSE_BIT) -#define pinuse(p) ((p)->head & PINUSE_BIT) -#define flag4inuse(p) ((p)->head & FLAG4_BIT) -#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) -#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) -#define chunksize(p) ((p)->head & ~(FLAG_BITS)) +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) -#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) -#define set_flag4(p) ((p)->head |= FLAG4_BIT) -#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) /* Treat space at ptr +/- offset as a chunk */ -#define chunk_plus_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) -#define chunk_minus_offset(p, s) ((mchunkptr)(((char *)(p)) - (s))) +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) /* Ptr to next or previous physical malloc_chunk. */ -#define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->head & ~FLAG_BITS))) -#define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_foot))) +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) /* extract next chunk's pinuse bit */ -#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) /* Get/set size at footer */ -#define get_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot) -#define set_foot(p, s) (((mchunkptr)((char *)(p) + (s)))->prev_foot = (s)) +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) /* Set size, pinuse bit, and foot */ -#define set_size_and_pinuse_of_free_chunk(p, s) \ - ((p)->head = (s | PINUSE_BIT), set_foot(p, s)) +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) /* Set size, pinuse bit, foot, and clear next pinuse */ -#define set_free_with_pinuse(p, s, n) \ +#define set_free_with_pinuse(p, s, n)\ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) /* Get the internal overhead associated with chunk p */ -#define overhead_for(p) (is_mmapped(p) ? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) /* Return true if malloced space is not necessarily cleared */ #if MMAP_CLEARS - #define calloc_must_clear(p) (!is_mmapped(p)) -#else /* MMAP_CLEARS */ - #define calloc_must_clear(p) (1) -#endif /* MMAP_CLEARS */ +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ /* ---------------------- Overlaid data structures ----------------------- */ @@ -2522,25 +2445,23 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct malloc_tree_chunk { - /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; - struct malloc_tree_chunk *fd; - struct malloc_tree_chunk *bk; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; - struct malloc_tree_chunk *child[2]; - struct malloc_tree_chunk *parent; + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; bindex_t index; - }; typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk *tchunkptr; -typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ /* A little helper macro for trees */ -#define leftmost_child(t) ((t)->child[0] != 0 ? (t)->child[0] : (t)->child[1]) +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) /* ----------------------------- Segments -------------------------------- */ @@ -2600,19 +2521,17 @@ typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ */ struct malloc_segment { - - char * base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment *next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ - + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ }; -#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) -#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) typedef struct malloc_segment msegment; -typedef struct malloc_segment *msegmentptr; +typedef struct malloc_segment* msegmentptr; /* ---------------------------- malloc_state ----------------------------- */ @@ -2702,43 +2621,41 @@ typedef struct malloc_segment *msegmentptr; */ /* Bin types, widths and sizes */ -#define NSMALLBINS (32U) -#define NTREEBINS (32U) -#define SMALLBIN_SHIFT (3U) -#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) -#define TREEBIN_SHIFT (8U) -#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) -#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) struct malloc_state { - - binmap_t smallmap; - binmap_t treemap; - size_t dvsize; - size_t topsize; - char * least_addr; - mchunkptr dv; - mchunkptr top; - size_t trim_check; - size_t release_checks; - size_t magic; - mchunkptr smallbins[(NSMALLBINS + 1) * 2]; - tbinptr treebins[NTREEBINS]; - size_t footprint; - size_t max_footprint; - size_t footprint_limit; /* zero means no limit */ - flag_t mflags; + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ + flag_t mflags; #if USE_LOCKS - MLOCK_T mutex; /* locate lock among fields that rarely change */ -#endif /* USE_LOCKS */ - msegment seg; - void * extp; /* Unused but available for extensions */ - size_t exts; - + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; }; -typedef struct malloc_state *mstate; +typedef struct malloc_state* mstate; /* ------------- Global malloc_state and malloc_params ------------------- */ @@ -2750,14 +2667,12 @@ typedef struct malloc_state *mstate; */ struct malloc_params { - size_t magic; size_t page_size; size_t granularity; size_t mmap_threshold; size_t trim_threshold; flag_t default_mflags; - }; static struct malloc_params mparams; @@ -2769,108 +2684,106 @@ static struct malloc_params mparams; /* The global malloc_state used for all non-"mspace" calls */ static struct malloc_state _gm_; - #define gm (&_gm_) - #define is_global(M) ((M) == &_gm_) +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) -#endif /* !ONLY_MSPACES */ +#endif /* !ONLY_MSPACES */ -#define is_initialized(M) ((M)->top != 0) +#define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ /* Operations on mflags */ -#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) -#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) #if USE_LOCKS - #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) #else - #define disable_lock(M) +#define disable_lock(M) #endif -#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) -#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) #if HAVE_MMAP - #define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) #else - #define disable_mmap(M) +#define disable_mmap(M) #endif -#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) -#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) -#define set_lock(M, L) \ - ((M)->mflags = \ - (L) ? ((M)->mflags | USE_LOCK_BIT) : ((M)->mflags & ~USE_LOCK_BIT)) +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) /* page-align a size */ -#define page_align(S) \ - (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) /* granularity-align a size */ -#define granularity_align(S) \ - (((S) + (mparams.granularity - SIZE_T_ONE)) & \ - ~(mparams.granularity - SIZE_T_ONE)) +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + /* For mmap, use granularity alignment on windows, else page-align */ #ifdef WIN32 - #define mmap_align(S) granularity_align(S) +#define mmap_align(S) granularity_align(S) #else - #define mmap_align(S) page_align(S) +#define mmap_align(S) page_align(S) #endif /* For sys_alloc, enough padding to ensure can malloc request on success */ #define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) -#define is_page_aligned(S) \ - (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) -#define is_granularity_aligned(S) \ - (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) /* True if segment S holds address A */ -#define segment_holds(S, A) \ - ((char *)(A) >= S->base && (char *)(A) < S->base + S->size) +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) /* Return segment holding given address */ -static msegmentptr segment_holding(mstate m, char *addr) { - +static msegmentptr segment_holding(mstate m, char* addr) { msegmentptr sp = &m->seg; for (;;) { - - if (addr >= sp->base && addr < sp->base + sp->size) return sp; - if ((sp = sp->next) == 0) return 0; - + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; } - } /* Return true if segment contains a segment link */ static int has_segment_link(mstate m, msegmentptr ss) { - msegmentptr sp = &m->seg; for (;;) { - - if ((char *)sp >= ss->base && (char *)sp < ss->base + ss->size) return 1; - if ((sp = sp->next) == 0) return 0; - + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; } - } #ifndef MORECORE_CANNOT_TRIM - #define should_trim(M, s) ((s) > (M)->trim_check) -#else /* MORECORE_CANNOT_TRIM */ - #define should_trim(M, s) (0) -#endif /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ /* TOP_FOOT_SIZE is padding at the end of a segment, including space that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ -#define TOP_FOOT_SIZE \ - (align_offset(chunk2mem(0)) + pad_request(sizeof(struct malloc_segment)) + \ - MIN_CHUNK_SIZE) +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + /* ------------------------------- Hooks -------------------------------- */ @@ -2881,24 +2794,19 @@ static int has_segment_link(mstate m, msegmentptr ss) { */ #if USE_LOCKS - #define PREACTION(M) ((use_lock(M)) ? ACQUIRE_LOCK(&(M)->mutex) : 0) - #define POSTACTION(M) \ - { \ - \ - if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); \ - \ - } -#else /* USE_LOCKS */ +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ - #ifndef PREACTION - #define PREACTION(M) (0) - #endif /* PREACTION */ +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ - #ifndef POSTACTION - #define POSTACTION(M) - #endif /* POSTACTION */ +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ -#endif /* USE_LOCKS */ +#endif /* USE_LOCKS */ /* CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. @@ -2916,180 +2824,164 @@ int malloc_corruption_error_count; /* default corruption action */ static void reset_on_error(mstate m); - #define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) - #define USAGE_ERROR_ACTION(m, p) +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) -#else /* PROCEED_ON_ERROR */ +#else /* PROCEED_ON_ERROR */ - #ifndef CORRUPTION_ERROR_ACTION - #define CORRUPTION_ERROR_ACTION(m) ABORT - #endif /* CORRUPTION_ERROR_ACTION */ +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ - #ifndef USAGE_ERROR_ACTION - #define USAGE_ERROR_ACTION(m, p) ABORT - #endif /* USAGE_ERROR_ACTION */ +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ -#endif /* PROCEED_ON_ERROR */ /* -------------------------- Debugging setup ---------------------------- */ -#if !DEBUG +#if ! DEBUG - #define check_free_chunk(M, P) - #define check_inuse_chunk(M, P) - #define check_malloced_chunk(M, P, N) - #define check_mmapped_chunk(M, P) - #define check_malloc_state(M) - #define check_top_chunk(M, P) +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) -#else /* DEBUG */ - #define check_free_chunk(M, P) do_check_free_chunk(M, P) - #define check_inuse_chunk(M, P) do_check_inuse_chunk(M, P) - #define check_top_chunk(M, P) do_check_top_chunk(M, P) - #define check_malloced_chunk(M, P, N) do_check_malloced_chunk(M, P, N) - #define check_mmapped_chunk(M, P) do_check_mmapped_chunk(M, P) - #define check_malloc_state(M) do_check_malloc_state(M) +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) static void do_check_any_chunk(mstate m, mchunkptr p); static void do_check_top_chunk(mstate m, mchunkptr p); static void do_check_mmapped_chunk(mstate m, mchunkptr p); static void do_check_inuse_chunk(mstate m, mchunkptr p); static void do_check_free_chunk(mstate m, mchunkptr p); -static void do_check_malloced_chunk(mstate m, void *mem, size_t s); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); static void do_check_tree(mstate m, tchunkptr t); static void do_check_treebin(mstate m, bindex_t i); static void do_check_smallbin(mstate m, bindex_t i); static void do_check_malloc_state(mstate m); static int bin_find(mstate m, mchunkptr x); static size_t traverse_and_check(mstate m); -#endif /* DEBUG */ +#endif /* DEBUG */ /* ---------------------------- Indexing Bins ---------------------------- */ -#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) -#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) -#define small_index2size(i) ((i) << SMALLBIN_SHIFT) -#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) /* addressing by index. See above about smallbin repositioning */ -#define smallbin_at(M, i) ((sbinptr)((char *)&((M)->smallbins[(i) << 1]))) -#define treebin_at(M, i) (&((M)->treebins[i])) +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) /* assign tree index for size S to variable I. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - #define compute_tree_index(S, I) \ - { \ - \ - unsigned int X = S >> TREEBIN_SHIFT; \ - if (X == 0) \ - I = 0; \ - else if (X > 0xFFFF) \ - I = NTREEBINS - 1; \ - else { \ - \ - unsigned int K = (unsigned)sizeof(X) * __CHAR_BIT__ - 1 - \ - (unsigned)__builtin_clz(X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ - \ - } \ - \ - } +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} -#elif defined(__INTEL_COMPILER) - #define compute_tree_index(S, I) \ - { \ - \ - size_t X = S >> TREEBIN_SHIFT; \ - if (X == 0) \ - I = 0; \ - else if (X > 0xFFFF) \ - I = NTREEBINS - 1; \ - else { \ - \ - unsigned int K = _bit_scan_reverse(X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ - \ - } \ - \ - } +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - #define compute_tree_index(S, I) \ - { \ - \ - size_t X = S >> TREEBIN_SHIFT; \ - if (X == 0) \ - I = 0; \ - else if (X > 0xFFFF) \ - I = NTREEBINS - 1; \ - else { \ - \ - unsigned int K; \ - _BitScanReverse((DWORD *)&K, (DWORD)X); \ - I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1))); \ - \ - } \ - \ - } +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, (DWORD) X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} -#else /* GNUC */ - #define compute_tree_index(S, I) \ - { \ - \ - size_t X = S >> TREEBIN_SHIFT; \ - if (X == 0) \ - I = 0; \ - else if (X > 0xFFFF) \ - I = NTREEBINS - 1; \ - else { \ - \ - unsigned int Y = (unsigned int)X; \ - unsigned int N = ((Y - 0x100) >> 16) & 8; \ - unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4; \ - N += K; \ - N += K = (((Y <<= K) - 0x4000) >> 16) & 2; \ - K = 14 - N + ((Y <<= K) >> 15); \ - I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)); \ - \ - } \ - \ - } -#endif /* GNUC */ +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ /* Bit representing maximum resolved size in a treebin at i */ #define bit_for_tree_index(i) \ - (i == NTREEBINS - 1) ? (SIZE_T_BITSIZE - 1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) /* Shift placing maximum resolved bit in a treebin at i as sign bit */ #define leftshift_for_tree_index(i) \ - ((i == NTREEBINS - 1) \ - ? 0 \ - : ((SIZE_T_BITSIZE - SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) /* The size of the smallest chunk held in bin with index i */ -#define minsize_for_tree_index(i) \ - ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ - (((size_t)((i)&SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + /* ------------------------ Operations on bin maps ----------------------- */ /* bit corresponding to given index */ -#define idx2bit(i) ((binmap_t)(1) << (i)) +#define idx2bit(i) ((binmap_t)(1) << (i)) /* Mark/Clear bits with given index */ -#define mark_smallmap(M, i) ((M)->smallmap |= idx2bit(i)) -#define clear_smallmap(M, i) ((M)->smallmap &= ~idx2bit(i)) -#define smallmap_is_marked(M, i) ((M)->smallmap & idx2bit(i)) +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) -#define mark_treemap(M, i) ((M)->treemap |= idx2bit(i)) -#define clear_treemap(M, i) ((M)->treemap &= ~idx2bit(i)) -#define treemap_is_marked(M, i) ((M)->treemap & idx2bit(i)) +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) /* isolate the least set bit of a bitmap */ -#define least_bit(x) ((x) & -(x)) +#define least_bit(x) ((x) & -(x)) /* mask with all bits to left of least bit of x on */ -#define left_bits(x) ((x << 1) | -(x << 1)) +#define left_bits(x) ((x<<1) | -(x<<1)) /* mask with all bits to left of or equal to least bit of x on */ #define same_or_left_bits(x) ((x) | -(x)) @@ -3097,58 +2989,46 @@ static size_t traverse_and_check(mstate m); /* index corresponding to given bit. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - #define compute_bit2idx(X, I) \ - { \ - \ - unsigned int J; \ - J = __builtin_ctz(X); \ - I = (bindex_t)J; \ - \ - } +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = __builtin_ctz(X); \ + I = (bindex_t)J;\ +} -#elif defined(__INTEL_COMPILER) - #define compute_bit2idx(X, I) \ - { \ - \ - unsigned int J; \ - J = _bit_scan_forward(X); \ - I = (bindex_t)J; \ - \ - } +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} -#elif defined(_MSC_VER) && _MSC_VER >= 1300 - #define compute_bit2idx(X, I) \ - { \ - \ - unsigned int J; \ - _BitScanForward((DWORD *)&J, X); \ - I = (bindex_t)J; \ - \ - } +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} #elif USE_BUILTIN_FFS - #define compute_bit2idx(X, I) I = ffs(X) - 1 +#define compute_bit2idx(X, I) I = ffs(X)-1 #else - #define compute_bit2idx(X, I) \ - { \ - \ - unsigned int Y = X - 1; \ - unsigned int K = Y >> (16 - 4) & 16; \ - unsigned int N = K; \ - Y >>= K; \ - N += K = Y >> (8 - 3) & 8; \ - Y >>= K; \ - N += K = Y >> (4 - 2) & 4; \ - Y >>= K; \ - N += K = Y >> (2 - 1) & 2; \ - Y >>= K; \ - N += K = Y >> (1 - 0) & 1; \ - Y >>= K; \ - I = (bindex_t)(N + Y); \ - \ - } -#endif /* GNUC */ +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + /* ----------------------- Runtime Check Support ------------------------- */ @@ -3179,141 +3059,121 @@ static size_t traverse_and_check(mstate m); */ #if !INSECURE - /* Check if address a is at least as high as any from MORECORE or MMAP */ - #define ok_address(M, a) ((char *)(a) >= (M)->least_addr) - /* Check if address of next chunk n is higher than base chunk p */ - #define ok_next(p, n) ((char *)(p) < (char *)(n)) - /* Check if p has inuse status */ - #define ok_inuse(p) is_inuse(p) - /* Check if p has its pinuse bit on */ - #define ok_pinuse(p) pinuse(p) - -#else /* !INSECURE */ - #define ok_address(M, a) (1) - #define ok_next(b, n) (1) - #define ok_inuse(p) (1) - #define ok_pinuse(p) (1) -#endif /* !INSECURE */ +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ #if (FOOTERS && !INSECURE) - /* Check if (alleged) mstate m has expected magic field */ - #define ok_magic(M) ((M)->magic == mparams.magic) -#else /* (FOOTERS && !INSECURE) */ - #define ok_magic(M) (1) -#endif /* (FOOTERS && !INSECURE) */ +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ /* In gcc, use __builtin_expect to minimize impact of checks */ #if !INSECURE - #if defined(__GNUC__) && __GNUC__ >= 3 - #define RTCHECK(e) __builtin_expect(e, 1) - #else /* GNUC */ - #define RTCHECK(e) (e) - #endif /* GNUC */ -#else /* !INSECURE */ - #define RTCHECK(e) (1) -#endif /* !INSECURE */ +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ /* macros to set up inuse chunks with or without footers */ #if !FOOTERS - #define mark_inuse_foot(M, p, s) +#define mark_inuse_foot(M,p,s) - /* Macros for setting head/foot of non-mmapped chunks */ +/* Macros for setting head/foot of non-mmapped chunks */ - /* Set cinuse bit and pinuse bit of next chunk */ - #define set_inuse(M, p, s) \ - ((p)->head = (((p)->head & PINUSE_BIT) | s | CINUSE_BIT), \ - ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - /* Set cinuse and pinuse of this chunk and pinuse of next chunk */ - #define set_inuse_and_pinuse(M, p, s) \ - ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), \ - ((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT) +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) - /* Set size, cinuse and pinuse bit of this chunk */ - #define set_size_and_pinuse_of_inuse_chunk(M, p, s) \ - ((p)->head = (s | PINUSE_BIT | CINUSE_BIT)) +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) -#else /* FOOTERS */ +#else /* FOOTERS */ - /* Set foot of inuse chunk to be xor of mstate and seed */ - #define mark_inuse_foot(M, p, s) \ - (((mchunkptr)((char *)(p) + (s)))->prev_foot = \ - ((size_t)(M) ^ mparams.magic)) +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) - #define get_mstate_for(p) \ - ((mstate)(((mchunkptr)((char *)(p) + (chunksize(p))))->prev_foot ^ \ - mparams.magic)) +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) - #define set_inuse(M, p, s) \ - ((p)->head = (((p)->head & PINUSE_BIT) | s | CINUSE_BIT), \ - (((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M, p, s)) +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) - #define set_inuse_and_pinuse(M, p, s) \ - ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), \ - (((mchunkptr)(((char *)(p)) + (s)))->head |= PINUSE_BIT), \ - mark_inuse_foot(M, p, s)) +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) - #define set_size_and_pinuse_of_inuse_chunk(M, p, s) \ - ((p)->head = (s | PINUSE_BIT | CINUSE_BIT), mark_inuse_foot(M, p, s)) +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) -#endif /* !FOOTERS */ +#endif /* !FOOTERS */ /* ---------------------------- setting mparams -------------------------- */ #if LOCK_AT_FORK -static void pre_fork(void) { - - ACQUIRE_LOCK(&(gm)->mutex); - -} - -static void post_fork_parent(void) { - - RELEASE_LOCK(&(gm)->mutex); - -} - -static void post_fork_child(void) { - - INITIAL_LOCK(&(gm)->mutex); - -} - -#endif /* LOCK_AT_FORK */ +static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } +static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } +static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } +#endif /* LOCK_AT_FORK */ /* Initialize mparams */ static int init_mparams(void) { - #ifdef NEED_GLOBAL_LOCK_INIT - if (malloc_global_mutex_status <= 0) init_malloc_global_mutex(); + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); #endif ACQUIRE_MALLOC_GLOBAL_LOCK(); if (mparams.magic == 0) { - size_t magic; size_t psize; size_t gsize; #ifndef WIN32 psize = malloc_getpagesize; - gsize = ((DEFAULT_GRANULARITY != 0) ? DEFAULT_GRANULARITY : psize); -#else /* WIN32 */ + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* WIN32 */ { - SYSTEM_INFO system_info; GetSystemInfo(&system_info); psize = system_info.dwPageSize; - gsize = - ((DEFAULT_GRANULARITY != 0) ? DEFAULT_GRANULARITY - : system_info.dwAllocationGranularity); - + gsize = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); } - -#endif /* WIN32 */ +#endif /* WIN32 */ /* Sanity-check configuration: size_t must be unsigned and as wide as pointer type. @@ -3321,23 +3181,24 @@ static int init_mparams(void) { alignment must be at least 8. Alignment, min chunk size, and page size must all be powers of 2. */ - if ((sizeof(size_t) != sizeof(char *)) || (MAX_SIZE_T < MIN_CHUNK_SIZE) || - (sizeof(int) < 4) || (MALLOC_ALIGNMENT < (size_t)8U) || - ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT - SIZE_T_ONE)) != 0) || - ((MCHUNK_SIZE & (MCHUNK_SIZE - SIZE_T_ONE)) != 0) || - ((gsize & (gsize - SIZE_T_ONE)) != 0) || - ((psize & (psize - SIZE_T_ONE)) != 0)) + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) ABORT; mparams.granularity = gsize; mparams.page_size = psize; mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; #if MORECORE_CONTIGUOUS - mparams.default_mflags = USE_LOCK_BIT | USE_MMAP_BIT; -#else /* MORECORE_CONTIGUOUS */ - mparams.default_mflags = - USE_LOCK_BIT | USE_MMAP_BIT | USE_NONCONTIGUOUS_BIT; -#endif /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ #if !ONLY_MSPACES /* Set up lock for main malloc area */ @@ -3349,69 +3210,57 @@ static int init_mparams(void) { #endif { - #if USE_DEV_RANDOM - int fd; + int fd; unsigned char buf[sizeof(size_t)]; /* Try to use /dev/urandom, else fall back on using time */ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && read(fd, buf, sizeof(buf)) == sizeof(buf)) { - - magic = *((size_t *)buf); + magic = *((size_t *) buf); close(fd); - - } else - -#endif /* USE_DEV_RANDOM */ + } + else +#endif /* USE_DEV_RANDOM */ #ifdef WIN32 - magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); #elif defined(LACKS_TIME_H) magic = (size_t)&magic ^ (size_t)0x55555555U; #else magic = (size_t)(time(0) ^ (size_t)0x55555555U); #endif - magic |= (size_t)8U; /* ensure nonzero */ - magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ /* Until memory modes commonly available, use volatile-write */ (*(volatile size_t *)(&(mparams.magic))) = magic; - } - } RELEASE_MALLOC_GLOBAL_LOCK(); return 1; - } /* support for mallopt */ static int change_mparam(int param_number, int value) { - size_t val; ensure_initialization(); - val = (value == -1) ? MAX_SIZE_T : (size_t)value; - switch (param_number) { - - case M_TRIM_THRESHOLD: - mparams.trim_threshold = val; - return 1; - case M_GRANULARITY: - if (val >= mparams.page_size && ((val & (val - 1)) == 0)) { - - mparams.granularity = val; - return 1; - - } else - - return 0; - case M_MMAP_THRESHOLD: - mparams.mmap_threshold = val; + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; return 1; - default: + } + else return 0; - + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; } - } #if DEBUG @@ -3419,118 +3268,100 @@ static int change_mparam(int param_number, int value) { /* Check properties of any chunk, whether free, inuse, mmapped etc */ static void do_check_any_chunk(mstate m, mchunkptr p) { - assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); - } /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { - - msegmentptr sp = segment_holding(m, (char *)p); - size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ assert(sp != 0); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(sz == m->topsize); assert(sz > 0); - assert(sz == ((sp->base + sp->size) - (char *)p) - TOP_FOOT_SIZE); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); assert(pinuse(p)); assert(!pinuse(chunk_plus_offset(p, sz))); - } /* Check properties of (inuse) mmapped chunks */ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { - - size_t sz = chunksize(p); + size_t sz = chunksize(p); size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); assert(is_mmapped(p)); assert(use_mmap(m)); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(!is_small(sz)); - assert((len & (mparams.page_size - SIZE_T_ONE)) == 0); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); - assert(chunk_plus_offset(p, sz + SIZE_T_SIZE)->head == 0); - + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); } /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { - do_check_any_chunk(m, p); assert(is_inuse(p)); assert(next_pinuse(p)); /* If not pinuse and not mmapped, previous chunk has OK offset */ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); - if (is_mmapped(p)) do_check_mmapped_chunk(m, p); - + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); } /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { - - size_t sz = chunksize(p); + size_t sz = chunksize(p); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); assert(!is_inuse(p)); assert(!next_pinuse(p)); - assert(!is_mmapped(p)); + assert (!is_mmapped(p)); if (p != m->dv && p != m->top) { - if (sz >= MIN_CHUNK_SIZE) { - assert((sz & CHUNK_ALIGN_MASK) == 0); assert(is_aligned(chunk2mem(p))); assert(next->prev_foot == sz); assert(pinuse(p)); - assert(next == m->top || is_inuse(next)); + assert (next == m->top || is_inuse(next)); assert(p->fd->bk == p); assert(p->bk->fd == p); - - } else /* markers are always of size SIZE_T_SIZE */ - + } + else /* markers are always of size SIZE_T_SIZE */ assert(sz == SIZE_T_SIZE); - } - } /* Check properties of malloced chunks at the point they are malloced */ -static void do_check_malloced_chunk(mstate m, void *mem, size_t s) { - +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t sz = p->head & ~INUSE_BITS; + size_t sz = p->head & ~INUSE_BITS; do_check_inuse_chunk(m, p); assert((sz & CHUNK_ALIGN_MASK) == 0); assert(sz >= MIN_CHUNK_SIZE); assert(sz >= s); /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); - } - } /* Check a tree and its subtrees. */ static void do_check_tree(mstate m, tchunkptr t) { - tchunkptr head = 0; tchunkptr u = t; - bindex_t tindex = t->index; - size_t tsize = chunksize(t); - bindex_t idx; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; compute_tree_index(tsize, idx); assert(tindex == idx); assert(tsize >= MIN_LARGE_SIZE); assert(tsize >= minsize_for_tree_index(idx)); - assert((idx == NTREEBINS - 1) || (tsize < minsize_for_tree_index((idx + 1)))); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); - do { /* traverse through chain of same-sized nodes */ + do { /* traverse through chain of same-sized nodes */ do_check_any_chunk(m, ((mchunkptr)u)); assert(u->index == tindex); assert(chunksize(u) == tsize); @@ -3539,72 +3370,56 @@ static void do_check_tree(mstate m, tchunkptr t) { assert(u->fd->bk == u); assert(u->bk->fd == u); if (u->parent == 0) { - assert(u->child[0] == 0); assert(u->child[1] == 0); - - } else { - - assert(head == 0); /* only one node on chain has parent */ + } + else { + assert(head == 0); /* only one node on chain has parent */ head = u; assert(u->parent != u); - assert(u->parent->child[0] == u || u->parent->child[1] == u || - *((tbinptr *)(u->parent)) == u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); if (u->child[0] != 0) { - assert(u->child[0]->parent == u); assert(u->child[0] != u); do_check_tree(m, u->child[0]); - } - if (u->child[1] != 0) { - assert(u->child[1]->parent == u); assert(u->child[1] != u); do_check_tree(m, u->child[1]); - } - if (u->child[0] != 0 && u->child[1] != 0) { - assert(chunksize(u->child[0]) < chunksize(u->child[1])); - } - } - u = u->fd; - } while (u != t); - assert(head != 0); - } /* Check all the chunks in a treebin. */ static void do_check_treebin(mstate m, bindex_t i) { - - tbinptr * tb = treebin_at(m, i); + tbinptr* tb = treebin_at(m, i); tchunkptr t = *tb; - int empty = (m->treemap & (1U << i)) == 0; - if (t == 0) assert(empty); - if (!empty) do_check_tree(m, t); - + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); } /* Check all the chunks in a smallbin. */ static void do_check_smallbin(mstate m, bindex_t i) { - - sbinptr b = smallbin_at(m, i); - mchunkptr p = b->bk; + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; unsigned int empty = (m->smallmap & (1U << i)) == 0; - if (p == b) assert(empty); + if (p == b) + assert(empty); if (!empty) { - for (; p != b; p = p->bk) { - - size_t size = chunksize(p); + size_t size = chunksize(p); mchunkptr q; /* each chunk claims to be free */ do_check_free_chunk(m, p); @@ -3613,249 +3428,185 @@ static void do_check_smallbin(mstate m, bindex_t i) { assert(p->bk == b || chunksize(p->bk) == chunksize(p)); /* chunk is followed by an inuse chunk */ q = next_chunk(p); - if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q); - + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); } - } - } /* Find x in a bin. Used in other check functions. */ static int bin_find(mstate m, mchunkptr x) { - size_t size = chunksize(x); if (is_small(size)) { - bindex_t sidx = small_index(size); - sbinptr b = smallbin_at(m, sidx); + sbinptr b = smallbin_at(m, sidx); if (smallmap_is_marked(m, sidx)) { - mchunkptr p = b; do { - - if (p == x) return 1; - + if (p == x) + return 1; } while ((p = p->fd) != b); - } - - } else { - + } + else { bindex_t tidx; compute_tree_index(size, tidx); if (treemap_is_marked(m, tidx)) { - tchunkptr t = *treebin_at(m, tidx); - size_t sizebits = size << leftshift_for_tree_index(tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { - - t = t->child[(sizebits >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; sizebits <<= 1; - } - if (t != 0) { - tchunkptr u = t; do { - - if (u == (tchunkptr)x) return 1; - + if (u == (tchunkptr)x) + return 1; } while ((u = u->fd) != t); - } - } - } - return 0; - } /* Traverse each chunk and check it; return total */ static size_t traverse_and_check(mstate m) { - size_t sum = 0; if (is_initialized(m)) { - msegmentptr s = &m->seg; sum += m->topsize + TOP_FOOT_SIZE; while (s != 0) { - mchunkptr q = align_as_chunk(s->base); mchunkptr lastq = 0; assert(pinuse(q)); - while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { - + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { sum += chunksize(q); if (is_inuse(q)) { - assert(!bin_find(m, q)); do_check_inuse_chunk(m, q); - - } else { - + } + else { assert(q == m->dv || bin_find(m, q)); - assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ do_check_free_chunk(m, q); - } - lastq = q; q = next_chunk(q); - } - s = s->next; - } - } - return sum; - } + /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { - bindex_t i; - size_t total; + size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i); for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i); - if (m->dvsize != 0) { /* check dv chunk */ + if (m->dvsize != 0) { /* check dv chunk */ do_check_any_chunk(m, m->dv); assert(m->dvsize == chunksize(m->dv)); assert(m->dvsize >= MIN_CHUNK_SIZE); assert(bin_find(m, m->dv) == 0); - } - if (m->top != 0) { /* check top chunk */ + if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); /*assert(m->topsize == chunksize(m->top)); redundant */ assert(m->topsize > 0); assert(bin_find(m, m->top) == 0); - } total = traverse_and_check(m); assert(total <= m->footprint); assert(m->footprint <= m->max_footprint); - } - -#endif /* DEBUG */ +#endif /* DEBUG */ /* ----------------------------- statistics ------------------------------ */ #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { - - struct mallinfo nm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ensure_initialization(); if (!PREACTION(m)) { - check_malloc_state(m); if (is_initialized(m)) { - - size_t nfree = SIZE_T_ONE; /* top always free */ - size_t mfree = m->topsize + TOP_FOOT_SIZE; - size_t sum = mfree; + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && q != m->top && - q->head != FENCEPOST_HEAD) { - + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { size_t sz = chunksize(q); sum += sz; if (!is_inuse(q)) { - mfree += sz; ++nfree; - } - q = next_chunk(q); - } - s = s->next; - } - nm.arena = sum; - nm.ordblks = nfree; - nm.hblkhd = m->footprint - sum; - nm.usmblks = m->max_footprint; + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; nm.uordblks = m->footprint - mfree; nm.fordblks = mfree; nm.keepcost = m->topsize; - } POSTACTION(m); - } - return nm; - } - -#endif /* !NO_MALLINFO */ +#endif /* !NO_MALLINFO */ #if !NO_MALLOC_STATS static void internal_malloc_stats(mstate m) { - ensure_initialization(); if (!PREACTION(m)) { - size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { - msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { - mchunkptr q = align_as_chunk(s->base); - while (segment_holds(s, q) && q != m->top && - q->head != FENCEPOST_HEAD) { - - if (!is_inuse(q)) used -= chunksize(q); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); q = next_chunk(q); - } - s = s->next; - } - } - - POSTACTION(m); /* drop lock */ + POSTACTION(m); /* drop lock */ fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); - } - } - -#endif /* NO_MALLOC_STATS */ +#endif /* NO_MALLOC_STATS */ /* ----------------------- Operations on smallbins ----------------------- */ @@ -3867,181 +3618,134 @@ static void internal_malloc_stats(mstate m) { */ /* Link a free chunk into a smallbin */ -#define insert_small_chunk(M, P, S) \ - { \ - \ - bindex_t I = small_index(S); \ - mchunkptr B = smallbin_at(M, I); \ - mchunkptr F = B; \ - assert(S >= MIN_CHUNK_SIZE); \ - if (!smallmap_is_marked(M, I)) \ - mark_smallmap(M, I); \ - else if (RTCHECK(ok_address(M, B->fd))) \ - F = B->fd; \ - else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - B->fd = P; \ - F->bk = P; \ - P->fd = F; \ - P->bk = B; \ - \ - } +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} /* Unlink a chunk from a smallbin */ -#define unlink_small_chunk(M, P, S) \ - { \ - \ - mchunkptr F = P->fd; \ - mchunkptr B = P->bk; \ - bindex_t I = small_index(S); \ - assert(P != B); \ - assert(P != F); \ - assert(chunksize(P) == small_index2size(I)); \ - if (RTCHECK(F == smallbin_at(M, I) || (ok_address(M, F) && F->bk == P))) { \ - \ - if (B == F) { \ - \ - clear_smallmap(M, I); \ - \ - } else if (RTCHECK(B == smallbin_at(M, I) || \ - \ - \ - (ok_address(M, B) && B->fd == P))) { \ - \ - F->bk = B; \ - B->fd = F; \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(B == smallbin_at(M,I) ||\ + (ok_address(M, B) && B->fd == P))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} /* Unlink the first chunk from a smallbin */ -#define unlink_first_small_chunk(M, B, P, I) \ - { \ - \ - mchunkptr F = P->fd; \ - assert(P != B); \ - assert(P != F); \ - assert(chunksize(P) == small_index2size(I)); \ - if (B == F) { \ - \ - clear_smallmap(M, I); \ - \ - } else if (RTCHECK(ok_address(M, F) && F->bk == P)) { \ - \ - F->bk = B; \ - B->fd = F; \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} /* Replace dv node, binning the old one */ /* Used only when dvsize known to be small */ -#define replace_dv(M, P, S) \ - { \ - \ - size_t DVS = M->dvsize; \ - assert(is_small(DVS)); \ - if (DVS != 0) { \ - \ - mchunkptr DV = M->dv; \ - insert_small_chunk(M, DV, DVS); \ - \ - } \ - M->dvsize = S; \ - M->dv = P; \ - \ - } +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + assert(is_small(DVS));\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} /* ------------------------- Operations on trees ------------------------- */ /* Insert chunk into tree */ -#define insert_large_chunk(M, X, S) \ - { \ - \ - tbinptr *H; \ - bindex_t I; \ - compute_tree_index(S, I); \ - H = treebin_at(M, I); \ - X->index = I; \ - X->child[0] = X->child[1] = 0; \ - if (!treemap_is_marked(M, I)) { \ - \ - mark_treemap(M, I); \ - *H = X; \ - X->parent = (tchunkptr)H; \ - X->fd = X->bk = X; \ - \ - } else { \ - \ - tchunkptr T = *H; \ - size_t K = S << leftshift_for_tree_index(I); \ - for (;;) { \ - \ - if (chunksize(T) != S) { \ - \ - tchunkptr *C = \ - &(T->child[(K >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]); \ - K <<= 1; \ - if (*C != 0) \ - T = *C; \ - else if (RTCHECK(ok_address(M, C))) { \ - \ - *C = X; \ - X->parent = T; \ - X->fd = X->bk = X; \ - break; \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - break; \ - \ - } \ - \ - } else { \ - \ - tchunkptr F = T->fd; \ - if (RTCHECK(ok_address(M, T) && ok_address(M, F))) { \ - \ - T->fd = F->bk = X; \ - X->fd = F; \ - X->bk = T; \ - X->parent = 0; \ - break; \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - break; \ - \ - } \ - \ - } \ - \ - } \ - \ - } \ - \ - } +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} /* Unlink steps: @@ -4060,149 +3764,104 @@ static void internal_malloc_stats(mstate m) { x's parent and children to x's replacement (or null if none). */ -#define unlink_large_chunk(M, X) \ - { \ - \ - tchunkptr XP = X->parent; \ - tchunkptr R; \ - if (X->bk != X) { \ - \ - tchunkptr F = X->fd; \ - R = X->bk; \ - if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) { \ - \ - F->bk = R; \ - R->fd = F; \ - \ - } else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } else { \ - \ - tchunkptr *RP; \ - if (((R = *(RP = &(X->child[1]))) != 0) || \ - ((R = *(RP = &(X->child[0]))) != 0)) { \ - \ - tchunkptr *CP; \ - while ((*(CP = &(R->child[1])) != 0) || \ - (*(CP = &(R->child[0])) != 0)) { \ - \ - R = *(RP = CP); \ - \ - } \ - if (RTCHECK(ok_address(M, RP))) \ - *RP = 0; \ - else { \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } \ - \ - } \ - if (XP != 0) { \ - \ - tbinptr *H = treebin_at(M, X->index); \ - if (X == *H) { \ - \ - if ((*H = R) == 0) clear_treemap(M, X->index); \ - \ - } else if (RTCHECK(ok_address(M, XP))) { \ - \ - if (XP->child[0] == X) \ - XP->child[0] = R; \ - else \ - XP->child[1] = R; \ - \ - } else \ - \ - \ - CORRUPTION_ERROR_ACTION(M); \ - if (R != 0) { \ - \ - if (RTCHECK(ok_address(M, R))) { \ - \ - tchunkptr C0, C1; \ - R->parent = XP; \ - if ((C0 = X->child[0]) != 0) { \ - \ - if (RTCHECK(ok_address(M, C0))) { \ - \ - R->child[0] = C0; \ - C0->parent = R; \ - \ - } else \ - \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - if ((C1 = X->child[1]) != 0) { \ - \ - if (RTCHECK(ok_address(M, C1))) { \ - \ - R->child[1] = C1; \ - C1->parent = R; \ - \ - } else \ - \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } else \ - \ - \ - CORRUPTION_ERROR_ACTION(M); \ - \ - } \ - \ - } \ - \ - } +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} /* Relays to large vs small bin operations */ -#define insert_chunk(M, P, S) \ - if (is_small(S)) insert_small_chunk(M, P, S) else { \ - \ - tchunkptr TP = (tchunkptr)(P); \ - insert_large_chunk(M, TP, S); \ - \ - } +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } -#define unlink_chunk(M, P, S) \ - if (is_small(S)) unlink_small_chunk(M, P, S) else { \ - \ - tchunkptr TP = (tchunkptr)(P); \ - unlink_large_chunk(M, TP); \ - \ - } /* Relays to internal calls to malloc/free from realloc, memalign etc */ #if ONLY_MSPACES - #define internal_malloc(m, b) mspace_malloc(m, b) - #define internal_free(m, mem) mspace_free(m, mem); -#else /* ONLY_MSPACES */ - #if MSPACES - #define internal_malloc(m, b) \ - ((m == gm) ? dlmalloc(b) : mspace_malloc(m, b)) - #define internal_free(m, mem) \ - if (m == gm) \ - dlfree(mem); \ - else \ - mspace_free(m, mem); - #else /* MSPACES */ - #define internal_malloc(m, b) dlmalloc(b) - #define internal_free(m, mem) dlfree(mem) - #endif /* MSPACES */ -#endif /* ONLY_MSPACES */ +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ /* ----------------------- Direct-mmapping chunks ----------------------- */ @@ -4215,93 +3874,80 @@ static void internal_malloc_stats(mstate m) { */ /* Malloc using mmap */ -static void *mmap_alloc(mstate m, size_t nb) { - +static void* mmap_alloc(mstate m, size_t nb) { size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); if (m->footprint_limit != 0) { - size_t fp = m->footprint + mmsize; - if (fp <= m->footprint || fp > m->footprint_limit) return 0; - + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; } - - if (mmsize > nb) { /* Check for wrap around 0 */ - char *mm = (char *)(CALL_DIRECT_MMAP(mmsize)); + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); if (mm != CMFAIL) { - - size_t offset = align_offset(chunk2mem(mm)); - size_t psize = mmsize - offset - MMAP_FOOT_PAD; + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; mchunkptr p = (mchunkptr)(mm + offset); p->prev_foot = offset; p->head = psize; mark_inuse_foot(m, p, psize); chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(p, psize + SIZE_T_SIZE)->head = 0; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; - if (m->least_addr == 0 || mm < m->least_addr) m->least_addr = mm; + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; if ((m->footprint += mmsize) > m->max_footprint) m->max_footprint = m->footprint; assert(is_aligned(chunk2mem(p))); check_mmapped_chunk(m, p); return chunk2mem(p); - } - } - return 0; - } /* Realloc using mmap */ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { - size_t oldsize = chunksize(oldp); - (void)flags; /* placate people compiling -Wunused */ - if (is_small(nb)) /* Can't shrink mmap regions below small size */ + (void)flags; /* placate people compiling -Wunused */ + if (is_small(nb)) /* Can't shrink mmap regions below small size */ return 0; /* Keep old chunk if big enough but not too big */ if (oldsize >= nb + SIZE_T_SIZE && (oldsize - nb) <= (mparams.granularity << 1)) return oldp; else { - size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - char * cp = - (char *)CALL_MREMAP((char *)oldp - offset, oldmmsize, newmmsize, flags); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, flags); if (cp != CMFAIL) { - mchunkptr newp = (mchunkptr)(cp + offset); - size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; - chunk_plus_offset(newp, psize + SIZE_T_SIZE)->head = 0; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; - if (cp < m->least_addr) m->least_addr = cp; + if (cp < m->least_addr) + m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); return newp; - } - } - return 0; - } + /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ static void init_top(mstate m, mchunkptr p, size_t psize) { - /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); - p = (mchunkptr)((char *)p + offset); + p = (mchunkptr)((char*)p + offset); psize -= offset; m->top = p; @@ -4309,29 +3955,23 @@ static void init_top(mstate m, mchunkptr p, size_t psize) { p->head = psize | PINUSE_BIT; /* set size of fake trailing chunk holding overhead space only once */ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; - m->trim_check = mparams.trim_threshold; /* reset on each update */ - + m->trim_check = mparams.trim_threshold; /* reset on each update */ } /* Initialize bins for a new mstate that is otherwise zeroed out */ static void init_bins(mstate m) { - /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { - - sbinptr bin = smallbin_at(m, i); + sbinptr bin = smallbin_at(m,i); bin->fd = bin->bk = bin; - } - } #if PROCEED_ON_ERROR /* default corruption action */ static void reset_on_error(mstate m) { - int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ @@ -4344,78 +3984,67 @@ static void reset_on_error(mstate m) { for (i = 0; i < NTREEBINS; ++i) *treebin_at(m, i) = 0; init_bins(m); - } - -#endif /* PROCEED_ON_ERROR */ +#endif /* PROCEED_ON_ERROR */ /* Allocate chunk and prepend remainder with chunk in successor base. */ -static void *prepend_alloc(mstate m, char *newbase, char *oldbase, size_t nb) { - +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); - size_t psize = (char *)oldfirst - (char *)p; + size_t psize = (char*)oldfirst - (char*)p; mchunkptr q = chunk_plus_offset(p, nb); - size_t qsize = psize - nb; + size_t qsize = psize - nb; set_size_and_pinuse_of_inuse_chunk(m, p, nb); - assert((char *)oldfirst > (char *)q); + assert((char*)oldfirst > (char*)q); assert(pinuse(oldfirst)); assert(qsize >= MIN_CHUNK_SIZE); /* consolidate remainder with first chunk of old base */ if (oldfirst == m->top) { - size_t tsize = m->topsize += qsize; m->top = q; q->head = tsize | PINUSE_BIT; check_top_chunk(m, q); - - } else if (oldfirst == m->dv) { - + } + else if (oldfirst == m->dv) { size_t dsize = m->dvsize += qsize; m->dv = q; set_size_and_pinuse_of_free_chunk(q, dsize); - - } else { - + } + else { if (!is_inuse(oldfirst)) { - size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); qsize += nsize; - } - set_free_with_pinuse(q, qsize, oldfirst); insert_chunk(m, q, qsize); check_free_chunk(m, q); - } check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); - } /* Add a segment to hold a new noncontiguous region */ -static void add_segment(mstate m, char *tbase, size_t tsize, flag_t mmapped) { - +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Determine locations and sizes of segment, fenceposts, old top */ - char * old_top = (char *)m->top; + char* old_top = (char*)m->top; msegmentptr oldsp = segment_holding(m, old_top); - char * old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); - char * rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); - size_t offset = align_offset(chunk2mem(rawsp)); - char * asp = rawsp + offset; - char * csp = (asp < (old_top + MIN_CHUNK_SIZE)) ? old_top : asp; - mchunkptr sp = (mchunkptr)csp; + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; msegmentptr ss = (msegmentptr)(chunk2mem(sp)); - mchunkptr tnext = chunk_plus_offset(sp, ssize); - mchunkptr p = tnext; - int nfences = 0; + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; /* reset top to new space */ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); @@ -4423,7 +4052,7 @@ static void add_segment(mstate m, char *tbase, size_t tsize, flag_t mmapped) { /* Set up segment record */ assert(is_aligned(ss)); set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); - *ss = m->seg; /* Push current record */ + *ss = m->seg; /* Push current record */ m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmapped; @@ -4431,61 +4060,53 @@ static void add_segment(mstate m, char *tbase, size_t tsize, flag_t mmapped) { /* Insert trailing fenceposts */ for (;;) { - mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); p->head = FENCEPOST_HEAD; ++nfences; - if ((char *)(&(nextp->head)) < old_end) + if ((char*)(&(nextp->head)) < old_end) p = nextp; else break; - } - assert(nfences >= 2); /* Insert the rest of old top into a bin as an ordinary free chunk */ if (csp != old_top) { - mchunkptr q = (mchunkptr)old_top; - size_t psize = csp - old_top; + size_t psize = csp - old_top; mchunkptr tn = chunk_plus_offset(q, psize); set_free_with_pinuse(q, psize, tn); insert_chunk(m, q, psize); - } check_top_chunk(m, m->top); - } /* -------------------------- System allocation -------------------------- */ /* Get memory from system using MORECORE or MMAP */ -static void *sys_alloc(mstate m, size_t nb) { - - char * tbase = CMFAIL; +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; - size_t asize; /* allocation size */ + size_t asize; /* allocation size */ ensure_initialization(); /* Directly map large chunks, but only if already initialized */ if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { - - void *mem = mmap_alloc(m, nb); - if (mem != 0) return mem; - + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; } asize = granularity_align(nb + SYS_ALLOC_PADDING); - if (asize <= nb) return 0; /* wraparound */ + if (asize <= nb) + return 0; /* wraparound */ if (m->footprint_limit != 0) { - size_t fp = m->footprint + asize; - if (fp <= m->footprint || fp > m->footprint_limit) return 0; - + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; } /* @@ -4511,119 +4132,91 @@ static void *sys_alloc(mstate m, size_t nb) { */ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { - - char * br = CMFAIL; - size_t ssize = asize; /* sbrk call size */ - msegmentptr ss = (m->top == 0) ? 0 : segment_holding(m, (char *)m->top); + char* br = CMFAIL; + size_t ssize = asize; /* sbrk call size */ + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); ACQUIRE_MALLOC_GLOBAL_LOCK(); - if (ss == 0) { /* First time through or recovery */ - char *base = (char *)CALL_MORECORE(0); + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); if (base != CMFAIL) { - size_t fp; /* Adjust to end on a page boundary */ if (!is_page_aligned(base)) ssize += (page_align((size_t)base) - (size_t)base); - fp = m->footprint + ssize; /* recheck limits */ + fp = m->footprint + ssize; /* recheck limits */ if (ssize > nb && ssize < HALF_MAX_SIZE_T && (m->footprint_limit == 0 || (fp > m->footprint && fp <= m->footprint_limit)) && - (br = (char *)(CALL_MORECORE(ssize))) == base) { - + (br = (char*)(CALL_MORECORE(ssize))) == base) { tbase = base; tsize = ssize; - } - } - - } else { - + } + else { /* Subtract out existing available top space from MORECORE request. */ ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); /* Use mem here only if it did continuously extend old space */ if (ssize < HALF_MAX_SIZE_T && - (br = (char *)(CALL_MORECORE(ssize))) == ss->base + ss->size) { - + (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { tbase = br; tsize = ssize; - } - } - if (tbase == CMFAIL) { /* Cope with partial failure */ - if (br != CMFAIL) { /* Try to use/extend the space we did get */ - if (ssize < HALF_MAX_SIZE_T && ssize < nb + SYS_ALLOC_PADDING) { - + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (ssize < HALF_MAX_SIZE_T && + ssize < nb + SYS_ALLOC_PADDING) { size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); if (esize < HALF_MAX_SIZE_T) { - - char *end = (char *)CALL_MORECORE(esize); + char* end = (char*)CALL_MORECORE(esize); if (end != CMFAIL) ssize += esize; - else { /* Can't use; try to release */ - (void)CALL_MORECORE(-ssize); + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-ssize); br = CMFAIL; - } - } - } - } - - if (br != CMFAIL) { /* Use the space we did get */ + if (br != CMFAIL) { /* Use the space we did get */ tbase = br; tsize = ssize; - - } else - - disable_contiguous(m); /* Don't try contiguous path in the future */ - + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ } RELEASE_MALLOC_GLOBAL_LOCK(); - } - if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ - char *mp = (char *)(CALL_MMAP(asize)); + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + char* mp = (char*)(CALL_MMAP(asize)); if (mp != CMFAIL) { - tbase = mp; tsize = asize; mmap_flag = USE_MMAP_BIT; - } - } - if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ if (asize < HALF_MAX_SIZE_T) { - - char *br = CMFAIL; - char *end = CMFAIL; + char* br = CMFAIL; + char* end = CMFAIL; ACQUIRE_MALLOC_GLOBAL_LOCK(); - br = (char *)(CALL_MORECORE(asize)); - end = (char *)(CALL_MORECORE(0)); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); RELEASE_MALLOC_GLOBAL_LOCK(); if (br != CMFAIL && end != CMFAIL && br < end) { - size_t ssize = end - br; if (ssize > nb + TOP_FOOT_SIZE) { - tbase = br; tsize = ssize; - } - } - } - } if (tbase != CMFAIL) { @@ -4631,8 +4224,9 @@ static void *sys_alloc(mstate m, size_t nb) { if ((m->footprint += tsize) > m->max_footprint) m->max_footprint = m->footprint; - if (!is_initialized(m)) { /* first-time initialization */ - if (m->least_addr == 0 || tbase < m->least_addr) m->least_addr = tbase; + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmap_flag; @@ -4645,52 +4239,46 @@ static void *sys_alloc(mstate m, size_t nb) { else #endif { - /* Offset top by embedded malloc_state */ mchunkptr mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); - + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); } - } else { - /* Try to merge with an existing segment */ msegmentptr sp = &m->seg; /* Only consider most recent segment if traversal suppressed */ while (sp != 0 && tbase != sp->base + sp->size) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && !is_extern_segment(sp) && + if (sp != 0 && + !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag && - segment_holds(sp, m->top)) { /* append */ + segment_holds(sp, m->top)) { /* append */ sp->size += tsize; init_top(m, m->top, m->topsize + tsize); - - } else { - - if (tbase < m->least_addr) m->least_addr = tbase; + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; sp = &m->seg; while (sp != 0 && sp->base != tbase + tsize) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; - if (sp != 0 && !is_extern_segment(sp) && + if (sp != 0 && + !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag) { - - char *oldbase = sp->base; + char* oldbase = sp->base; sp->base = tbase; sp->size += tsize; return prepend_alloc(m, tbase, oldbase, nb); - - } else - + } + else add_segment(m, tbase, tsize, mmap_flag); - } - } - if (nb < m->topsize) { /* Allocate from new or extended top space */ - size_t rsize = m->topsize -= nb; + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; mchunkptr p = m->top; mchunkptr r = m->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -4698,421 +4286,313 @@ static void *sys_alloc(mstate m, size_t nb) { check_top_chunk(m, m->top); check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); - } - } MALLOC_FAILURE_ACTION; return 0; - } /* ----------------------- system deallocation -------------------------- */ /* Unmap and unlink any mmapped segments that don't contain used chunks */ static size_t release_unused_segments(mstate m) { - - size_t released = 0; - int nsegs = 0; + size_t released = 0; + int nsegs = 0; msegmentptr pred = &m->seg; msegmentptr sp = pred->next; while (sp != 0) { - - char * base = sp->base; - size_t size = sp->size; + char* base = sp->base; + size_t size = sp->size; msegmentptr next = sp->next; ++nsegs; if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { - mchunkptr p = align_as_chunk(base); - size_t psize = chunksize(p); + size_t psize = chunksize(p); /* Can unmap if first chunk holds entire segment and not pinned */ - if (!is_inuse(p) && (char *)p + psize >= base + size - TOP_FOOT_SIZE) { - + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { tchunkptr tp = (tchunkptr)p; - assert(segment_holds(sp, (char *)sp)); + assert(segment_holds(sp, (char*)sp)); if (p == m->dv) { - m->dv = 0; m->dvsize = 0; - - } else { - + } + else { unlink_large_chunk(m, tp); - } - if (CALL_MUNMAP(base, size) == 0) { - released += size; m->footprint -= size; /* unlink obsoleted record */ sp = pred; sp->next = next; - - } else { /* back out if cannot unmap */ - + } + else { /* back out if cannot unmap */ insert_large_chunk(m, tp, psize); - } - } - } - - if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ break; pred = sp; sp = next; - } - /* Reset check counter */ - m->release_checks = (((size_t)nsegs > (size_t)MAX_RELEASE_CHECK_RATE) - ? (size_t)nsegs - : (size_t)MAX_RELEASE_CHECK_RATE); + m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? + (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); return released; - } static int sys_trim(mstate m, size_t pad) { - size_t released = 0; ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { - - pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ if (m->topsize > pad) { - /* Shrink top space in granularity-size units, keeping at least one */ size_t unit = mparams.granularity; - size_t extra = - ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - SIZE_T_ONE) * unit; - msegmentptr sp = segment_holding(m, (char *)m->top); + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); if (!is_extern_segment(sp)) { - if (is_mmapped_segment(sp)) { - - if (HAVE_MMAP && sp->size >= extra && - !has_segment_link(m, sp)) { /* can't shrink if pinned */ + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ size_t newsize = sp->size - extra; - (void)newsize; /* placate people compiling -Wunused-variable */ + (void)newsize; /* placate people compiling -Wunused-variable */ /* Prefer mremap, fall back to munmap */ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { - released = extra; - } - } - - } else if (HAVE_MORECORE) { - - if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; ACQUIRE_MALLOC_GLOBAL_LOCK(); { - /* Make sure end of memory is where we last set it. */ - char *old_br = (char *)(CALL_MORECORE(0)); + char* old_br = (char*)(CALL_MORECORE(0)); if (old_br == sp->base + sp->size) { - - char *rel_br = (char *)(CALL_MORECORE(-extra)); - char *new_br = (char *)(CALL_MORECORE(0)); + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); if (rel_br != CMFAIL && new_br < old_br) released = old_br - new_br; - } - } - RELEASE_MALLOC_GLOBAL_LOCK(); - } - } if (released != 0) { - sp->size -= released; m->footprint -= released; init_top(m, m->top, m->topsize - released); check_top_chunk(m, m->top); - } - } /* Unmap any unused mmapped segments */ - if (HAVE_MMAP) released += release_unused_segments(m); + if (HAVE_MMAP) + released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ - if (released == 0 && m->topsize > m->trim_check) m->trim_check = MAX_SIZE_T; - + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; } - return (released != 0) ? 1 : 0; - + return (released != 0)? 1 : 0; } /* Consolidate and bin a chunk. Differs from exported versions of free mainly in that the chunk need not be marked as inuse. */ static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { - mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { - mchunkptr prev; - size_t prevsize = p->prev_foot; + size_t prevsize = p->prev_foot; if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) m->footprint -= psize; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + m->footprint -= psize; return; - } - prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ if (p != m->dv) { - unlink_chunk(m, p, prevsize); - - } else if ((next->head & INUSE_BITS) == INUSE_BITS) { - + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { m->dvsize = psize; set_free_with_pinuse(p, psize, next); return; - } - - } else { - + } + else { CORRUPTION_ERROR_ACTION(m); return; - } - } - if (RTCHECK(ok_address(m, next))) { - - if (!cinuse(next)) { /* consolidate forward */ + if (!cinuse(next)) { /* consolidate forward */ if (next == m->top) { - size_t tsize = m->topsize += psize; m->top = p; p->head = tsize | PINUSE_BIT; if (p == m->dv) { - m->dv = 0; m->dvsize = 0; - } - return; - - } else if (next == m->dv) { - + } + else if (next == m->dv) { size_t dsize = m->dvsize += psize; m->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); return; - - } else { - + } + else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(m, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == m->dv) { - m->dvsize = psize; return; - } - } - - } else { - + } + else { set_free_with_pinuse(p, psize, next); - } - insert_chunk(m, p, psize); - - } else { - + } + else { CORRUPTION_ERROR_ACTION(m); - } - } /* ---------------------------- malloc --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ -static void *tmalloc_large(mstate m, size_t nb) { - +static void* tmalloc_large(mstate m, size_t nb) { tchunkptr v = 0; - size_t rsize = -nb; /* Unsigned negation */ + size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; - bindex_t idx; + bindex_t idx; compute_tree_index(nb, idx); if ((t = *treebin_at(m, idx)) != 0) { - /* Traverse tree for this bin looking for node with size == nb */ - size_t sizebits = nb << leftshift_for_tree_index(idx); - tchunkptr rst = 0; /* The deepest untaken right subtree */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ for (;;) { - tchunkptr rt; - size_t trem = chunksize(t) - nb; + size_t trem = chunksize(t) - nb; if (trem < rsize) { - v = t; - if ((rsize = trem) == 0) break; - + if ((rsize = trem) == 0) + break; } - rt = t->child[1]; - t = t->child[(sizebits >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1]; - if (rt != 0 && rt != t) rst = rt; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; if (t == 0) { - - t = rst; /* set t to least subtree holding sizes > nb */ + t = rst; /* set t to least subtree holding sizes > nb */ break; - } - sizebits <<= 1; - } - } - - if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { - bindex_t i; binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); t = *treebin_at(m, i); - } - } - while (t != 0) { /* find smallest of tree or subtree */ + while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { - rsize = trem; v = t; - } - t = leftmost_child(t); - } /* If dv is a better fit, return 0 so malloc will use it */ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { - - if (RTCHECK(ok_address(m, v))) { /* split */ + if (RTCHECK(ok_address(m, v))) { /* split */ mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); - } - return chunk2mem(v); - } - } - CORRUPTION_ERROR_ACTION(m); - } - return 0; - } /* allocate a small request from the best fitting chunk in a treebin */ -static void *tmalloc_small(mstate m, size_t nb) { - +static void* tmalloc_small(mstate m, size_t nb) { tchunkptr t, v; - size_t rsize; - bindex_t i; - binmap_t leastbit = least_bit(m->treemap); + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; while ((t = leftmost_child(t)) != 0) { - size_t trem = chunksize(t) - nb; if (trem < rsize) { - rsize = trem; v = t; - } - } if (RTCHECK(ok_address(m, v))) { - mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { - unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { - set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(m, r, rsize); - } - return chunk2mem(v); - } - } CORRUPTION_ERROR_ACTION(m); return 0; - } #if !ONLY_MSPACES -void *dlmalloc(size_t bytes) { - +void* dlmalloc(size_t bytes) { /* Basic algorithm: If a small request (< 256 bytes minus per-chunk overhead): @@ -5136,25 +4616,23 @@ void *dlmalloc(size_t bytes) { The ugly goto's here ensure that postaction occurs along all paths. */ - #if USE_LOCKS - ensure_initialization(); /* initialize in sys_alloc if not using locks */ - #endif +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif if (!PREACTION(gm)) { - - void * mem; + void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; binmap_t smallbits; - nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes); + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = gm->smallmap >> idx; - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ + idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(gm, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); @@ -5163,17 +4641,15 @@ void *dlmalloc(size_t bytes) { mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; - } else if (nb > gm->dvsize) { - - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(gm, i); p = b->fd; @@ -5184,71 +4660,54 @@ void *dlmalloc(size_t bytes) { if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(gm, p, small_index2size(i)); else { - set_size_and_pinuse_of_inuse_chunk(gm, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(gm, r, rsize); - } - mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; - } else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); goto postaction; - } - } - - } else if (bytes >= MAX_REQUEST) - + } + else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { - nb = pad_request(bytes); if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { - check_malloced_chunk(gm, mem, nb); goto postaction; - } - } if (nb <= gm->dvsize) { - - size_t rsize = gm->dvsize - nb; + size_t rsize = gm->dvsize - nb; mchunkptr p = gm->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = gm->dv = chunk_plus_offset(p, nb); gm->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(gm, p, nb); - - } else { /* exhaust dv */ - + } + else { /* exhaust dv */ size_t dvs = gm->dvsize; gm->dvsize = 0; gm->dv = 0; set_inuse_and_pinuse(gm, p, dvs); - } - mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; - } - else if (nb < gm->topsize) { /* Split top */ - size_t rsize = gm->topsize -= nb; + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; mchunkptr p = gm->top; mchunkptr r = gm->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -5257,7 +4716,6 @@ void *dlmalloc(size_t bytes) { check_top_chunk(gm, gm->top); check_malloced_chunk(gm, mem, nb); goto postaction; - } mem = sys_alloc(gm, nb); @@ -5265,17 +4723,14 @@ void *dlmalloc(size_t bytes) { postaction: POSTACTION(gm); return mem; - } return 0; - } /* ---------------------------- free --------------------------- */ -void dlfree(void *mem) { - +void dlfree(void* mem) { /* Consolidate freed chunks with preceeding or succeeding bordering free chunks, if they exist, and then place in a bin. Intermixed @@ -5283,216 +4738,164 @@ void dlfree(void *mem) { */ if (mem != 0) { - - mchunkptr p = mem2chunk(mem); - #if FOOTERS + mchunkptr p = mem2chunk(mem); +#if FOOTERS mstate fm = get_mstate_for(p); if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); return; - } - - #else /* FOOTERS */ - #define fm gm - #endif /* FOOTERS */ +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - - size_t psize = chunksize(p); + size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { - size_t prevsize = p->prev_foot; if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; - - } else { - + } + else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - - } else if ((next->head & INUSE_BITS) == INUSE_BITS) { - + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; - } - - } else - + } + else goto erroraction; - } - } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - - if (!cinuse(next)) { /* consolidate forward */ + if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { - size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { - fm->dv = 0; fm->dvsize = 0; - } - - if (should_trim(fm, tsize)) sys_trim(fm, 0); + if (should_trim(fm, tsize)) + sys_trim(fm, 0); goto postaction; - - } else if (next == fm->dv) { - + } + else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; - - } else { - + } + else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { - fm->dvsize = psize; goto postaction; - } - } - - } else - + } + else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { - insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); - - } else { - + } + else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); - if (--fm->release_checks == 0) release_unused_segments(fm); - + if (--fm->release_checks == 0) + release_unused_segments(fm); } - goto postaction; - } - } - erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); - } - } - - #if !FOOTERS - #undef fm - #endif /* FOOTERS */ - +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ } -void *dlcalloc(size_t n_elements, size_t elem_size) { - - void * mem; +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; size_t req = 0; if (n_elements != 0) { - req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - + req = MAX_SIZE_T; /* force downstream failure on overflow */ } - mem = dlmalloc(req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) __builtin_memset(mem, 0, req); return mem; - } -#endif /* !ONLY_MSPACES */ +#endif /* !ONLY_MSPACES */ /* ------------ Internal support for realloc, memalign, etc -------------- */ /* Try to realloc; only in-place unless can_move true */ static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, int can_move) { - mchunkptr newp = 0; - size_t oldsize = chunksize(p); + size_t oldsize = chunksize(p); mchunkptr next = chunk_plus_offset(p, oldsize); - if (RTCHECK(ok_address(m, p) && ok_inuse(p) && ok_next(p, next) && - ok_pinuse(next))) { - + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) { if (is_mmapped(p)) { - newp = mmap_resize(m, p, nb, can_move); - - } else if (oldsize >= nb) { /* already big enough */ - + } + else if (oldsize >= nb) { /* already big enough */ size_t rsize = oldsize - nb; - if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); - } - newp = p; - - } else if (next == m->top) { /* extend into top */ - + } + else if (next == m->top) { /* extend into top */ if (oldsize + m->topsize > nb) { - - size_t newsize = oldsize + m->topsize; - size_t newtopsize = newsize - nb; + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(p, nb); set_inuse(m, p, nb); - newtop->head = newtopsize | PINUSE_BIT; + newtop->head = newtopsize |PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = p; - } - - } else if (next == m->dv) { /* extend into dv */ - + } + else if (next == m->dv) { /* extend into dv */ size_t dvs = m->dvsize; if (oldsize + dvs >= nb) { - size_t dsize = oldsize + dvs - nb; if (dsize >= MIN_CHUNK_SIZE) { - mchunkptr r = chunk_plus_offset(p, nb); mchunkptr n = chunk_plus_offset(r, dsize); set_inuse(m, p, nb); @@ -5500,87 +4903,64 @@ static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, clear_pinuse(n); m->dvsize = dsize; m->dv = r; - - } else { /* exhaust dv */ - + } + else { /* exhaust dv */ size_t newsize = oldsize + dvs; set_inuse(m, p, newsize); m->dvsize = 0; m->dv = 0; - } - newp = p; - } - - } else if (!cinuse(next)) { /* extend into next free chunk */ - + } + else if (!cinuse(next)) { /* extend into next free chunk */ size_t nextsize = chunksize(next); if (oldsize + nextsize >= nb) { - size_t rsize = oldsize + nextsize - nb; unlink_chunk(m, next, nextsize); if (rsize < MIN_CHUNK_SIZE) { - size_t newsize = oldsize + nextsize; set_inuse(m, p, newsize); - - } else { - + } + else { mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); - } - newp = p; - } - } - - } else { - + } + else { USAGE_ERROR_ACTION(m, chunk2mem(p)); - } - return newp; - } -static void *internal_memalign(mstate m, size_t alignment, size_t bytes) { - - void *mem = 0; - if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; - if ((alignment & (alignment - SIZE_T_ONE)) != 0) { /* Ensure a power of 2 */ + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ size_t a = MALLOC_ALIGNMENT << 1; - while (a < alignment) - a <<= 1; + while (a < alignment) a <<= 1; alignment = a; - } - if (bytes >= MAX_REQUEST - alignment) { - - if (m != 0) { /* Test isn't needed but avoids compiler warning */ + if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; - } - - } else { - + } + else { size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; mem = internal_malloc(m, req); if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (PREACTION(m)) return 0; - if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + if (PREACTION(m)) + return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if @@ -5589,59 +4969,47 @@ static void *internal_memalign(mstate m, size_t alignment, size_t bytes) { We've allocated enough total room so that this is always possible. */ - char * br = (char *)mem2chunk((size_t)( - ((size_t)((char *)mem + alignment - SIZE_T_ONE)) & -alignment)); - char * pos = ((size_t)(br - (char *)(p)) >= MIN_CHUNK_SIZE) - ? br - : br + alignment; + char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char *)(p); - size_t newsize = chunksize(p) - leadsize; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; - if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ newp->prev_foot = p->prev_foot + leadsize; newp->head = newsize; - - } else { /* Otherwise, give back leader, use the rest */ - + } + else { /* Otherwise, give back leader, use the rest */ set_inuse(m, newp, newsize); set_inuse(m, p, leadsize); dispose_chunk(m, p, leadsize); - } - p = newp; - } /* Give back spare room at the end */ if (!is_mmapped(p)) { - size_t size = chunksize(p); if (size > nb + MIN_CHUNK_SIZE) { - - size_t remainder_size = size - nb; + size_t remainder_size = size - nb; mchunkptr remainder = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, remainder, remainder_size); dispose_chunk(m, remainder, remainder_size); - } - } mem = chunk2mem(p); - assert(chunksize(p) >= nb); + assert (chunksize(p) >= nb); assert(((size_t)mem & (alignment - 1)) == 0); check_inuse_chunk(m, p); POSTACTION(m); - } - } - return mem; - } /* @@ -5651,50 +5019,50 @@ static void *internal_memalign(mstate m, size_t alignment, size_t bytes) { bit 0 set if all elements are same size (using sizes[0]) bit 1 set if elements should be zeroed */ -static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, - void *chunks[]) { - - size_t element_size; /* chunksize of each element, if all same */ - size_t contents_size; /* total size of elements */ - size_t array_size; /* request size of pointer array */ - void * mem; /* malloced aggregate space */ - mchunkptr p; /* corresponding chunk */ - size_t remainder_size; /* remaining bytes while splitting */ - void ** marray; /* either "chunks" or malloced ptr array */ - mchunkptr array_chunk; /* chunk for malloced ptr array */ - flag_t was_enabled; /* to disable mmap */ +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ size_t size; size_t i; ensure_initialization(); /* compute array length, if needed */ if (chunks != 0) { - - if (n_elements == 0) return chunks; /* nothing to do */ + if (n_elements == 0) + return chunks; /* nothing to do */ marray = chunks; array_size = 0; - - } else { - + } + else { /* if empty req, must still return chunk representing empty array */ - if (n_elements == 0) return (void **)internal_malloc(m, 0); + if (n_elements == 0) + return (void**)internal_malloc(m, 0); marray = 0; - array_size = request2size(n_elements * (sizeof(void *))); - + array_size = request2size(n_elements * (sizeof(void*))); } /* compute total element size */ - if (opts & 0x1) { /* all-same-size */ + if (opts & 0x1) { /* all-same-size */ element_size = request2size(*sizes); contents_size = n_elements * element_size; - - } else { /* add up all the sizes */ - + } + else { /* add up all the sizes */ element_size = 0; contents_size = 0; for (i = 0; i != n_elements; ++i) contents_size += request2size(sizes[i]); - } size = contents_size + array_size; @@ -5707,8 +5075,10 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, was_enabled = use_mmap(m); disable_mmap(m); mem = internal_malloc(m, size - CHUNK_OVERHEAD); - if (was_enabled) enable_mmap(m); - if (mem == 0) return 0; + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; if (PREACTION(m)) return 0; p = mem2chunk(mem); @@ -5716,30 +5086,24 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, assert(!is_mmapped(p)); - if (opts & 0x2) { /* optionally clear the elements */ - __builtin_memset((size_t *)mem, 0, - remainder_size - SIZE_T_SIZE - array_size); - + if (opts & 0x2) { /* optionally clear the elements */ + __builtin_memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); } /* If not provided, allocate the pointer array as final part of chunk */ if (marray == 0) { - - size_t array_chunk_size; + size_t array_chunk_size; array_chunk = chunk_plus_offset(p, contents_size); array_chunk_size = remainder_size - contents_size; - marray = (void **)(chunk2mem(array_chunk)); + marray = (void**) (chunk2mem(array_chunk)); set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); remainder_size = contents_size; - } /* split out elements */ - for (i = 0;; ++i) { - + for (i = 0; ; ++i) { marray[i] = chunk2mem(p); - if (i != n_elements - 1) { - + if (i != n_elements-1) { if (element_size != 0) size = element_size; else @@ -5747,42 +5111,31 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); - - } else { /* the final element absorbs any overallocation slop */ - + } + else { /* the final element absorbs any overallocation slop */ set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); break; - } - } #if DEBUG if (marray != chunks) { - /* final element must have exactly exhausted chunk */ if (element_size != 0) { - assert(remainder_size == element_size); - - } else { - + } + else { assert(remainder_size == request2size(sizes[i])); - } - check_inuse_chunk(m, mem2chunk(marray)); - } - for (i = 0; i != n_elements; ++i) check_inuse_chunk(m, mem2chunk(marray[i])); -#endif /* DEBUG */ +#endif /* DEBUG */ POSTACTION(m); return marray; - } /* Try to free all pointers in the given array. @@ -5792,431 +5145,316 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, chunks before freeing, which will occur often if allocated with ialloc or the array is sorted. */ -static size_t internal_bulk_free(mstate m, void *array[], size_t nelem) { - +static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { size_t unfreed = 0; if (!PREACTION(m)) { - - void **a; - void **fence = &(array[nelem]); + void** a; + void** fence = &(array[nelem]); for (a = array; a != fence; ++a) { - - void *mem = *a; + void* mem = *a; if (mem != 0) { - mchunkptr p = mem2chunk(mem); - size_t psize = chunksize(p); + size_t psize = chunksize(p); #if FOOTERS if (get_mstate_for(p) != m) { - ++unfreed; continue; - } - #endif check_inuse_chunk(m, p); *a = 0; if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { - - void ** b = a + 1; /* try to merge with next chunk */ + void ** b = a + 1; /* try to merge with next chunk */ mchunkptr next = next_chunk(p); if (b != fence && *b == chunk2mem(next)) { - size_t newsize = chunksize(next) + psize; set_inuse(m, p, newsize); *b = chunk2mem(p); - - } else - + } + else dispose_chunk(m, p, psize); - - } else { - + } + else { CORRUPTION_ERROR_ACTION(m); break; - } - } - } - - if (should_trim(m, m->topsize)) sys_trim(m, 0); + if (should_trim(m, m->topsize)) + sys_trim(m, 0); POSTACTION(m); - } - return unfreed; - } /* Traversal */ #if MALLOC_INSPECT_ALL static void internal_inspect_all(mstate m, - void (*handler)(void *start, void *end, - size_t used_bytes, - void * callback_arg), - void *arg) { - + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { if (is_initialized(m)) { - - mchunkptr top = m->top; + mchunkptr top = m->top; msegmentptr s; for (s = &m->seg; s != 0; s = s->next) { - mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { - mchunkptr next = next_chunk(q); - size_t sz = chunksize(q); - size_t used; - void * start; + size_t sz = chunksize(q); + size_t used; + void* start; if (is_inuse(q)) { - - used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ start = chunk2mem(q); - - } else { - + } + else { used = 0; - if (is_small(sz)) { /* offset by possible bookkeeping */ - start = (void *)((char *)q + sizeof(struct malloc_chunk)); - - } else { - - start = (void *)((char *)q + sizeof(struct malloc_tree_chunk)); - + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void*)((char*)q + sizeof(struct malloc_chunk)); + } + else { + start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); } - } - - if (start < (void *)next) /* skip if all space is bookkeeping */ + if (start < (void*)next) /* skip if all space is bookkeeping */ handler(start, next, used, arg); - if (q == top) break; + if (q == top) + break; q = next; - } - } - } - } - -#endif /* MALLOC_INSPECT_ALL */ +#endif /* MALLOC_INSPECT_ALL */ /* ------------------ Exported realloc, memalign, etc -------------------- */ #if !ONLY_MSPACES -void *dlrealloc(void *oldmem, size_t bytes) { - - void *mem = 0; +void* dlrealloc(void* oldmem, size_t bytes) { + void* mem = 0; if (oldmem == 0) { - mem = dlmalloc(bytes); - - } else if (bytes >= MAX_REQUEST) { - + } + else if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; - } - - #ifdef REALLOC_ZERO_BYTES_FREES +#ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { - dlfree(oldmem); - } - - #endif /* REALLOC_ZERO_BYTES_FREES */ +#endif /* REALLOC_ZERO_BYTES_FREES */ else { - - size_t nb = request2size(bytes); + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); - #if !FOOTERS +#if ! FOOTERS mstate m = gm; - #else /* FOOTERS */ +#else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); return 0; - } - - #endif /* FOOTERS */ +#endif /* FOOTERS */ if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { - check_inuse_chunk(m, newp); mem = chunk2mem(newp); - - } else { - + } + else { mem = internal_malloc(m, bytes); if (mem != 0) { - size_t oc = chunksize(oldp) - overhead_for(oldp); - __builtin_memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); + __builtin_memcpy(mem, oldmem, (oc < bytes)? oc : bytes); internal_free(m, oldmem); - } - } - } - } - return mem; - } -void *dlrealloc_in_place(void *oldmem, size_t bytes) { - - void *mem = 0; +void* dlrealloc_in_place(void* oldmem, size_t bytes) { + void* mem = 0; if (oldmem != 0) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - - } else { - - size_t nb = request2size(bytes); + } + else { + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); - #if !FOOTERS +#if ! FOOTERS mstate m = gm; - #else /* FOOTERS */ +#else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); return 0; - } - - #endif /* FOOTERS */ +#endif /* FOOTERS */ if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { - check_inuse_chunk(m, newp); mem = oldmem; - } - } - } - } - return mem; - } -void *dlmemalign(size_t alignment, size_t bytes) { - - if (alignment <= MALLOC_ALIGNMENT) { return dlmalloc(bytes); } +void* dlmemalign(size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc(bytes); + } return internal_memalign(gm, alignment, bytes); - } -int dlposix_memalign(void **pp, size_t alignment, size_t bytes) { - - void *mem = 0; +int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { + void* mem = 0; if (alignment == MALLOC_ALIGNMENT) mem = dlmalloc(bytes); else { - - size_t d = alignment / sizeof(void *); - size_t r = alignment % sizeof(void *); - if (r != 0 || d == 0 || (d & (d - SIZE_T_ONE)) != 0) + size_t d = alignment / sizeof(void*); + size_t r = alignment % sizeof(void*); + if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) return EINVAL; else if (bytes <= MAX_REQUEST - alignment) { - - if (alignment < MIN_CHUNK_SIZE) alignment = MIN_CHUNK_SIZE; + if (alignment < MIN_CHUNK_SIZE) + alignment = MIN_CHUNK_SIZE; mem = internal_memalign(gm, alignment, bytes); - } - } - if (mem == 0) return ENOMEM; else { - *pp = mem; return 0; - } - } -void *dlvalloc(size_t bytes) { - +void* dlvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, bytes); - } -void *dlpvalloc(size_t bytes) { - +void* dlpvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; - return dlmemalign(pagesz, - (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); - + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); } -void **dlindependent_calloc(size_t n_elements, size_t elem_size, - void *chunks[]) { - - size_t sz = elem_size; /* serves as 1-element array */ +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ return ialloc(gm, n_elements, &sz, 3, chunks); - } -void **dlindependent_comalloc(size_t n_elements, size_t sizes[], - void *chunks[]) { - +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { return ialloc(gm, n_elements, sizes, 0, chunks); - } -size_t dlbulk_free(void *array[], size_t nelem) { - +size_t dlbulk_free(void* array[], size_t nelem) { return internal_bulk_free(gm, array, nelem); - } - #if MALLOC_INSPECT_ALL -void dlmalloc_inspect_all(void (*handler)(void *start, void *end, - size_t used_bytes, - void * callback_arg), - void *arg) { - +#if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { ensure_initialization(); if (!PREACTION(gm)) { - internal_inspect_all(gm, handler, arg); POSTACTION(gm); - } - } - - #endif /* MALLOC_INSPECT_ALL */ +#endif /* MALLOC_INSPECT_ALL */ int dlmalloc_trim(size_t pad) { - int result = 0; ensure_initialization(); if (!PREACTION(gm)) { - result = sys_trim(gm, pad); POSTACTION(gm); - } - return result; - } size_t dlmalloc_footprint(void) { - return gm->footprint; - } size_t dlmalloc_max_footprint(void) { - return gm->max_footprint; - } size_t dlmalloc_footprint_limit(void) { - size_t maf = gm->footprint_limit; return maf == 0 ? MAX_SIZE_T : maf; - } size_t dlmalloc_set_footprint_limit(size_t bytes) { - - size_t result; /* invert sense of 0 */ - if (bytes == 0) result = granularity_align(1); /* Use minimal size */ + size_t result; /* invert sense of 0 */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) - result = 0; /* disable */ + result = 0; /* disable */ else result = granularity_align(bytes); return gm->footprint_limit = result; - } - #if !NO_MALLINFO +#if !NO_MALLINFO struct mallinfo dlmallinfo(void) { - return internal_mallinfo(gm); - } +#endif /* NO_MALLINFO */ - #endif /* NO_MALLINFO */ - - #if !NO_MALLOC_STATS +#if !NO_MALLOC_STATS void dlmalloc_stats() { - internal_malloc_stats(gm); - } - - #endif /* NO_MALLOC_STATS */ +#endif /* NO_MALLOC_STATS */ int dlmallopt(int param_number, int value) { - return change_mparam(param_number, value); - } -size_t dlmalloc_usable_size(void *mem) { - +size_t dlmalloc_usable_size(void* mem) { if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) return chunksize(p) - overhead_for(p); - + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); } - return 0; - } -#endif /* !ONLY_MSPACES */ +#endif /* !ONLY_MSPACES */ /* ----------------------------- user mspaces ---------------------------- */ #if MSPACES -static mstate init_user_mstate(char *tbase, size_t tsize) { - - size_t msize = pad_request(sizeof(struct malloc_state)); +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); mchunkptr mn; mchunkptr msp = align_as_chunk(tbase); - mstate m = (mstate)(chunk2mem(msp)); + mstate m = (mstate)(chunk2mem(msp)); __builtin_memset(m, 0, msize); (void)INITIAL_LOCK(&m->mutex); - msp->head = (msize | INUSE_BITS); + msp->head = (msize|INUSE_BITS); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; @@ -6227,111 +5465,82 @@ static mstate init_user_mstate(char *tbase, size_t tsize) { disable_contiguous(m); init_bins(m); mn = next_chunk(mem2chunk(m)); - init_top(m, mn, (size_t)((tbase + tsize) - (char *)mn) - TOP_FOOT_SIZE); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); check_top_chunk(m, m->top); return m; - } mspace create_mspace(size_t capacity, int locked) { - mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); - if (capacity < (size_t) - (msize + TOP_FOOT_SIZE + mparams.page_size)) { - - size_t rs = ((capacity == 0) ? mparams.granularity - : (capacity + TOP_FOOT_SIZE + msize)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); size_t tsize = granularity_align(rs); - char * tbase = (char *)(CALL_MMAP(tsize)); + char* tbase = (char*)(CALL_MMAP(tsize)); if (tbase != CMFAIL) { - m = init_user_mstate(tbase, tsize); m->seg.sflags = USE_MMAP_BIT; set_lock(m, locked); - } - } - return (mspace)m; - } -mspace create_mspace_with_base(void *base, size_t capacity, int locked) { - +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity > msize + TOP_FOOT_SIZE && - capacity < (size_t) - (msize + TOP_FOOT_SIZE + mparams.page_size)) { - - m = init_user_mstate((char *)base, capacity); + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); m->seg.sflags = EXTERN_BIT; set_lock(m, locked); - } - return (mspace)m; - } int mspace_track_large_chunks(mspace msp, int enable) { - - int ret = 0; + int ret = 0; mstate ms = (mstate)msp; if (!PREACTION(ms)) { - - if (!use_mmap(ms)) { ret = 1; } + if (!use_mmap(ms)) { + ret = 1; + } if (!enable) { - enable_mmap(ms); - } else { - disable_mmap(ms); - } - POSTACTION(ms); - } - return ret; - } size_t destroy_mspace(mspace msp) { - size_t freed = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - msegmentptr sp = &ms->seg; - (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ while (sp != 0) { - - char * base = sp->base; + char* base = sp->base; size_t size = sp->size; flag_t flag = sp->sflags; - (void)base; /* placate people compiling -Wunused-variable */ + (void)base; /* placate people compiling -Wunused-variable */ sp = sp->next; if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && CALL_MUNMAP(base, size) == 0) freed += size; - } - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return freed; - } /* @@ -6339,31 +5548,25 @@ size_t destroy_mspace(mspace msp) { versions. This is not so nice but better than the alternatives. */ -void *mspace_malloc(mspace msp, size_t bytes) { - +void* mspace_malloc(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { - - USAGE_ERROR_ACTION(ms, ms); + USAGE_ERROR_ACTION(ms,ms); return 0; - } - if (!PREACTION(ms)) { - - void * mem; + void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { - bindex_t idx; binmap_t smallbits; - nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes); + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = ms->smallmap >> idx; - if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; - idx += ~smallbits & 1; /* Uses next bin if idx empty */ + idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(ms, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); @@ -6372,17 +5575,15 @@ void *mspace_malloc(mspace msp, size_t bytes) { mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; - } else if (nb > ms->dvsize) { - - if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; - size_t rsize; - bindex_t i; - binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); - binmap_t leastbit = least_bit(leftbits); + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; @@ -6393,71 +5594,54 @@ void *mspace_malloc(mspace msp, size_t bytes) { if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { - set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); - } - mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; - } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); goto postaction; - } - } - - } else if (bytes >= MAX_REQUEST) - + } + else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { - nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { - check_malloced_chunk(ms, mem, nb); goto postaction; - } - } if (nb <= ms->dvsize) { - - size_t rsize = ms->dvsize - nb; + size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; - if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); - - } else { /* exhaust dv */ - + } + else { /* exhaust dv */ size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); - } - mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; - } - else if (nb < ms->topsize) { /* Split top */ - size_t rsize = ms->topsize -= nb; + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; @@ -6466,7 +5650,6 @@ void *mspace_malloc(mspace msp, size_t bytes) { check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; - } mem = sys_alloc(ms, nb); @@ -6474,519 +5657,372 @@ void *mspace_malloc(mspace msp, size_t bytes) { postaction: POSTACTION(ms); return mem; - } return 0; - } -void mspace_free(mspace msp, void *mem) { - +void mspace_free(mspace msp, void* mem) { if (mem != 0) { - - mchunkptr p = mem2chunk(mem); - #if FOOTERS + mchunkptr p = mem2chunk(mem); +#if FOOTERS mstate fm = get_mstate_for(p); - (void)msp; /* placate people compiling -Wunused */ - #else /* FOOTERS */ + (void)msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ mstate fm = (mstate)msp; - #endif /* FOOTERS */ +#endif /* FOOTERS */ if (!ok_magic(fm)) { - USAGE_ERROR_ACTION(fm, p); return; - } - if (!PREACTION(fm)) { - check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { - - size_t psize = chunksize(p); + size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { - size_t prevsize = p->prev_foot; if (is_mmapped(p)) { - psize += prevsize + MMAP_FOOT_PAD; - if (CALL_MUNMAP((char *)p - prevsize, psize) == 0) + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; - - } else { - + } + else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; - if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { - unlink_chunk(fm, p, prevsize); - - } else if ((next->head & INUSE_BITS) == INUSE_BITS) { - + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; - } - - } else - + } + else goto erroraction; - } - } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { - - if (!cinuse(next)) { /* consolidate forward */ + if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { - size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { - fm->dv = 0; fm->dvsize = 0; - } - - if (should_trim(fm, tsize)) sys_trim(fm, 0); + if (should_trim(fm, tsize)) + sys_trim(fm, 0); goto postaction; - - } else if (next == fm->dv) { - + } + else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; - - } else { - + } + else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { - fm->dvsize = psize; goto postaction; - } - } - - } else - + } + else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { - insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); - - } else { - + } + else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); - if (--fm->release_checks == 0) release_unused_segments(fm); - + if (--fm->release_checks == 0) + release_unused_segments(fm); } - goto postaction; - } - } - erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); - } - } - } -void *mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { - - void * mem; +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; size_t req = 0; mstate ms = (mstate)msp; if (!ok_magic(ms)) { - - USAGE_ERROR_ACTION(ms, ms); + USAGE_ERROR_ACTION(ms,ms); return 0; - } - if (n_elements != 0) { - req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) - req = MAX_SIZE_T; /* force downstream failure on overflow */ - + req = MAX_SIZE_T; /* force downstream failure on overflow */ } - mem = internal_malloc(ms, req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) __builtin_memset(mem, 0, req); return mem; - } -void *mspace_realloc(mspace msp, void *oldmem, size_t bytes) { - - void *mem = 0; +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; if (oldmem == 0) { - mem = mspace_malloc(msp, bytes); - - } else if (bytes >= MAX_REQUEST) { - + } + else if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; - } - - #ifdef REALLOC_ZERO_BYTES_FREES +#ifdef REALLOC_ZERO_BYTES_FREES else if (bytes == 0) { - mspace_free(msp, oldmem); - } - - #endif /* REALLOC_ZERO_BYTES_FREES */ +#endif /* REALLOC_ZERO_BYTES_FREES */ else { - - size_t nb = request2size(bytes); + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); - #if !FOOTERS +#if ! FOOTERS mstate m = (mstate)msp; - #else /* FOOTERS */ +#else /* FOOTERS */ mstate m = get_mstate_for(oldp); if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); return 0; - } - - #endif /* FOOTERS */ +#endif /* FOOTERS */ if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); POSTACTION(m); if (newp != 0) { - check_inuse_chunk(m, newp); mem = chunk2mem(newp); - - } else { - + } + else { mem = mspace_malloc(m, bytes); if (mem != 0) { - size_t oc = chunksize(oldp) - overhead_for(oldp); - __builtin_memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); + __builtin_memcpy(mem, oldmem, (oc < bytes)? oc : bytes); mspace_free(m, oldmem); - } - } - } - } - return mem; - } -void *mspace_realloc_in_place(mspace msp, void *oldmem, size_t bytes) { - - void *mem = 0; +void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; if (oldmem != 0) { - if (bytes >= MAX_REQUEST) { - MALLOC_FAILURE_ACTION; - - } else { - - size_t nb = request2size(bytes); + } + else { + size_t nb = request2size(bytes); mchunkptr oldp = mem2chunk(oldmem); - #if !FOOTERS +#if ! FOOTERS mstate m = (mstate)msp; - #else /* FOOTERS */ +#else /* FOOTERS */ mstate m = get_mstate_for(oldp); - (void)msp; /* placate people compiling -Wunused */ + (void)msp; /* placate people compiling -Wunused */ if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); return 0; - } - - #endif /* FOOTERS */ +#endif /* FOOTERS */ if (!PREACTION(m)) { - mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); POSTACTION(m); if (newp == oldp) { - check_inuse_chunk(m, newp); mem = oldmem; - } - } - } - } - return mem; - } -void *mspace_memalign(mspace msp, size_t alignment, size_t bytes) { - +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { - - USAGE_ERROR_ACTION(ms, ms); + USAGE_ERROR_ACTION(ms,ms); return 0; - } - - if (alignment <= MALLOC_ALIGNMENT) return mspace_malloc(msp, bytes); + if (alignment <= MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); return internal_memalign(ms, alignment, bytes); - } -void **mspace_independent_calloc(mspace msp, size_t n_elements, - size_t elem_size, void *chunks[]) { - - size_t sz = elem_size; /* serves as 1-element array */ +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ mstate ms = (mstate)msp; if (!ok_magic(ms)) { - - USAGE_ERROR_ACTION(ms, ms); + USAGE_ERROR_ACTION(ms,ms); return 0; - } - return ialloc(ms, n_elements, &sz, 3, chunks); - } -void **mspace_independent_comalloc(mspace msp, size_t n_elements, - size_t sizes[], void *chunks[]) { - +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { - - USAGE_ERROR_ACTION(ms, ms); + USAGE_ERROR_ACTION(ms,ms); return 0; - } - return ialloc(ms, n_elements, sizes, 0, chunks); - } -size_t mspace_bulk_free(mspace msp, void *array[], size_t nelem) { - +size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { return internal_bulk_free((mstate)msp, array, nelem); - } - #if MALLOC_INSPECT_ALL +#if MALLOC_INSPECT_ALL void mspace_inspect_all(mspace msp, - void (*handler)(void *start, void *end, - size_t used_bytes, void *callback_arg), - void *arg) { - + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { mstate ms = (mstate)msp; if (ok_magic(ms)) { - if (!PREACTION(ms)) { - internal_inspect_all(ms, handler, arg); POSTACTION(ms); - } - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } } - - #endif /* MALLOC_INSPECT_ALL */ +#endif /* MALLOC_INSPECT_ALL */ int mspace_trim(mspace msp, size_t pad) { - - int result = 0; + int result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - if (!PREACTION(ms)) { - result = sys_trim(ms, pad); POSTACTION(ms); - } - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; - } - #if !NO_MALLOC_STATS +#if !NO_MALLOC_STATS void mspace_malloc_stats(mspace msp) { - mstate ms = (mstate)msp; if (ok_magic(ms)) { - internal_malloc_stats(ms); - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } } - - #endif /* NO_MALLOC_STATS */ +#endif /* NO_MALLOC_STATS */ size_t mspace_footprint(mspace msp) { - size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - result = ms->footprint; - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; - } size_t mspace_max_footprint(mspace msp) { - size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - result = ms->max_footprint; - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; - } size_t mspace_footprint_limit(mspace msp) { - size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - size_t maf = ms->footprint_limit; result = (maf == 0) ? MAX_SIZE_T : maf; - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; - } size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { - size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { - - if (bytes == 0) result = granularity_align(1); /* Use minimal size */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ if (bytes == MAX_SIZE_T) - result = 0; /* disable */ + result = 0; /* disable */ else result = granularity_align(bytes); ms->footprint_limit = result; - - } else { - - USAGE_ERROR_ACTION(ms, ms); - } - + else { + USAGE_ERROR_ACTION(ms,ms); + } return result; - } - #if !NO_MALLINFO +#if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { - mstate ms = (mstate)msp; - if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms, ms); } + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } return internal_mallinfo(ms); - } +#endif /* NO_MALLINFO */ - #endif /* NO_MALLINFO */ - -size_t mspace_usable_size(const void *mem) { - +size_t mspace_usable_size(const void* mem) { if (mem != 0) { - mchunkptr p = mem2chunk(mem); - if (is_inuse(p)) return chunksize(p) - overhead_for(p); - + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); } - return 0; - } int mspace_mallopt(int param_number, int value) { - return change_mparam(param_number, value); - } -#endif /* MSPACES */ +#endif /* MSPACES */ + /* -------------------- Alternative MORECORE functions ------------------- */ @@ -7031,48 +6067,35 @@ int mspace_mallopt(int param_number, int value) { void *osMoreCore(int size) { - void *ptr = 0; static void *sbrk_top = 0; if (size > 0) { - if (size < MINIMUM_MORECORE_SIZE) size = MINIMUM_MORECORE_SIZE; if (CurrentExecutionLevel() == kTaskLevel) ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); if (ptr == 0) { - return (void *) MFAIL; - } - // save ptrs so they can be freed during cleanup our_os_pools[next_os_pool] = ptr; next_os_pool++; ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); sbrk_top = (char *) ptr + size; return ptr; - } - else if (size < 0) { - // we don't currently support shrink behavior return (void *) MFAIL; - } - else { - return sbrk_top; - } - } // cleanup any allocated memory pools @@ -7080,22 +6103,19 @@ int mspace_mallopt(int param_number, int value) { void osCleanupMem(void) { - void **ptr; for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) if (*ptr) { - PoolDeallocate(*ptr); *ptr = 0; - } - } */ + /* ----------------------------------------------------------------------- History: v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea @@ -7315,3 +6335,4 @@ History: */ +#endif // __GLIBC__ diff --git a/qemu_mode/libqasan/malloc.c b/qemu_mode/libqasan/malloc.c index f8237826..54c1096a 100644 --- a/qemu_mode/libqasan/malloc.c +++ b/qemu_mode/libqasan/malloc.c @@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #include "libqasan.h" +#include #include #include #include @@ -50,9 +51,9 @@ typedef struct { struct chunk_begin { size_t requested_size; - void * aligned_orig; // NULL if not aligned - struct chunk_begin *next; - struct chunk_begin *prev; + void* aligned_orig; // NULL if not aligned + struct chunk_begin* next; + struct chunk_begin* prev; char redzone[REDZONE_SIZE]; }; @@ -65,30 +66,47 @@ struct chunk_struct { }; +#ifdef __GLIBC__ + +void* (*__lq_libc_malloc)(size_t); +void (*__lq_libc_free)(void*); +#define backend_malloc __lq_libc_malloc +#define backend_free __lq_libc_free + +#define TMP_ZONE_SIZE 4096 +static int __tmp_alloc_zone_idx; +static unsigned char __tmp_alloc_zone[TMP_ZONE_SIZE]; + +#else + // From dlmalloc.c -void *dlmalloc(size_t); -void dlfree(void *); +void* dlmalloc(size_t); +void dlfree(void*); +#define backend_malloc dlmalloc +#define backend_free dlfree + +#endif int __libqasan_malloc_initialized; -static struct chunk_begin *quarantine_top; -static struct chunk_begin *quarantine_end; +static struct chunk_begin* quarantine_top; +static struct chunk_begin* quarantine_end; static size_t quarantine_bytes; #ifdef __BIONIC__ -static pthread_mutex_t quarantine_lock; - #define LOCK_TRY pthread_mutex_trylock - #define LOCK_INIT pthread_mutex_init - #define LOCK_UNLOCK pthread_mutex_unlock +static pthread_mutex_t quarantine_lock; +#define LOCK_TRY pthread_mutex_trylock +#define LOCK_INIT pthread_mutex_init +#define LOCK_UNLOCK pthread_mutex_unlock #else -static pthread_spinlock_t quarantine_lock; - #define LOCK_TRY pthread_spin_trylock - #define LOCK_INIT pthread_spin_init - #define LOCK_UNLOCK pthread_spin_unlock +static pthread_spinlock_t quarantine_lock; +#define LOCK_TRY pthread_spin_trylock +#define LOCK_INIT pthread_spin_init +#define LOCK_UNLOCK pthread_spin_unlock #endif // need qasan disabled -static int quanratine_push(struct chunk_begin *ck) { +static int quanratine_push(struct chunk_begin* ck) { if (ck->requested_size >= QUARANTINE_MAX_BYTES) return 0; @@ -96,15 +114,15 @@ static int quanratine_push(struct chunk_begin *ck) { while (ck->requested_size + quarantine_bytes >= QUARANTINE_MAX_BYTES) { - struct chunk_begin *tmp = quarantine_end; + struct chunk_begin* tmp = quarantine_end; quarantine_end = tmp->prev; quarantine_bytes -= tmp->requested_size; if (tmp->aligned_orig) - dlfree(tmp->aligned_orig); + backend_free(tmp->aligned_orig); else - dlfree(tmp); + backend_free(tmp); } @@ -122,6 +140,11 @@ void __libqasan_init_malloc(void) { if (__libqasan_malloc_initialized) return; +#ifdef __GLIBC__ + __lq_libc_malloc = dlsym(RTLD_NEXT, "malloc"); + __lq_libc_free = dlsym(RTLD_NEXT, "free"); +#endif + LOCK_INIT(&quarantine_lock, PTHREAD_PROCESS_PRIVATE); __libqasan_malloc_initialized = 1; @@ -131,24 +154,38 @@ void __libqasan_init_malloc(void) { } -size_t __libqasan_malloc_usable_size(void *ptr) { +size_t __libqasan_malloc_usable_size(void* ptr) { - char *p = ptr; + char* p = ptr; p -= sizeof(struct chunk_begin); - return ((struct chunk_begin *)p)->requested_size; + return ((struct chunk_begin*)p)->requested_size; } -void *__libqasan_malloc(size_t size) { +void* __libqasan_malloc(size_t size) { - if (!__libqasan_malloc_initialized) { __libqasan_init_malloc(); } + if (!__libqasan_malloc_initialized) { + + __libqasan_init_malloc(); - if (!__libqasan_malloc_initialized) __libqasan_init_malloc(); +#ifdef __GLIBC__ + void* r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; + + if (size & (ALLOC_ALIGN_SIZE - 1)) + __tmp_alloc_zone_idx += + (size & ~(ALLOC_ALIGN_SIZE - 1)) + ALLOC_ALIGN_SIZE; + else + __tmp_alloc_zone_idx += size; + + return r; +#endif + + } int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread - struct chunk_begin *p = dlmalloc(sizeof(struct chunk_struct) + size); + struct chunk_begin* p = backend_malloc(sizeof(struct chunk_struct) + size); QASAN_SWAP(state); @@ -160,14 +197,14 @@ void *__libqasan_malloc(size_t size) { p->aligned_orig = NULL; p->next = p->prev = NULL; - QASAN_ALLOC(&p[1], (char *)&p[1] + size); + QASAN_ALLOC(&p[1], (char*)&p[1] + size); QASAN_POISON(p->redzone, REDZONE_SIZE, ASAN_HEAP_LEFT_RZ); if (size & (ALLOC_ALIGN_SIZE - 1)) - QASAN_POISON((char *)&p[1] + size, + QASAN_POISON((char*)&p[1] + size, (size & ~(ALLOC_ALIGN_SIZE - 1)) + 8 - size + REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); else - QASAN_POISON((char *)&p[1] + size, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); + QASAN_POISON((char*)&p[1] + size, REDZONE_SIZE, ASAN_HEAP_RIGHT_RZ); __builtin_memset(&p[1], 0xff, size); @@ -175,11 +212,17 @@ void *__libqasan_malloc(size_t size) { } -void __libqasan_free(void *ptr) { +void __libqasan_free(void* ptr) { if (!ptr) return; + +#ifdef __GLIBC__ + if (ptr >= (void*)__tmp_alloc_zone && + ptr < ((void*)__tmp_alloc_zone + TMP_ZONE_SIZE)) + return; +#endif - struct chunk_begin *p = ptr; + struct chunk_begin* p = ptr; p -= 1; size_t n = p->requested_size; @@ -190,9 +233,9 @@ void __libqasan_free(void *ptr) { if (!quanratine_push(p)) { if (p->aligned_orig) - dlfree(p->aligned_orig); + backend_free(p->aligned_orig); else - dlfree(p); + backend_free(p); } @@ -206,11 +249,21 @@ void __libqasan_free(void *ptr) { } -void *__libqasan_calloc(size_t nmemb, size_t size) { +void* __libqasan_calloc(size_t nmemb, size_t size) { size *= nmemb; - char *p = __libqasan_malloc(size); +#ifdef __GLIBC__ + if (!__libqasan_malloc_initialized) { + + void* r = &__tmp_alloc_zone[__tmp_alloc_zone_idx]; + __tmp_alloc_zone_idx += size; + return r; + + } +#endif + + char* p = __libqasan_malloc(size); if (!p) return NULL; __builtin_memset(p, 0, size); @@ -219,14 +272,14 @@ void *__libqasan_calloc(size_t nmemb, size_t size) { } -void *__libqasan_realloc(void *ptr, size_t size) { +void* __libqasan_realloc(void* ptr, size_t size) { - char *p = __libqasan_malloc(size); + char* p = __libqasan_malloc(size); if (!p) return NULL; if (!ptr) return p; - size_t n = ((struct chunk_begin *)ptr)[-1].requested_size; + size_t n = ((struct chunk_begin*)ptr)[-1].requested_size; if (size < n) n = size; __builtin_memcpy(p, ptr, n); @@ -236,9 +289,9 @@ void *__libqasan_realloc(void *ptr, size_t size) { } -int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { +int __libqasan_posix_memalign(void** ptr, size_t align, size_t len) { - if ((align % 2) || (align % sizeof(void *))) return EINVAL; + if ((align % 2) || (align % sizeof(void*))) return EINVAL; if (len == 0) { *ptr = NULL; @@ -252,7 +305,7 @@ int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { int state = QASAN_SWAP(QASAN_DISABLED); // disable qasan for this thread - char *orig = dlmalloc(sizeof(struct chunk_struct) + size); + char* orig = backend_malloc(sizeof(struct chunk_struct) + size); QASAN_SWAP(state); @@ -260,10 +313,10 @@ int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { QASAN_UNPOISON(orig, sizeof(struct chunk_struct) + size); - char *data = orig + sizeof(struct chunk_begin); + char* data = orig + sizeof(struct chunk_begin); data += align - ((uintptr_t)data % align); - struct chunk_begin *p = (struct chunk_begin *)data - 1; + struct chunk_begin* p = (struct chunk_begin*)data - 1; p->requested_size = len; p->aligned_orig = orig; @@ -286,9 +339,9 @@ int __libqasan_posix_memalign(void **ptr, size_t align, size_t len) { } -void *__libqasan_memalign(size_t align, size_t len) { +void* __libqasan_memalign(size_t align, size_t len) { - void *ret = NULL; + void* ret = NULL; __libqasan_posix_memalign(&ret, align, len); @@ -296,9 +349,9 @@ void *__libqasan_memalign(size_t align, size_t len) { } -void *__libqasan_aligned_alloc(size_t align, size_t len) { +void* __libqasan_aligned_alloc(size_t align, size_t len) { - void *ret = NULL; + void* ret = NULL; if ((len % align)) return NULL; diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 6ab6bf28..47722f64 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 6ab6bf28decb3e36eee43ffbd4a3bfd052dbbb50 +Subproject commit 47722f64e4c1662bad97dc25f3e4cc63959ff5f3 -- cgit 1.4.1 From 5b2634f711e95b48b6105d3cac659e51706ff4e9 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 10 Feb 2021 17:56:27 +0100 Subject: update changelog + ideas --- docs/Changelog.md | 12 ++++++------ docs/ideas.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f2041917..56137eec 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,8 +16,8 @@ sending a mail to . to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz - - Making AFL_MAP_SIZE obsolete - afl-fuzz now learns on start the - target map size + - Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on start + the target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). this is costly hence new command line option @@ -27,7 +27,7 @@ sending a mail to . - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - on restarts (-i)/autoresume (AFL_AUTORESUME) the stats are now - reloaded and used, thanks to Vimal Joseph for this PR! + reloaded and used, thanks to Vimal Joseph for this patch! - if determinstic mode is active (-D, or -M without -d) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map @@ -47,8 +47,10 @@ sending a mail to . - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang + - fixed a potential crash in the LAF feature - qemuafl - - ported QASan to qemuafl! see qemu_mode/libqasan/README.md + - QASan (address sanitizer for Qemu) ported to qemuafl! + See qemu_mode/libqasan/README.md - solved some persistent mode bugs (thanks Dil4rd) - solved an issue when dumping the memory maps (thanks wizche) - Android support for QASan @@ -58,8 +60,6 @@ sending a mail to . - Added a new example harness to compare python, c, and rust bindings - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars - - LLVM mode is now compiled with -j4, unicorn with all cores. qemu was - already building with all cores, the gcc plugin needs only one. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) diff --git a/docs/ideas.md b/docs/ideas.md index 7cbe60a5..08cb16ef 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -3,6 +3,40 @@ In the following, we describe a variety of ideas that could be implemented for future AFL++ versions. +# GSoC 2021 + +All GSoC 2021 projects will be in the Rust development language! + +## UI for libaflrs + +Write a user interface to libaflrs, the upcoming backend of afl++. +This might look like the afl-fuzz UI, but you can improve on it - and should! + +## Schedulers for libaflrs + +Schedulers is a mechanism that selects items from the fuzzing corpus based +on strategy and randomness. One scheduler might focus on long paths, +another on rarity of edges disocvered, still another on a combination on +things. Some of the schedulers in afl++ have to be ported, but you are free +to come up with your own if you want to - and see how it performs. + +## Forkserver support for libaflrs + +The current libaflrs implementation fuzzes in-memory, however obviously we +want to support afl instrumented binaries as well. +Hence a forkserver support needs to be implemented - forking off the target +and talking to the target via a socketpair and the communication protocol +within. + +## More Observers for libaflrs + +An observer is measuring functionality that looks at the target being fuzzed +and documents something about it. In traditional fuzzing this is the coverage +in the target, however we want to add various more observers, e.g. stack depth, +heap usage, etc. - this is a topic for an experienced Rust developer. + +# Generic ideas and wishlist + ## Analysis software Currently analysis is done by using afl-plot, which is rather outdated. -- cgit 1.4.1 From dd3f4bb41c47025c490e7687d7bd1f04188f7bc4 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Thu, 11 Feb 2021 20:05:06 +0100 Subject: typos & formatting --- docs/Changelog.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 56137eec..919e2aeb 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -20,18 +20,18 @@ sending a mail to . the target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, - arithmetics, etc.). this is costly hence new command line option - -l that sets the intensity (values 1 to 3). recommended is 1 or 2. + arithmetics, etc.). This is costly hence new command line option + `-l` that sets the intensity (values 1 to 3). Recommended is 1 or 2. - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from `-i` or resumes (as these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - - on restarts (-i)/autoresume (AFL_AUTORESUME) the stats are now + - on restarts (`-i`)/autoresume (AFL_AUTORESUME) the stats are now reloaded and used, thanks to Vimal Joseph for this patch! - - if determinstic mode is active (-D, or -M without -d) then we sync + - if deterministic mode is active (`-D`, or `-M` without `-d`) then we sync after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map - - fix for -Z + - fix for `-Z` - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc @@ -55,7 +55,7 @@ sending a mail to . - solved an issue when dumping the memory maps (thanks wizche) - Android support for QASan - unicornafl - - Substential speed gains in python bindings for certain use cases + - Substantial speed gains in python bindings for certain use cases - Improved rust bindings - Added a new example harness to compare python, c, and rust bindings - changed default: no memory limit for afl-cmin and afl-cmin.bash -- cgit 1.4.1 From 223bd70f1f1530a0b8331e3deee0de2b20ae061b Mon Sep 17 00:00:00 2001 From: hexcoder Date: Thu, 11 Feb 2021 20:08:28 +0100 Subject: typo --- docs/env_variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index ab56c178..886669ad 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -517,7 +517,7 @@ The QEMU wrapper used to instrument binary-only code supports several settings: - With `AFL_USE_QASAN` you can enable QEMU AddressSanitizer for dynamically linked binaries. - - With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered singal + - With `AFL_QEMU_FORCE_DFL` you force QEMU to ignore the registered signal handlers of the target. ## 6) Settings for afl-cmin -- cgit 1.4.1 From 2ff6e5023f9116c24842e6aee0da644106a61829 Mon Sep 17 00:00:00 2001 From: hexcoder Date: Thu, 11 Feb 2021 20:14:48 +0100 Subject: typos --- docs/rpc_statsd.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/rpc_statsd.md b/docs/rpc_statsd.md index 02f72be6..fb97aa09 100644 --- a/docs/rpc_statsd.md +++ b/docs/rpc_statsd.md @@ -1,6 +1,6 @@ # Remote monitoring with StatsD -StatsD allows you to receive and aggregate metrics from a wide range of application and retransmit them to the backend of your choice. +StatsD allows you to receive and aggregate metrics from a wide range of applications and retransmit them to the backend of your choice. This enables you to create nice and readable dashboards containing all the information you need on your fuzzer instances. No need to write your own statistics parsing system, deploy and maintain it to all your instances, sync with your graph rendering system... @@ -45,7 +45,7 @@ For more information on these env vars, check out `docs/env_variables.md`. The simplest way of using this feature is to use any metric provider and change the host/port of your StatsD daemon, with `AFL_STATSD_HOST` and `AFL_STATSD_PORT`, if required (defaults are `localhost` and port `8125`). -To get started, here are some instruction with free and open source tools. +To get started, here are some instructions with free and open source tools. The following setup is based on Prometheus, statsd_exporter and Grafana. Grafana here is not mandatory, but gives you some nice graphs and features. @@ -131,7 +131,7 @@ mappings: Run `docker-compose up -d`. -Everything should be now setup, you are now able to run your fuzzers with +Everything should now be setup, you are now able to run your fuzzers with ``` AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -M test-fuzzer-1 -i i -o o ./bin/my-application @@ @@ -139,5 +139,5 @@ AFL_STATSD_TAGS_FLAVOR=dogstatsd AFL_STATSD=1 afl-fuzz -S test-fuzzer-2 -i i -o ... ``` -This setup may be modified before use in production environment. Depending on your needs: addind passwords, creating volumes for storage, +This setup may be modified before use in a production environment. Depending on your needs: adding passwords, creating volumes for storage, tweaking the metrics gathering to get host metrics (CPU, RAM ...). -- cgit 1.4.1 From 87a607c7d081dac1e4afd7c3cbe5accf62d355e4 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 13 Feb 2021 11:17:53 +0100 Subject: update doc --- docs/Changelog.md | 1 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 10 +++++----- unicorn_mode/unicornafl | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 919e2aeb..c59a1a7a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -48,6 +48,7 @@ sending a mail to . support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang - fixed a potential crash in the LAF feature + - workaround for llvm 13 - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 5d6d6703..80c8f917 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -1088,7 +1088,7 @@ void ModuleSanitizerCoverage::InjectTraceForSwitch( } - llvm::sort(Initializers.begin() + 2, Initializers.end(), + llvm::sort(drop_begin(Initializers, 2), [](const Constant *A, const Constant *B) { return cast(A)->getLimitedValue() < @@ -1136,10 +1136,10 @@ void ModuleSanitizerCoverage::InjectTraceForGep( for (auto GEP : GepTraceTargets) { IRBuilder<> IRB(GEP); - for (auto I = GEP->idx_begin(); I != GEP->idx_end(); ++I) - if (!isa(*I) && (*I)->getType()->isIntegerTy()) - IRB.CreateCall(SanCovTraceGepFunction, - {IRB.CreateIntCast(*I, IntptrTy, true)}); + for (Use &Idx : GEP->indices()) + if (!isa(Idx) && Idx->getType()->isIntegerTy()) + IRB.CreateCall(SanCovTraceGepFunction, + {IRB.CreateIntCast(Idx, IntptrTy, true)}); } diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl index 80d31ef3..fb2fc9f2 160000 --- a/unicorn_mode/unicornafl +++ b/unicorn_mode/unicornafl @@ -1 +1 @@ -Subproject commit 80d31ef367f7a1a75fc48e08e129d10f2ffa0498 +Subproject commit fb2fc9f25df32f17f6b6b859e4dbd70f9a857e0c -- cgit 1.4.1 From 6ce9230ed66dbd091001436cdab2fc1718e8e61e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 13 Feb 2021 14:24:21 +0100 Subject: afl-cmin/afl-showmap -f --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c59a1a7a..895ab845 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -59,6 +59,7 @@ sending a mail to . - Substantial speed gains in python bindings for certain use cases - Improved rust bindings - Added a new example harness to compare python, c, and rust bindings + - afl-cmin and afl-showmap now support the -f option - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars - added dummy Makefile to instrumentation/ -- cgit 1.4.1 From 9bd1e19d7f004b4da6a610b07e59f99d66bb7ec2 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sat, 13 Feb 2021 22:43:56 +0100 Subject: added AFL_IGNORE_UNKNOWN_ENVS --- docs/Changelog.md | 3 ++- docs/env_variables.md | 5 ++++- include/envs.h | 1 + instrumentation/SanitizerCoveragePCGUARD.so.cc | 2 +- src/afl-cc.c | 1 + src/afl-common.c | 4 ++-- src/afl-fuzz.c | 1 + src/afl-showmap.c | 5 +++-- 8 files changed, 15 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 895ab845..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -61,7 +61,8 @@ sending a mail to . - Added a new example harness to compare python, c, and rust bindings - afl-cmin and afl-showmap now support the -f option - changed default: no memory limit for afl-cmin and afl-cmin.bash - - warn on any _AFL and __AFL env vars + - warn on any _AFL and __AFL env vars. + - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) diff --git a/docs/env_variables.md b/docs/env_variables.md index 886669ad..f7745247 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -5,6 +5,10 @@ users or for some types of custom fuzzing setups. See [README.md](README.md) for the general instruction manual. + Note that most tools will warn on any unknown AFL environment variables. + This is for warning on typos that can happen. If you want to disable this + check then set the `AFL_IGNORE_UNKNOWN_ENVS` environment variable. + ## 1) Settings for all compilers Starting with afl++ 3.0 there is only one compiler: afl-cc @@ -18,7 +22,6 @@ To select the different instrumentation modes this can be done by `MODE` can be one of `LTO` (afl-clang-lto*), `LLVM` (afl-clang-fast*), `GCC_PLUGIN` (afl-g*-fast) or `GCC` (afl-gcc/afl-g++). - Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: diff --git a/include/envs.h b/include/envs.h index 210b34a6..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -61,6 +61,7 @@ static char *afl_environment_variables[] = { "AFL_FORKSRV_INIT_TMOUT", "AFL_HARDEN", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES", + "AFL_IGNORE_UNKNOWN_ENVS", "AFL_IMPORT_FIRST", "AFL_INST_LIBS", "AFL_INST_RATIO", diff --git a/instrumentation/SanitizerCoveragePCGUARD.so.cc b/instrumentation/SanitizerCoveragePCGUARD.so.cc index 80c8f917..9b1351b0 100644 --- a/instrumentation/SanitizerCoveragePCGUARD.so.cc +++ b/instrumentation/SanitizerCoveragePCGUARD.so.cc @@ -1138,7 +1138,7 @@ void ModuleSanitizerCoverage::InjectTraceForGep( IRBuilder<> IRB(GEP); for (Use &Idx : GEP->indices()) if (!isa(Idx) && Idx->getType()->isIntegerTy()) - IRB.CreateCall(SanCovTraceGepFunction, + IRB.CreateCall(SanCovTraceGepFunction, {IRB.CreateIntCast(Idx, IntptrTy, true)}); } diff --git a/src/afl-cc.c b/src/afl-cc.c index 9d88f262..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1587,6 +1587,7 @@ int main(int argc, char **argv, char **envp) { "libtokencap.so)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" + " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" " AFL_INST_RATIO: percentage of branches to instrument\n" " AFL_QUIET: suppress verbose output\n" " AFL_HARDEN: adds code hardening to catch memory bugs\n" diff --git a/src/afl-common.c b/src/afl-common.c index 1cc7f462..589aac71 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -523,7 +523,7 @@ void check_environment_vars(char **envp) { if (be_quiet) { return; } int index = 0, issue_detected = 0; - char *env, *val; + char *env, *val, *ignore = getenv("AFL_IGNORE_UNKNOWN_ENVS"); while ((env = envp[index++]) != NULL) { if (strncmp(env, "ALF_", 4) == 0 || strncmp(env, "_ALF", 4) == 0 || @@ -582,7 +582,7 @@ void check_environment_vars(char **envp) { } - if (match == 0) { + if (match == 0 && !ignore) { WARNF("Mistyped AFL environment variable: %s", env); issue_detected = 1; diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e4139857..e0ac8840 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -198,6 +198,7 @@ static void usage(u8 *argv0, int more_help) { "AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n" "AFL_HANG_TMOUT: override timeout value (in milliseconds)\n" "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n" + "AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" "AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n" "AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n" "AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n" diff --git a/src/afl-showmap.c b/src/afl-showmap.c index 0d2c5ceb..b40527d3 100644 --- a/src/afl-showmap.c +++ b/src/afl-showmap.c @@ -1168,8 +1168,9 @@ int main(int argc, char **argv_orig, char **envp) { } - stdin_file = at_file ? strdup(at_file) : (char *) - alloc_printf("%s/.afl-showmap-temp-%u", use_dir, (u32)getpid()); + stdin_file = at_file ? strdup(at_file) + : (char *)alloc_printf("%s/.afl-showmap-temp-%u", + use_dir, (u32)getpid()); unlink(stdin_file); atexit(at_exit_handler); fsrv->out_file = stdin_file; -- cgit 1.4.1 From dd2fd8027454acaa5c12beea6f7b721fc8794715 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Feb 2021 12:40:10 +0100 Subject: doc updates --- README.md | 5 +++-- docs/env_variables.md | 1 + qemu_mode/libqasan/README.md | 23 ++++++++++++++++------- qemu_mode/qemuafl | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 0778026c..00095390 100644 --- a/README.md +++ b/README.md @@ -751,6 +751,8 @@ campaigns as these are much shorter runnings. * for CMPLOG targets, 60% for `-l 2`, 40% for `-l 3` 4. Do *not* run any `-M` modes, just running `-S` modes is better for CI fuzzing. + `-M` enables deterministic fuzzing, old queue handling etc. which is good for + a fuzzing campaign but not good for short CI runs. ## Fuzzing binary-only targets @@ -788,8 +790,7 @@ If [afl-dyninst](https://github.com/vanhauser-thc/afl-dyninst) works for your binary, then you can use afl-fuzz normally and it will have twice the speed compared to qemu_mode (but slower than persistent mode). Note that several other binary rewriters exist, all with their advantages and -caveats. As rewriting a binary is much faster than Qemu this is a highly -recommended approach! +caveats. ### Unicorn diff --git a/docs/env_variables.md b/docs/env_variables.md index f7745247..f6ed12d0 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -451,6 +451,7 @@ checks or alter some of the more exotic semantics of the tool: `banner` corresponds to the name of the fuzzer provided through `-M/-S`. `afl_version` corresponds to the currently running afl version (e.g `++3.0c`). Default (empty/non present) will add no tags to the metrics. + See [rpc_statsd.md](rpc_statsd.md) for more information. - Setting `AFL_CRASH_EXITCODE` sets the exit code afl treats as crash. For example, if `AFL_CRASH_EXITCODE='-1'` is set, each input resulting diff --git a/qemu_mode/libqasan/README.md b/qemu_mode/libqasan/README.md index 83fb2442..4a241233 100644 --- a/qemu_mode/libqasan/README.md +++ b/qemu_mode/libqasan/README.md @@ -4,16 +4,25 @@ This library is the injected runtime used by QEMU AddressSanitizer (QASan). The original repository is [here](https://github.com/andreafioraldi/qasan). -The version embedded in qemuafl is an updated version of just the usermode part and this runtime is injected via LD_PRELOAD (so works just for dynamically linked binaries). +The version embedded in qemuafl is an updated version of just the usermode part +and this runtime is injected via LD_PRELOAD (so works just for dynamically +linked binaries). -The usage is super simple, just set the env var `AFL_USE_QASAN=1` when fuzzing in qemu mode (-Q). afl-fuzz will automatically set AFL_PRELOAD to load this library and enable the QASan instrumentation in afl-qemu-trace. +The usage is super simple, just set the env var `AFL_USE_QASAN=1` when fuzzing +in qemu mode (-Q). afl-fuzz will automatically set AFL_PRELOAD to load this +library and enable the QASan instrumentation in afl-qemu-trace. -For debugging purposes, we still suggest to run the original QASan as the stacktrace support for ARM (just a debug feature, it does not affect the bug finding capabilities during fuzzing) is WIP. +For debugging purposes, we still suggest to run the original QASan as the +stacktrace support for ARM (just a debug feature, it does not affect the bug +finding capabilities during fuzzing) is WIP. -### When I should use QASan? +### When should I use QASan? -If your target binary is PIC x86_64, you should also give a try to [retrowrite](https://github.com/HexHive/retrowrite) for static rewriting. +If your target binary is PIC x86_64, you should also give a try to +[retrowrite](https://github.com/HexHive/retrowrite) for static rewriting. -If it fails, or if your binary is for another architecture, or you want to use persistent and snapshot mode, AFL++ QASan mode is what you want/have to use. +If it fails, or if your binary is for another architecture, or you want to use +persistent and snapshot mode, AFL++ QASan mode is what you want/have to use. -Note that the overhead of libdislocator when combined with QEMU mode is much lower but it can catch less bugs. This is a short blanket, take your choice. +Note that the overhead of libdislocator when combined with QEMU mode is much +lower but it can catch less bugs. This is a short blanket, take your choice. diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 213f3b27..9a258d5b 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 213f3b27dd099ef352181c48cd75c0f20a73e3f0 +Subproject commit 9a258d5b7a38c045a6e385fcfcf80a746a60e557 -- cgit 1.4.1 From e3a5c31307f323452dc4b5288e0d19a02b596a33 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Feb 2021 13:25:15 +0100 Subject: llvm bug workaround for lto extint --- docs/Changelog.md | 1 + include/envs.h | 1 + instrumentation/cmplog-instructions-pass.cc | 39 ++++++++++++++++++++++++++--- qemu_mode/libqasan/dlmalloc.c | 5 ++++ src/afl-cc.c | 2 ++ src/afl-fuzz-redqueen.c | 1 + 6 files changed, 45 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 71ef4c2c..e2482f8f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,6 +49,7 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 + - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 4313e053..36667ebc 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,6 +16,7 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { + "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index b5cc1882..6b071b48 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -113,6 +113,8 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); + char *is_lto = getenv("_AFL_LTO_COMPILE"); + #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -265,10 +267,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16 || max_size % 8) { + if (!SI->getNumCases() || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { - // if (!be_quiet) errs() << "skip trivial switch..\n"; - continue; + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } } @@ -285,6 +297,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -301,6 +314,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -540,7 +554,22 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size % 8 || max_size < 16) { continue; } + if (!max_size || max_size < 16) { continue; } + + if (max_size % 8) { + + if (is_lto) { + + continue; // LTO cannot bitcast from _ExtInt() :( + + } else { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + + } if (max_size > 128) { @@ -552,6 +581,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -568,6 +598,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: + if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index bace0ff6..aff58ad5 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,6 +3917,7 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ + \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4128,6 +4129,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4144,6 +4146,7 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4156,12 +4159,14 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ + \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index d41f79a2..959c9a6f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,6 +1875,8 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); + if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } + if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index 527feef5..2b01ecad 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1533,6 +1533,7 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } + #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 6caec2169cef890ba8a62715c2c26cc0608626e3 Mon Sep 17 00:00:00 2001 From: Michael Rodler Date: Mon, 15 Feb 2021 19:14:28 +0100 Subject: Revert "llvm bug workaround for lto extint" This reverts commit e3a5c31307f323452dc4b5288e0d19a02b596a33. --- docs/Changelog.md | 1 - include/envs.h | 1 - instrumentation/cmplog-instructions-pass.cc | 39 +++-------------------------- qemu_mode/libqasan/dlmalloc.c | 5 ---- src/afl-cc.c | 2 -- src/afl-fuzz-redqueen.c | 1 - 6 files changed, 4 insertions(+), 45 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e2482f8f..71ef4c2c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -49,7 +49,6 @@ sending a mail to . CLANG for old afl-clang - fixed a potential crash in the LAF feature - workaround for llvm 13 - - workaround for llvm internal lto bug that lets not bitcast from _ExtInt() - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! See qemu_mode/libqasan/README.md diff --git a/include/envs.h b/include/envs.h index 36667ebc..4313e053 100644 --- a/include/envs.h +++ b/include/envs.h @@ -16,7 +16,6 @@ static char *afl_environment_deprecated[] = { static char *afl_environment_variables[] = { - "_AFL_LTO_COMPILE", "AFL_ALIGNED_ALLOC", "AFL_ALLOW_TMP", "AFL_ANALYZE_HEX", diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 9cd99f85..50ade9fd 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -114,8 +114,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { IntegerType *Int64Ty = IntegerType::getInt64Ty(C); IntegerType *Int128Ty = IntegerType::getInt128Ty(C); - char *is_lto = getenv("_AFL_LTO_COMPILE"); - #if LLVM_VERSION_MAJOR < 9 Constant * #else @@ -268,20 +266,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { + if (!SI->getNumCases() || max_size < 16 || max_size % 8) { - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } + // if (!be_quiet) errs() << "skip trivial switch..\n"; + continue; } @@ -298,7 +286,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( max_size = 128; do_cast = 1; @@ -315,7 +302,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast _ExtInt() :( cast_size = 128; do_cast = 1; @@ -504,22 +490,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size < 16) { continue; } - - if (max_size % 8) { - - if (is_lto) { - - continue; // LTO cannot bitcast from _ExtInt() :( - - } else { - - max_size = (((max_size / 8) + 1) * 8); - do_cast = 1; - - } - - } + if (!max_size || max_size % 8 || max_size < 16) { continue; } if (max_size > 128) { @@ -531,7 +502,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( max_size = 128; do_cast = 1; @@ -548,7 +518,6 @@ bool CmpLogInstructions::hookInstrs(Module &M) { cast_size = max_size; break; default: - if (is_lto) { continue; } // LTO cannot bitcast from _ExtInt() :( cast_size = 128; do_cast = 1; diff --git a/qemu_mode/libqasan/dlmalloc.c b/qemu_mode/libqasan/dlmalloc.c index aff58ad5..bace0ff6 100644 --- a/qemu_mode/libqasan/dlmalloc.c +++ b/qemu_mode/libqasan/dlmalloc.c @@ -3917,7 +3917,6 @@ static void internal_malloc_stats(mstate m) { \ } else if (RTCHECK(B == smallbin_at(M, I) || \ \ - \ (ok_address(M, B) && B->fd == P))) { \ \ F->bk = B; \ @@ -4129,7 +4128,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ if (R != 0) { \ \ @@ -4146,7 +4144,6 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ @@ -4159,14 +4156,12 @@ static void internal_malloc_stats(mstate m) { \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ \ } else \ \ - \ CORRUPTION_ERROR_ACTION(M); \ \ } \ diff --git a/src/afl-cc.c b/src/afl-cc.c index 959c9a6f..d41f79a2 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1875,8 +1875,6 @@ int main(int argc, char **argv, char **envp) { edit_params(argc, argv, envp); - if (lto_mode) { setenv("_AFL_LTO_COMPILE", "1", 1); } - if (debug) { DEBUGF("cd '%s';", getthecwd()); diff --git a/src/afl-fuzz-redqueen.c b/src/afl-fuzz-redqueen.c index cf65d3c1..275af9c8 100644 --- a/src/afl-fuzz-redqueen.c +++ b/src/afl-fuzz-redqueen.c @@ -1547,7 +1547,6 @@ static u8 cmp_fuzz(afl_state_t *afl, u32 key, u8 *orig_buf, u8 *buf, u8 *cbuf, is_n = 1; } - #endif for (i = 0; i < loggeds; ++i) { -- cgit 1.4.1 From 80bdbf7be0a1e7bd54b24d4376048d85fb403bf6 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Feb 2021 09:54:15 +0100 Subject: minor cmplog fix --- docs/Changelog.md | 1 + instrumentation/cmplog-instructions-pass.cc | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 71ef4c2c..477498d0 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -48,6 +48,7 @@ sending a mail to . support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang - fixed a potential crash in the LAF feature + - workaround for llvm bitcast lto bug - workaround for llvm 13 - qemuafl - QASan (address sanitizer for Qemu) ported to qemuafl! diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 50ade9fd..dbca9afa 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -266,13 +266,20 @@ bool CmpLogInstructions::hookInstrs(Module &M) { unsigned int max_size = Val->getType()->getIntegerBitWidth(), cast_size; unsigned char do_cast = 0; - if (!SI->getNumCases() || max_size < 16 || max_size % 8) { + if (!SI->getNumCases() || max_size < 16) { // if (!be_quiet) errs() << "skip trivial switch..\n"; continue; } + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } + IRBuilder<> IRB(SI->getParent()); IRB.SetInsertPoint(SI); @@ -310,8 +317,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) { Value *CompareTo = Val; if (do_cast) { + CompareTo = IRB.CreateIntCast(CompareTo, IntegerType::get(C, cast_size), false); + } for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; @@ -331,8 +340,10 @@ bool CmpLogInstructions::hookInstrs(Module &M) { Value *new_param = cint; if (do_cast) { + new_param = IRB.CreateIntCast(cint, IntegerType::get(C, cast_size), false); + } if (new_param) { @@ -490,7 +501,14 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - if (!max_size || max_size % 8 || max_size < 16) { continue; } + if (!max_size || max_size < 16) { continue; } + + if (max_size % 8) { + + max_size = (((max_size / 8) + 1) * 8); + do_cast = 1; + + } if (max_size > 128) { -- cgit 1.4.1 From a252943236b12c080248747bee06c9c5084b871e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 12:59:01 +0100 Subject: another fix for disabled entries --- docs/Changelog.md | 1 + src/afl-fuzz.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 477498d0..a006fccb 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -32,6 +32,7 @@ sending a mail to . after every queue entry as this can take very long time otherwise - better detection if a target needs a large shared map - fix for `-Z` + - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - afl-cc diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index e2db029d..24d77cc9 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1770,12 +1770,15 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->old_seed_selection)) { afl->current_entry = 0; - while (unlikely(afl->queue_buf[afl->current_entry]->disabled)) { + while (unlikely(afl->current_entry < afl->queued_paths && + afl->queue_buf[afl->current_entry]->disabled)) { ++afl->current_entry; } + if (afl->current_entry >= afl->queued_paths) { afl->current_entry = 0; } + afl->queue_cur = afl->queue_buf[afl->current_entry]; if (unlikely(seek_to)) { -- cgit 1.4.1 From fb2a6b6941ffb6cf575d2a772c6e0d47b49835ee Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 22 Feb 2021 16:56:35 +0100 Subject: minimum sync time --- docs/Changelog.md | 1 + include/afl-fuzz.h | 1 + include/config.h | 5 +++++ src/afl-fuzz-run.c | 2 ++ src/afl-fuzz.c | 15 ++++++++++++--- 5 files changed, 21 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index a006fccb..d8587334 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -35,6 +35,7 @@ sending a mail to . - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis + - added minimum SYNC_TIME to include/config.h (30 minutes default) - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 10d94fed..e191543a 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -570,6 +570,7 @@ typedef struct afl_state { blocks_eff_total, /* Blocks subject to effector maps */ blocks_eff_select, /* Blocks selected as fuzzable */ start_time, /* Unix start time (ms) */ + last_sync_time, /* Time of last sync */ last_path_time, /* Time for most recent path (ms) */ last_crash_time, /* Time for most recent crash (ms) */ last_hang_time; /* Time for most recent hang (ms) */ diff --git a/include/config.h b/include/config.h index 535ce0d3..083cad23 100644 --- a/include/config.h +++ b/include/config.h @@ -280,6 +280,11 @@ #define SYNC_INTERVAL 8 +/* Sync time (minimum time between syncing in ms, time is halfed for -M main + nodes): */ + +#define SYNC_TIME 18000000LLU /* 18000000 = 30 minutes */ + /* Output directory reuse grace period (minutes): */ #define OUTPUT_GRACE 25 diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 97cb7415..0b84a542 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -707,6 +707,8 @@ void sync_fuzzers(afl_state_t *afl) { if (afl->foreign_sync_cnt) read_foreign_testcases(afl, 0); + afl->last_sync_time = get_cur_time(); + } /* Trim all new test cases to save cycles when doing deterministic checks. The diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 9137dc23..f83aac9e 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1986,15 +1986,24 @@ int main(int argc, char **argv_orig, char **envp) { if (unlikely(afl->is_main_node)) { - if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + if (unlikely(get_cur_time() > + (SYNC_TIME >> 1) + afl->last_sync_time)) { - sync_fuzzers(afl); + if (!(sync_interval_cnt++ % (SYNC_INTERVAL / 3))) { + + sync_fuzzers(afl); + + } } } else { - if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + if (unlikely(get_cur_time() > SYNC_TIME + afl->last_sync_time)) { + + if (!(sync_interval_cnt++ % SYNC_INTERVAL)) { sync_fuzzers(afl); } + + } } -- cgit 1.4.1 From a090b2013f2ea2da72d64b0bae4a75d57a268c5b Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 24 Feb 2021 12:02:44 +0100 Subject: docs --- docs/docs.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/docs.md (limited to 'docs') diff --git a/docs/docs.md b/docs/docs.md new file mode 100644 index 00000000..d4fd939e --- /dev/null +++ b/docs/docs.md @@ -0,0 +1,92 @@ +# Restructure afl++'s documentation + +## About us + +We are dedicated to everything around fuzzing, our main and most well known +contribution is the fuzzer `afl++` which is part of all major Unix +distributions (e.g. Debian, Arch, FreeBSD, etc.) and is deployed on Google's +oss-fuzz and clusterfuzz. It is rated the top fuzzer on Google's fuzzbench. + +We are four individuals from Europe supported by a large community. + +All our tools are open source. + +## About the afl++ fuzzer project + +afl++ inherited it's documentation from the original Google afl project. +Since then it has been massively improved - feature and performance wise - +and although the documenation has likewise been continued it has grown out +of proportion. +The documentation is done by non-natives to the English language, plus +none of us has a writer background. + +We see questions on afl++ usage on mailing lists (e.g. afl-users), discord +channels, web forums and as issues in our repository. + +This only increases as afl++ has been on the top of Google's fuzzbench +statistics (which measures the performance of fuzzers) and is now being +integrated in Google's oss-fuzz and clusterfuzz - and is in many Unix +packaging repositories, e.g. Debian, FreeBSD, etc. + +afl++ now has 44 (!) documentation files with 13k total lines of content. +This is way too much. + +Hence afl++ needs a complete overhaul of it's documentation, both on a +organisation/structural level as well as the content. + +Overall the following actions have to be performed: + * Create a better structure of documentation so it is easier to find the + information that is being looked for, combining and/or splitting up the + existing documents as needed. + * Rewrite some documentation to remove duplication. Several information is + present several times in the documentation. These should be removed to + where needed so that we have as little bloat as possible. + * The documents have been written and modified by a lot of different people, + most of them non-native English speaker. Hence an overall review where + parts should be rewritten has to be performed and then the rewrite done. + * Create a cheat-sheet for a very short best-setup build and run of afl++ + * Pictures explain more than 1000 words. We need at least 4 images that + explain the workflow with afl++: + - the build workflow + - the fuzzing workflow + - the fuzzing campaign management workflow + - the overall workflow that is an overview of the above + - maybe more? where the technical writes seems it necessary for + understanding. + +Requirements: + * Documentation has to be in Markdown format + * Images have to be either in SVG or PNG format. + * All documentation should be (moved) in(to) docs/ + +The project does not require writing new documentation or tutorials beside the +cheat sheet. The technical information for the cheat sheet will be provided by +us. + +## Project Budget + +We have zero experience with technical writers, so this is very hard for us +to calculate. We expect it to be a lot of work though because of the amount +of documentation we have that needs to be restructured and rewritten +(44 documents with 13k total lines of content). + +Technical Writer 10000$ +Volunteer stipends 0$ (waved) +T-Shirts for the top 10 contributors and helpers to this documentation project: + 10 afl++ logo t-shirts 20$ each 200$ + 8 shipping cost of t-shirts 10$ each 80$ + (2 won't need to be shipped) + +Total: 10.280$ + +## Additional Information + +We have participated in Google Summer of Code in 2020 and hope to be selected +again in 2021. + +We have no experience with a technical writer, but we will support that person +with video calls, chats, emails and messaging, provide all necessary information +and write technical contents that is required for the success of this project. +It is clear to us that a technical writer knows how to write, but cannot know +the technical details in a complex tooling like in afl++. This guidance, input, +etc. has to come from us. -- cgit 1.4.1 From cc7c651dc97f5567af33164d396b390d36b46049 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 24 Feb 2021 18:07:08 +0100 Subject: tidied up env suggestions --- docs/Changelog.md | 3 ++- src/afl-common.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index d8587334..1260531e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -36,6 +36,7 @@ sending a mail to . - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - added minimum SYNC_TIME to include/config.h (30 minutes default) + - printing suggestions for mistyped `AFL_` env variables - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a @@ -68,7 +69,7 @@ sending a mail to . - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - - Added AFL_KILL_SIGNAL env variable (thanks @v-p-b) + - Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b) - @Edznux added a nice documentation on how to use rpc.statsd with afl++ in docs/rpc_statsd.md, thanks! diff --git a/src/afl-common.c b/src/afl-common.c index ce63c262..1628e517 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -529,6 +529,8 @@ static int string_distance_levenshtein(char *s1, char *s2) { s1len = strlen(s1); s2len = strlen(s2); unsigned int column[s1len + 1]; + column[s1len] = 1; + for (y = 1; y <= s1len; y++) column[y] = y; for (x = 1; x <= s2len; x++) { @@ -608,6 +610,8 @@ void print_suggested_envs(char *mispelled_env) { }; + ck_free(reduced); + } if (found) goto cleanup; -- cgit 1.4.1 From e2b4bc93103c1d311315e324833b7cb71d0677cc Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 25 Feb 2021 11:02:59 +0100 Subject: update changelog --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1260531e..9a61fac3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -64,6 +64,7 @@ sending a mail to . - Improved rust bindings - Added a new example harness to compare python, c, and rust bindings - afl-cmin and afl-showmap now support the -f option + - afl_plot now also generates a graph on the discovered edges - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars. - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. -- cgit 1.4.1 From 7ae7b0f3732cfd9d82cf9403818012d00faa07c3 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 25 Feb 2021 11:08:37 +0100 Subject: docs update --- docs/docs.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/docs.md b/docs/docs.md index d4fd939e..0f98bd54 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -67,17 +67,16 @@ us. We have zero experience with technical writers, so this is very hard for us to calculate. We expect it to be a lot of work though because of the amount -of documentation we have that needs to be restructured and rewritten +of documentation we have that needs to be restructured and partially rewritten (44 documents with 13k total lines of content). Technical Writer 10000$ Volunteer stipends 0$ (waved) T-Shirts for the top 10 contributors and helpers to this documentation project: 10 afl++ logo t-shirts 20$ each 200$ - 8 shipping cost of t-shirts 10$ each 80$ - (2 won't need to be shipped) + 10 shipping cost of t-shirts 10$ each 100$ -Total: 10.280$ +Total: 10.300$ ## Additional Information -- cgit 1.4.1 From ee0ca07f3c9f7c5971440f5dca70a2ee6f37584d Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 25 Feb 2021 12:19:46 +0100 Subject: changing the -t ...+ meaning to "auto-calculate buth this is the max" --- README.md | 7 ++++++- docs/Changelog.md | 21 +++++++++++++-------- src/afl-fuzz-init.c | 33 ++++++++++++--------------------- src/afl-fuzz-stats.c | 16 ++++++++-------- src/afl-fuzz.c | 33 +++++++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 42 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 0539752c..119426f6 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,12 @@ For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` with `AFL_LLVM_CMPLOG=1`. -## Major changes in afl++ 3.0 +## Major changes in afl++ 3.0 + 3.1 + +With afl++ 3.1 we introduced the following changes from previous behaviours: + * The '+' feature of the '-t' option now means to auto-calculate the timeout + with the value given being the maximum timeout. The original meaning of + "skipping timeouts instead of abort" is now inherent to the -t option. With afl++ 3.0 we introduced changes that break some previous afl and afl++ behaviours and defaults: diff --git a/docs/Changelog.md b/docs/Changelog.md index 9a61fac3..c4347baf 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,26 +16,31 @@ sending a mail to . to be placed in the source code. Check out instrumentation/README.instrument_list.md - afl-fuzz - - Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on start - the target map size + - Making AFL_MAP_SIZE (mostly) obsolete - afl-fuzz now learns on + start the target map size - upgraded cmplog/redqueen: solving for floating point, solving transformations (e.g. toupper, tolower, to/from hex, xor, arithmetics, etc.). This is costly hence new command line option - `-l` that sets the intensity (values 1 to 3). Recommended is 1 or 2. - - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial testcases from - `-i` or resumes (as these have most likely already been done) + `-l` that sets the intensity (values 1 to 3). Recommended is 2. + - added `AFL_CMPLOG_ONLY_NEW` to not use cmplog on initial seeds + from `-i` or resumes (these have most likely already been done) - fix crash for very, very fast targets+systems (thanks to mhlakhani for reporting) - on restarts (`-i`)/autoresume (AFL_AUTORESUME) the stats are now reloaded and used, thanks to Vimal Joseph for this patch! - - if deterministic mode is active (`-D`, or `-M` without `-d`) then we sync - after every queue entry as this can take very long time otherwise + - changed the meaning of '+' of the '-t' option, it now means to + auto-calculate the timeout with the value given being the max + timeout. The original meaning of skipping timeouts instead of + abort is now inherent to the -t option. + - if deterministic mode is active (`-D`, or `-M` without `-d`) then + we sync after every queue entry as this can take very long time + otherwise + - added minimum SYNC_TIME to include/config.h (30 minutes default) - better detection if a target needs a large shared map - fix for `-Z` - fixed a few crashes - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - - added minimum SYNC_TIME to include/config.h (30 minutes default) - printing suggestions for mistyped `AFL_` env variables - afl-cc - allow instrumenting LLVMFuzzerTestOneInput diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index d85a83e0..3dbc4c65 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -882,32 +882,23 @@ void perform_dry_run(afl_state_t *afl) { if (afl->timeout_given) { - /* The -t nn+ syntax in the command line sets afl->timeout_given to - '2' and instructs afl-fuzz to tolerate but skip queue entries that - time out. */ + /* if we have a timeout but a timeout value was given then always + skip. The '+' meaning has been changed! */ + WARNF("Test case results in a timeout (skipping)"); + ++cal_failures; + q->cal_failed = CAL_CHANCES; + q->disabled = 1; + q->perf_score = 0; - if (afl->timeout_given > 1) { + if (!q->was_fuzzed) { - WARNF("Test case results in a timeout (skipping)"); - q->cal_failed = CAL_CHANCES; - ++cal_failures; - break; + q->was_fuzzed = 1; + --afl->pending_not_fuzzed; + --afl->active_paths; } - SAYF("\n" cLRD "[-] " cRST - "The program took more than %u ms to process one of the initial " - "test cases.\n" - " Usually, the right thing to do is to relax the -t option - " - "or to delete it\n" - " altogether and allow the fuzzer to auto-calibrate. That " - "said, if you know\n" - " what you are doing and want to simply skip the unruly test " - "cases, append\n" - " '+' at the end of the value passed to -t ('-t %u+').\n", - afl->fsrv.exec_tmout, afl->fsrv.exec_tmout); - - FATAL("Test case '%s' results in a timeout", fn); + break; } else { diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c index 42c71b05..bd856088 100644 --- a/src/afl-fuzz-stats.c +++ b/src/afl-fuzz-stats.c @@ -388,13 +388,13 @@ void maybe_update_plot_file(afl_state_t *afl, u32 t_bytes, double bitmap_cvg, favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, execs_per_sec, edges_found */ - fprintf( - afl->fsrv.plot_file, - "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, %u\n", - get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, - afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, - bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, eps, - afl->plot_prev_ed, t_bytes); /* ignore errors */ + fprintf(afl->fsrv.plot_file, + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f, %llu, " + "%u\n", + get_cur_time() / 1000, afl->queue_cycle - 1, afl->current_entry, + afl->queued_paths, afl->pending_not_fuzzed, afl->pending_favored, + bitmap_cvg, afl->unique_crashes, afl->unique_hangs, afl->max_depth, + eps, afl->plot_prev_ed, t_bytes); /* ignore errors */ fflush(afl->fsrv.plot_file); @@ -1219,7 +1219,7 @@ void show_init_stats(afl_state_t *afl) { stringify_int(IB(0), min_us), stringify_int(IB(1), max_us), stringify_int(IB(2), avg_us)); - if (!afl->timeout_given) { + if (afl->timeout_given != 1) { /* Figure out the appropriate timeout. The basic idea is: 5x average or 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 5810e9a9..a02eadb2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -103,9 +103,10 @@ static void usage(u8 *argv0, int more_help) { " quad -- see docs/power_schedules.md\n" " -f file - location read by the fuzzed program (default: stdin " "or @@)\n" - " -t msec - timeout for each run (auto-scaled, 50-... ms, default " - "%u ms)\n" - " add a '+' to skip over seeds running longer.\n" + " -t msec - timeout for each run (auto-scaled, default %u ms). " + "Add a '+'\n" + " to auto-calculate the timeout, the value being the " + "maximum.\n" " -m megs - memory limit for child process (%u MB, 0 = no limit " "[default])\n" " -Q - use binary-only instrumentation (QEMU mode)\n" @@ -1453,7 +1454,7 @@ int main(int argc, char **argv_orig, char **envp) { } - if (!afl->timeout_given) { find_timeout(afl); } + if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! if ((afl->tmp_dir = afl->afl_env.afl_tmpdir) != NULL && !afl->in_place_resume) { @@ -1718,6 +1719,30 @@ int main(int argc, char **argv_orig, char **envp) { } + if (afl->timeout_given == 2) { // -t ...+ option + + if (valid_seeds == 1) { + + WARNF( + "Only one valid seed is present, auto-calculating the timeout is " + "disabled!"); + afl->timeout_given = 1; + + } else { + + u64 max_ms = 0; + + for (entry = 0; entry < afl->queued_paths; ++entry) + if (!afl->queue_buf[entry]->disabled) + if (afl->queue_buf[entry]->exec_us > max_ms) + max_ms = afl->queue_buf[entry]->exec_us; + + afl->fsrv.exec_tmout = max_ms; + + } + + } + show_init_stats(afl); if (unlikely(afl->old_seed_selection)) seek_to = find_start_position(afl); -- cgit 1.4.1 From a5da9ce42cab1eab93cf80ca744944ae26e6ab58 Mon Sep 17 00:00:00 2001 From: julihoh Date: Sat, 27 Feb 2021 15:05:13 +0100 Subject: custom mutator rust support (#752) * custom mutator rust support * clarify how to view documentation for rust mutators * remove `FuzzResult` hack and clarify lifetimes of CustomMutator::fuzz * rename TErr associated tyep to Error to be more idiomatic * fix warnings * add example for fallible custom mutator * make Fallible Custom Mutator the default and implement it's handle_err method by default * rename CustomMutator::handle_err to handle_error * add example mutator using lain --- custom_mutators/rust/.gitignore | 10 + custom_mutators/rust/Cargo.toml | 7 + custom_mutators/rust/README.md | 9 + custom_mutators/rust/custom_mutator-sys/Cargo.toml | 12 + custom_mutators/rust/custom_mutator-sys/build.rs | 42 ++ custom_mutators/rust/custom_mutator-sys/src/lib.rs | 5 + custom_mutators/rust/custom_mutator-sys/wrapper.h | 4 + custom_mutators/rust/custom_mutator/Cargo.toml | 13 + custom_mutators/rust/custom_mutator/src/lib.rs | 637 +++++++++++++++++++++ custom_mutators/rust/example/Cargo.toml | 13 + custom_mutators/rust/example/src/lib.rs | 50 ++ custom_mutators/rust/example_lain/Cargo.toml | 14 + custom_mutators/rust/example_lain/rust-toolchain | 1 + custom_mutators/rust/example_lain/src/lib.rs | 60 ++ docs/custom_mutators.md | 5 + 15 files changed, 882 insertions(+) create mode 100644 custom_mutators/rust/.gitignore create mode 100644 custom_mutators/rust/Cargo.toml create mode 100644 custom_mutators/rust/README.md create mode 100644 custom_mutators/rust/custom_mutator-sys/Cargo.toml create mode 100644 custom_mutators/rust/custom_mutator-sys/build.rs create mode 100644 custom_mutators/rust/custom_mutator-sys/src/lib.rs create mode 100644 custom_mutators/rust/custom_mutator-sys/wrapper.h create mode 100644 custom_mutators/rust/custom_mutator/Cargo.toml create mode 100644 custom_mutators/rust/custom_mutator/src/lib.rs create mode 100644 custom_mutators/rust/example/Cargo.toml create mode 100644 custom_mutators/rust/example/src/lib.rs create mode 100644 custom_mutators/rust/example_lain/Cargo.toml create mode 100644 custom_mutators/rust/example_lain/rust-toolchain create mode 100644 custom_mutators/rust/example_lain/src/lib.rs (limited to 'docs') diff --git a/custom_mutators/rust/.gitignore b/custom_mutators/rust/.gitignore new file mode 100644 index 00000000..088ba6ba --- /dev/null +++ b/custom_mutators/rust/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/custom_mutators/rust/Cargo.toml b/custom_mutators/rust/Cargo.toml new file mode 100644 index 00000000..003bc58a --- /dev/null +++ b/custom_mutators/rust/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "custom_mutator-sys", + "custom_mutator", + "example", + "example_lain", +] \ No newline at end of file diff --git a/custom_mutators/rust/README.md b/custom_mutators/rust/README.md new file mode 100644 index 00000000..119ac790 --- /dev/null +++ b/custom_mutators/rust/README.md @@ -0,0 +1,9 @@ +Bindings to create custom mutators in Rust. + +These bindings are documented with rustdoc. To view the documentation run +```cargo doc -p custom_mutator --open```. + +A minimal example can be found in `example`. + +An example using [lain](https://github.com/microsoft/lain) can be found in `example_lain`. +Since lain requires a nightly rust toolchain, you need to set one up before you can play with it. diff --git a/custom_mutators/rust/custom_mutator-sys/Cargo.toml b/custom_mutators/rust/custom_mutator-sys/Cargo.toml new file mode 100644 index 00000000..104f7df0 --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "custom_mutator-sys" +version = "0.1.0" +authors = ["Julius Hohnerlein "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +bindgen = "0.56" diff --git a/custom_mutators/rust/custom_mutator-sys/build.rs b/custom_mutators/rust/custom_mutator-sys/build.rs new file mode 100644 index 00000000..3c88a90d --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/build.rs @@ -0,0 +1,42 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +// this code is largely taken straight from the handbook: https://github.com/fitzgen/bindgen-tutorial-bzip2-sys +fn main() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=wrapper.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("wrapper.h") + .whitelist_type("afl_state_t") + .blacklist_type(r"u\d+") + .opaque_type(r"_.*") + .opaque_type("FILE") + .opaque_type("in_addr(_t)?") + .opaque_type("in_port(_t)?") + .opaque_type("sa_family(_t)?") + .opaque_type("sockaddr_in(_t)?") + .opaque_type("time_t") + .rustfmt_bindings(true) + .size_t_is_usize(true) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/custom_mutators/rust/custom_mutator-sys/src/lib.rs b/custom_mutators/rust/custom_mutator-sys/src/lib.rs new file mode 100644 index 00000000..a38a13a8 --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/custom_mutators/rust/custom_mutator-sys/wrapper.h b/custom_mutators/rust/custom_mutator-sys/wrapper.h new file mode 100644 index 00000000..81cdb90f --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/wrapper.h @@ -0,0 +1,4 @@ +#include "../../../include/afl-fuzz.h" +#include "../../../include/common.h" +#include "../../../include/config.h" +#include "../../../include/debug.h" diff --git a/custom_mutators/rust/custom_mutator/Cargo.toml b/custom_mutators/rust/custom_mutator/Cargo.toml new file mode 100644 index 00000000..2d3cdbfa --- /dev/null +++ b/custom_mutators/rust/custom_mutator/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "custom_mutator" +version = "0.1.0" +authors = ["Julius Hohnerlein "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +afl_internals = ["custom_mutator-sys"] + +[dependencies] +custom_mutator-sys = { path = "../custom_mutator-sys", optional=true } diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs new file mode 100644 index 00000000..0fb007ab --- /dev/null +++ b/custom_mutators/rust/custom_mutator/src/lib.rs @@ -0,0 +1,637 @@ +//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust. +//! +//! # Usage +//! AFL++ custom mutators are expected to be dynamic libraries which expose a set of symbols. +//! Check out [`CustomMutator`] to see which functions of the API are supported. +//! Then use [`export_mutator`] to export the correct symbols for your mutator. +//! In order to use the mutator, your crate needs to be a library crate and have a `crate-type` of `cdylib`. +//! Putting +//! ```yaml +//! [lib] +//! crate-type = ["cdylib"] +//! ``` +//! into your `Cargo.toml` should do the trick. +//! The final executable can be found in `target/(debug|release)/your_crate_name.so`. +//! # Example +//! See [`export_mutator`] for an example. +//! +//! # On `panic`s +//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++. +//! +//! # Access to AFL++ internals +//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state. +//! The state is passed to [`CustomMutator::init`], when the feature is activated. +//! +//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._ +use std::{ffi::CStr, fmt::Debug, os::raw::c_uint}; + +#[cfg(feature = "afl_internals")] +#[doc(hidden)] +pub use custom_mutator_sys::afl_state; + +#[allow(unused_variables)] +#[doc(hidden)] +pub trait RawCustomMutator { + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: c_uint) -> Self + where + Self: Sized; + #[cfg(not(feature = "afl_internals"))] + fn init(seed: c_uint) -> Self + where + Self: Sized; + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Option<&'b [u8]>; + + fn fuzz_count(&mut self, buffer: &[u8]) -> u32 { + 1 + } + + fn queue_new_entry(&mut self, filename_new_queue: &CStr, _filename_orig_queue: Option<&CStr>) {} + + fn queue_get(&mut self, filename: &CStr) -> bool { + true + } + + fn describe(&mut self, max_description: usize) -> Option<&CStr> { + None + } + + fn introspection(&mut self) -> Option<&CStr> { + None + } + + /*fn post_process(&self, buffer: &[u8], unsigned char **out_buf)-> usize; + int afl_custom_init_trim(&self, buffer: &[u8]); + size_t afl_custom_trim(&self, unsigned char **out_buf); + int afl_custom_post_trim(&self, unsigned char success); + size_t afl_custom_havoc_mutation(&self, buffer: &[u8], unsigned char **out_buf, size_t max_size); + unsigned char afl_custom_havoc_mutation_probability(&self);*/ +} + +/// Wrappers for the custom mutator which provide the bridging between the C API and CustomMutator. +/// These wrappers are not intended to be used directly, rather export_mutator will use them to publish the custom mutator C API. +#[doc(hidden)] +pub mod wrappers { + #[cfg(feature = "afl_internals")] + use custom_mutator_sys::afl_state; + + use core::slice; + use std::{ + any::Any, + convert::TryInto, + ffi::{c_void, CStr}, + mem::ManuallyDrop, + os::raw::{c_char, c_uint}, + panic::catch_unwind, + process::abort, + ptr::null, + }; + + use crate::RawCustomMutator; + + /// A structure to be used as the data pointer for our custom mutator. This was used as additional storage and is kept for now in case its needed later. + /// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]). + struct FFIContext { + mutator: M, + } + + impl FFIContext { + fn from(ptr: *mut c_void) -> ManuallyDrop> { + assert!(!ptr.is_null()); + ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) }) + } + + fn into_ptr(self: Box) -> *const c_void { + Box::into_raw(self) as *const c_void + } + + #[cfg(feature = "afl_internals")] + fn new(afl: &'static afl_state, seed: c_uint) -> Box { + Box::new(Self { + mutator: M::init(afl, seed), + }) + } + #[cfg(not(feature = "afl_internals"))] + fn new(seed: c_uint) -> Box { + Box::new(Self { + mutator: M::init(seed), + }) + } + } + + /// panic handler called for every panic + fn panic_handler(method: &str, panic_info: Box) -> ! { + use std::ops::Deref; + let cause = panic_info + .downcast_ref::() + .map(String::deref) + .unwrap_or_else(|| { + panic_info + .downcast_ref::<&str>() + .copied() + .unwrap_or("") + }); + eprintln!("A panic occurred at {}: {}", method, cause); + abort() + } + + /// Internal function used in the macro + #[cfg(not(feature = "afl_internals"))] + pub fn afl_custom_init_(seed: c_uint) -> *const c_void { + match catch_unwind(|| FFIContext::::new(seed).into_ptr()) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_init", err), + } + } + + /// Internal function used in the macro + #[cfg(feature = "afl_internals")] + pub fn afl_custom_init_( + afl: Option<&'static afl_state>, + seed: c_uint, + ) -> *const c_void { + match catch_unwind(|| { + let afl = afl.expect("mutator func called with NULL afl"); + FFIContext::::new(afl, seed).into_ptr() + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_init", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_fuzz_( + data: *mut c_void, + buf: *mut u8, + buf_size: usize, + out_buf: *mut *const u8, + add_buf: *mut u8, + add_buf_size: usize, + max_size: usize, + ) -> usize { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + if buf.is_null() { + panic!("null buf passed to afl_custom_fuzz") + } + if out_buf.is_null() { + panic!("null out_buf passed to afl_custom_fuzz") + } + let buff_slice = slice::from_raw_parts_mut(buf, buf_size); + let add_buff_slice = if add_buf.is_null() { + None + } else { + Some(slice::from_raw_parts(add_buf, add_buf_size)) + }; + match context + .mutator + .fuzz(buff_slice, add_buff_slice, max_size.try_into().unwrap()) + { + Some(buffer) => { + *out_buf = buffer.as_ptr(); + buffer.len().try_into().unwrap() + } + None => { + // return the input buffer with 0-length to let AFL skip this mutation attempt + *out_buf = buf; + 0 + } + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_fuzz", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_fuzz_count_( + data: *mut c_void, + buf: *const u8, + buf_size: usize, + ) -> u32 { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + if buf.is_null() { + panic!("null buf passed to afl_custom_fuzz") + } + let buf_slice = slice::from_raw_parts(buf, buf_size); + // see https://doc.rust-lang.org/nomicon/borrow-splitting.html + let ctx = &mut **context; + let mutator = &mut ctx.mutator; + mutator.fuzz_count(buf_slice) + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_fuzz_count", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_queue_new_entry_( + data: *mut c_void, + filename_new_queue: *const c_char, + filename_orig_queue: *const c_char, + ) { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + if filename_new_queue.is_null() { + panic!("received null filename_new_queue in afl_custom_queue_new_entry"); + } + let filename_new_queue = unsafe { CStr::from_ptr(filename_new_queue) }; + let filename_orig_queue = if !filename_orig_queue.is_null() { + Some(unsafe { CStr::from_ptr(filename_orig_queue) }) + } else { + None + }; + context + .mutator + .queue_new_entry(filename_new_queue, filename_orig_queue); + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_queue_new_entry", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_deinit_(data: *mut c_void) { + match catch_unwind(|| { + // drop the context + ManuallyDrop::into_inner(FFIContext::::from(data)); + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_deinit", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_introspection_(data: *mut c_void) -> *const c_char { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + if let Some(res) = context.mutator.introspection() { + res.as_ptr() + } else { + null() + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_introspection", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_describe_( + data: *mut c_void, + max_description_len: usize, + ) -> *const c_char { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + if let Some(res) = context.mutator.describe(max_description_len) { + res.as_ptr() + } else { + null() + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_describe", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_queue_get_( + data: *mut c_void, + filename: *const c_char, + ) -> u8 { + match catch_unwind(|| { + let mut context = FFIContext::::from(data); + assert!(!filename.is_null()); + + context + .mutator + .queue_get(unsafe { CStr::from_ptr(filename) }) as u8 + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_queue_get", err), + } + } +} + +/// exports the given Mutator as a custom mutator as the C interface that AFL++ expects. +/// It is not possible to call this macro multiple times, because it would define the custom mutator symbols multiple times. +/// # Example +/// ``` +/// # #[macro_use] extern crate custom_mutator; +/// # #[cfg(feature = "afl_internals")] +/// # use custom_mutator::afl_state; +/// # use custom_mutator::CustomMutator; +/// # use std::os::raw::c_uint; +/// struct MyMutator; +/// impl CustomMutator for MyMutator { +/// /// ... +/// # type Error = (); +/// # #[cfg(feature = "afl_internals")] +/// # fn init(_afl_state: &afl_state, _seed: c_uint) -> Result {unimplemented!()} +/// # #[cfg(not(feature = "afl_internals"))] +/// # fn init(_seed: c_uint) -> Result {unimplemented!()} +/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result,()> {unimplemented!()} +/// } +/// export_mutator!(MyMutator); +/// ``` +#[macro_export] +macro_rules! export_mutator { + ($mutator_type:ty) => { + #[cfg(feature = "afl_internals")] + #[no_mangle] + pub extern "C" fn afl_custom_init( + afl: ::std::option::Option<&'static $crate::afl_state>, + seed: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_void { + $crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed) + } + + #[cfg(not(feature = "afl_internals"))] + #[no_mangle] + pub extern "C" fn afl_custom_init( + _afl: *const ::std::os::raw::c_void, + seed: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_void { + $crate::wrappers::afl_custom_init_::<$mutator_type>(seed) + } + + #[no_mangle] + pub extern "C" fn afl_custom_fuzz_count( + data: *mut ::std::os::raw::c_void, + buf: *const u8, + buf_size: usize, + ) -> u32 { + unsafe { + $crate::wrappers::afl_custom_fuzz_count_::<$mutator_type>(data, buf, buf_size) + } + } + + #[no_mangle] + pub extern "C" fn afl_custom_fuzz( + data: *mut ::std::os::raw::c_void, + buf: *mut u8, + buf_size: usize, + out_buf: *mut *const u8, + add_buf: *mut u8, + add_buf_size: usize, + max_size: usize, + ) -> usize { + unsafe { + $crate::wrappers::afl_custom_fuzz_::<$mutator_type>( + data, + buf, + buf_size, + out_buf, + add_buf, + add_buf_size, + max_size, + ) + } + } + + #[no_mangle] + pub extern "C" fn afl_custom_queue_new_entry( + data: *mut ::std::os::raw::c_void, + filename_new_queue: *const ::std::os::raw::c_char, + filename_orig_queue: *const ::std::os::raw::c_char, + ) { + $crate::wrappers::afl_custom_queue_new_entry_::<$mutator_type>( + data, + filename_new_queue, + filename_orig_queue, + ) + } + + #[no_mangle] + pub extern "C" fn afl_custom_queue_get( + data: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + ) -> u8 { + $crate::wrappers::afl_custom_queue_get_::<$mutator_type>(data, filename) + } + + #[no_mangle] + pub extern "C" fn afl_custom_introspection( + data: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char { + $crate::wrappers::afl_custom_introspection_::<$mutator_type>(data) + } + + #[no_mangle] + pub extern "C" fn afl_custom_describe( + data: *mut ::std::os::raw::c_void, + max_description_len: usize, + ) -> *const ::std::os::raw::c_char { + $crate::wrappers::afl_custom_describe_::<$mutator_type>(data, max_description_len) + } + + #[no_mangle] + pub extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) { + unsafe { $crate::wrappers::afl_custom_deinit_::<$mutator_type>(data) } + } + }; +} + +#[cfg(test)] +/// this sanity test is supposed to just find out whether an empty mutator being exported by the macro compiles +mod sanity_test { + use std::os::raw::c_uint; + + #[cfg(feature = "afl_internals")] + use super::afl_state; + + use super::{export_mutator, RawCustomMutator}; + + struct ExampleMutator; + + impl RawCustomMutator for ExampleMutator { + #[cfg(feature = "afl_internals")] + fn init(_afl: &afl_state, _seed: c_uint) -> Self { + unimplemented!() + } + + #[cfg(not(feature = "afl_internals"))] + fn init(_seed: c_uint) -> Self { + unimplemented!() + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + _buffer: &'b mut [u8], + _add_buff: Option<&[u8]>, + _max_size: usize, + ) -> Option<&'b [u8]> { + unimplemented!() + } + } + + export_mutator!(ExampleMutator); +} + +#[allow(unused_variables)] +/// A custom mutator. +/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`]. +pub trait CustomMutator { + /// The error type. All methods must return the same error type. + type Error: Debug; + + /// The method which handles errors. + /// By default, this method will log the error to stderr if the environment variable "`AFL_CUSTOM_MUTATOR_DEBUG`" is set and non-empty. + /// After logging the error, execution will continue on a best-effort basis. + /// + /// This default behaviour can be customized by implementing this method. + fn handle_error(err: Self::Error) { + if std::env::var("AFL_CUSTOM_MUTATOR_DEBUG") + .map(|v| !v.is_empty()) + .unwrap_or(false) + { + eprintln!("Error in custom mutator: {:?}", err) + } + } + + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: c_uint) -> Result + where + Self: Sized; + + #[cfg(not(feature = "afl_internals"))] + fn init(seed: c_uint) -> Result + where + Self: Sized; + + fn fuzz_count(&mut self, buffer: &[u8]) -> Result { + Ok(1) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, Self::Error>; + + fn queue_new_entry( + &mut self, + filename_new_queue: &CStr, + filename_orig_queue: Option<&CStr>, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn queue_get(&mut self, filename: &CStr) -> Result { + Ok(true) + } + + fn describe(&mut self, max_description: usize) -> Result, Self::Error> { + Ok(None) + } + + fn introspection(&mut self) -> Result, Self::Error> { + Ok(None) + } +} + +impl RawCustomMutator for M +where + M: CustomMutator, + M::Error: Debug, +{ + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: c_uint) -> Self + where + Self: Sized, + { + match Self::init(afl, seed) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + panic!("Error in afl_custom_init") + } + } + } + + #[cfg(not(feature = "afl_internals"))] + fn init(seed: c_uint) -> Self + where + Self: Sized, + { + match Self::init(seed) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + panic!("Error in afl_custom_init") + } + } + } + + fn fuzz_count(&mut self, buffer: &[u8]) -> u32 { + match self.fuzz_count(buffer) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + 0 + } + } + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Option<&'b [u8]> { + match self.fuzz(buffer, add_buff, max_size) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } + + fn queue_new_entry(&mut self, filename_new_queue: &CStr, filename_orig_queue: Option<&CStr>) { + match self.queue_new_entry(filename_new_queue, filename_orig_queue) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + } + } + } + + fn queue_get(&mut self, filename: &CStr) -> bool { + match self.queue_get(filename) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + false + } + } + } + + fn describe(&mut self, max_description: usize) -> Option<&CStr> { + match self.describe(max_description) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } + + fn introspection(&mut self) -> Option<&CStr> { + match self.introspection() { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } +} diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml new file mode 100644 index 00000000..0c89b200 --- /dev/null +++ b/custom_mutators/rust/example/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "example" +version = "0.1.0" +authors = ["Julius Hohnerlein "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +custom_mutator = { path = "../custom_mutator" } + +[lib] +crate-type = ["cdylib"] \ No newline at end of file diff --git a/custom_mutators/rust/example/src/lib.rs b/custom_mutators/rust/example/src/lib.rs new file mode 100644 index 00000000..4f9345c0 --- /dev/null +++ b/custom_mutators/rust/example/src/lib.rs @@ -0,0 +1,50 @@ +#![allow(unused_variables)] + +use custom_mutator::{export_mutator, CustomMutator}; +use std::os::raw::c_uint; + +struct ExampleMutator; + +impl CustomMutator for ExampleMutator { + type Error = (); + + fn init(seed: c_uint) -> Result { + Ok(Self) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, ()> { + buffer.reverse(); + Ok(Some(buffer)) + } +} + +struct OwnBufferExampleMutator { + own_buffer: Vec, +} + +impl CustomMutator for OwnBufferExampleMutator { + type Error = (); + + fn init(seed: c_uint) -> Result { + Ok(Self { + own_buffer: Vec::new(), + }) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, ()> { + self.own_buffer.reverse(); + Ok(Some(self.own_buffer.as_slice())) + } +} + +export_mutator!(ExampleMutator); diff --git a/custom_mutators/rust/example_lain/Cargo.toml b/custom_mutators/rust/example_lain/Cargo.toml new file mode 100644 index 00000000..1f68c7e0 --- /dev/null +++ b/custom_mutators/rust/example_lain/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "example_lain" +version = "0.1.0" +authors = ["Julius Hohnerlein "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +custom_mutator = { path = "../custom_mutator" } +lain="0.5" + +[lib] +crate-type = ["cdylib"] \ No newline at end of file diff --git a/custom_mutators/rust/example_lain/rust-toolchain b/custom_mutators/rust/example_lain/rust-toolchain new file mode 100644 index 00000000..07ade694 --- /dev/null +++ b/custom_mutators/rust/example_lain/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/custom_mutators/rust/example_lain/src/lib.rs b/custom_mutators/rust/example_lain/src/lib.rs new file mode 100644 index 00000000..3336e861 --- /dev/null +++ b/custom_mutators/rust/example_lain/src/lib.rs @@ -0,0 +1,60 @@ +use custom_mutator::{export_mutator, CustomMutator}; +use lain::{ + mutator::Mutator, + prelude::*, + rand::{rngs::StdRng, SeedableRng}, +}; +use std::os::raw::c_uint; + +#[derive(Debug, Mutatable, NewFuzzed, BinarySerialize)] +struct MyStruct { + field_1: u8, + + #[lain(bits = 3)] + field_2: u8, + + #[lain(bits = 5)] + field_3: u8, + + #[lain(min = 5, max = 10000)] + field_4: u32, + + #[lain(ignore)] + ignored_field: u64, +} + +struct LainMutator { + mutator: Mutator, + buffer: Vec, +} + +impl CustomMutator for LainMutator { + type Error = (); + + fn init(seed: c_uint) -> Result { + Ok(Self { + mutator: Mutator::new(StdRng::seed_from_u64(seed as u64)), + buffer: Vec::new(), + }) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + _buffer: &'b mut [u8], + _add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, ()> { + // we just sample an instance of MyStruct, ignoring the current input + let instance = MyStruct::new_fuzzed(&mut self.mutator, None); + let size = instance.serialized_size(); + if size > max_size { + return Err(()); + } + self.buffer.clear(); + self.buffer.reserve(size); + instance.binary_serialize::<_, BigEndian>(&mut self.buffer); + Ok(Some(self.buffer.as_slice())) + } +} + +export_mutator!(LainMutator); diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index a2c544e3..61d711e4 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -4,6 +4,11 @@ This file describes how you can implement custom mutations to be used in AFL. For now, we support C/C++ library and Python module, collectivelly named as the custom mutator. +There is also experimental support for Rust in `custom_mutators/rust`. +Please refer to that directory for documentation. +Run ```cargo doc -p custom_mutator --open``` in that directory to view the +documentation in your web browser. + Implemented by - C/C++ library (`*.so`): Khaled Yakdan from Code Intelligence () - Python module: Christian Holler from Mozilla () -- cgit 1.4.1 From af628b16d1b601f75e46f4b0475a055088019263 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 27 Feb 2021 16:29:29 +0100 Subject: added rust binding reference --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c4347baf..e434e7d6 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -118,6 +118,7 @@ sending a mail to . mutation reproduction on crashing inputs - new env. var. AFL_NO_COLOR (or AFL_NO_COLOUR) to suppress colored console output (when configured with USE_COLOR and not ALWAYS_COLORED) + - added Rust bindings for custom mutators (thanks @julihoh) - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! -- cgit 1.4.1 From 8e051fd075e2854930bf828c5d23fe6dc648e1ed Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 27 Feb 2021 16:37:00 +0100 Subject: fixed rust bindings placement --- docs/Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index e434e7d6..192d6ee2 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -42,6 +42,7 @@ sending a mail to . - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - printing suggestions for mistyped `AFL_` env variables + - added Rust bindings for custom mutators (thanks @julihoh) - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a @@ -118,7 +119,6 @@ sending a mail to . mutation reproduction on crashing inputs - new env. var. AFL_NO_COLOR (or AFL_NO_COLOUR) to suppress colored console output (when configured with USE_COLOR and not ALWAYS_COLORED) - - added Rust bindings for custom mutators (thanks @julihoh) - instrumentation - We received an enhanced gcc_plugin module from AdaCore, thank you very much!! -- cgit 1.4.1 From 5cf0655071a83b1c87490be8399fe2365f087223 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Sun, 28 Feb 2021 10:01:35 +0100 Subject: metrics --- docs/docs.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/docs.md b/docs/docs.md index 0f98bd54..ed6ec85e 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -63,6 +63,31 @@ The project does not require writing new documentation or tutorials beside the cheat sheet. The technical information for the cheat sheet will be provided by us. +## Metrics + +afl++ is a the highest performant fuzzer publicly available - but is also the +most feature rich and complex. With the publicity of afl++' success and +deployment in Google projects internally and externally and availability as +a package on most Linux distributions we see more and more issues being +created and help requests on our Discord channel that would not be +necessary if people would have read through all our documentation - which +is unrealistic. + +We expect the the new documenation after this project to be cleaner, easier +accessible and lighter to digest by our users, resulting in much less +help requests. On the other hand the amount of users using afl++ should +increase as well as it will be more accessible which would also increase +questions again - but overall resulting in a reduction of help requests. + +In numbers: we currently have per week on average 5 issues on Github, +10 questions on discord and 1 on mailing lists that would not be necessary +with perfect documentation and perfect people. + +We would consider this project a success if afterwards we only have +2 issues on Github and 3 questions on discord anymore that would be answered +by reading the documentation. The mailing list is usually used by the most +novice users and we don't expect any less questions there. + ## Project Budget We have zero experience with technical writers, so this is very hard for us @@ -70,13 +95,19 @@ to calculate. We expect it to be a lot of work though because of the amount of documentation we have that needs to be restructured and partially rewritten (44 documents with 13k total lines of content). +We assume the daily rate of a very good and experienced technical writer in +times of a pandemic to be ~500$ (according to web research), and calculate +the overall amout of work to be around 20 days for everything incl. the +graphics (but again - this is basically just guessing). + Technical Writer 10000$ Volunteer stipends 0$ (waved) T-Shirts for the top 10 contributors and helpers to this documentation project: 10 afl++ logo t-shirts 20$ each 200$ 10 shipping cost of t-shirts 10$ each 100$ - + Total: 10.300$ +(in the submission form 10.280$ was entered) ## Additional Information -- cgit 1.4.1 From 0c38850f955a608529bdd02cc39dc68713ef8528 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 1 Mar 2021 09:43:35 +0100 Subject: 3.10c release --- README.md | 10 +++++----- docs/Changelog.md | 14 +++++++------- include/config.h | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 119426f6..800c2121 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ AFL++ Logo - Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.10c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.01a + Github Version: 3.11a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -25,14 +25,14 @@ For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` with `AFL_LLVM_CMPLOG=1`. -## Major changes in afl++ 3.0 + 3.1 +## Major changes in afl++ 3.00 + 3.10 -With afl++ 3.1 we introduced the following changes from previous behaviours: +With afl++ 3.10 we introduced the following changes from previous behaviours: * The '+' feature of the '-t' option now means to auto-calculate the timeout with the value given being the maximum timeout. The original meaning of "skipping timeouts instead of abort" is now inherent to the -t option. -With afl++ 3.0 we introduced changes that break some previous afl and afl++ +With afl++ 3.00 we introduced changes that break some previous afl and afl++ behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is diff --git a/docs/Changelog.md b/docs/Changelog.md index 192d6ee2..f3e15b6a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.01a (dev) +### Version ++3.10c (release) - Mac OS ARM64 support - Android support fixed and updated by Joey Jiaojg - thanks! - New selective instrumentation option with __AFL_COVERAGE_* commands @@ -49,10 +49,10 @@ sending a mail to . comment (thanks to Zherya for reporting) - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - cmplog/redqueen can now process basic libc++ and libstdc++ - std::string comparisons (though no position or length type variants) - - added support for __afl_coverage_interesting() for LTO and - and our own PCGUARD (llvm 10.0.1+), read more about this function - and selective coverage in instrumentation/README.instrument_list.md + std::string comparisons (no position or length type variants) + - added support for __afl_coverage_interesting() for LTO and our + own PCGUARD (llvm 10.0.1+), read more about this function and + selective coverage in instrumentation/README.instrument_list.md - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang @@ -68,12 +68,12 @@ sending a mail to . - unicornafl - Substantial speed gains in python bindings for certain use cases - Improved rust bindings - - Added a new example harness to compare python, c, and rust bindings + - Added a new example harness to compare python, c and rust bindings - afl-cmin and afl-showmap now support the -f option - afl_plot now also generates a graph on the discovered edges - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars. - - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. + - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b) diff --git a/include/config.h b/include/config.h index b0b1f7b9..c583f23b 100644 --- a/include/config.h +++ b/include/config.h @@ -25,8 +25,8 @@ /* Version string: */ -// c = release, d = volatile github dev, e = experimental branch -#define VERSION "++3.01a" +// c = release, a = volatile github dev, e = experimental branch +#define VERSION "++3.10c" /****************************************************** * * -- cgit 1.4.1 From 82c05630ba201bfc631e4e2335c6d8c2e76d91c0 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 1 Mar 2021 10:12:42 +0100 Subject: 3.11a init --- docs/Changelog.md | 3 +++ include/config.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f3e15b6a..4e3eef16 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,9 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++3.11a (dev) + - ... + ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/include/config.h b/include/config.h index c583f23b..a3aa9a4a 100644 --- a/include/config.h +++ b/include/config.h @@ -26,7 +26,7 @@ /* Version string: */ // c = release, a = volatile github dev, e = experimental branch -#define VERSION "++3.10c" +#define VERSION "++3.11a" /****************************************************** * * -- cgit 1.4.1 From c3a8052a16f7d2d1947ea538719905a9d7a24d22 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 1 Mar 2021 19:30:39 +0100 Subject: update changelog --- docs/Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 4e3eef16..f5742d24 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,8 +9,8 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.11a (dev) - - ... - + - afl-cc + - fixed for a crash that can occur with ASAN + CMPLOG together ### Version ++3.10c (release) - Mac OS ARM64 support -- cgit 1.4.1 From 333509bb0a56be9bd2e236f0e2f37d4af2dd7d59 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 2 Mar 2021 16:24:43 +0100 Subject: better unicode support --- docs/Changelog.md | 3 ++- instrumentation/SanitizerCoverageLTO.so.cc | 14 +++++++++----- instrumentation/afl-llvm-dict2file.so.cc | 14 +++++++++----- instrumentation/afl-llvm-lto-instrumentation.so.cc | 14 +++++++++----- instrumentation/compare-transform-pass.so.cc | 11 +++++++++-- 5 files changed, 38 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index f5742d24..01240b2a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,7 +10,8 @@ sending a mail to . ### Version ++3.11a (dev) - afl-cc - - fixed for a crash that can occur with ASAN + CMPLOG together + - fixed for a crash that can occur with ASAN + CMPLOG together plus + better support for unicode (thanks to @stbergmann for reporting!) ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 942d5357..849b6eef 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -872,17 +872,21 @@ bool ModuleSanitizerCoverage::instrumentModule( // was not already added if (!isMemcmp) { - if (addedNull == false) { + if (addedNull == false && thestring[optLen - 1] != '\0') { thestring.append("\0", 1); // add null byte optLen++; } - // ensure we do not have garbage - size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; - thestring = thestring.substr(0, optLen); + if (!isStdString) { + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } } diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index 1c365da8..885aa035 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -543,17 +543,21 @@ bool AFLdict2filePass::runOnModule(Module &M) { // was not already added if (!isMemcmp) { - if (addedNull == false) { + if (addedNull == false && thestring[optLen - 1] != '\0') { thestring.append("\0", 1); // add null byte optLen++; } - // ensure we do not have garbage - size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; - thestring = thestring.substr(0, optLen); + if (!isStdString) { + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } } diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index 1f21b917..ef270a1f 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -658,17 +658,21 @@ bool AFLLTOPass::runOnModule(Module &M) { // was not already added if (!isMemcmp) { - if (addedNull == false) { + if (addedNull == false && thestring[optLen - 1] != '\0') { thestring.append("\0", 1); // add null byte optLen++; } - // ensure we do not have garbage - size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; - thestring = thestring.substr(0, optLen); + if (!isStdString) { + + // ensure we do not have garbage + size_t offset = thestring.find('\0', 0); + if (offset + 1 < optLen) optLen = offset + 1; + thestring = thestring.substr(0, optLen); + + } } diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index 84301493..a85522a2 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -421,14 +421,21 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, } // add null termination character implicit in c strings - TmpConstStr.append("\0", 1); + if (TmpConstStr[TmpConstStr.length() - 1] != 0) { + + TmpConstStr.append("\0", 1); + + } // in the unusual case the const str has embedded null // characters, the string comparison functions should terminate // at the first null - if (!isMemcmp) + if (!isMemcmp) { + TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1); + } + constStrLen = TmpConstStr.length(); // prefer use of StringRef (in comparison to std::string a StringRef has // built-in runtime bounds checking, which makes debugging easier) -- cgit 1.4.1 From 108e588e888df5c2679600ea49846a565bac23f9 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 2 Mar 2021 17:46:43 +0100 Subject: add de-unicoded dictionary entries --- docs/Changelog.md | 2 + include/afl-fuzz.h | 1 + src/afl-fuzz-extras.c | 149 ++++++++++++++++++++++++++++++++++++++++++-------- src/afl-fuzz.c | 7 ++- 4 files changed, 134 insertions(+), 25 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 01240b2a..376f5f06 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,6 +9,8 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.11a (dev) + - afl-fuzz + - add non-unicode variants from unicode-looking dictionary entries - afl-cc - fixed for a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 3531d672..5003b563 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -1062,6 +1062,7 @@ u8 has_new_bits_unclassified(afl_state_t *, u8 *); void load_extras_file(afl_state_t *, u8 *, u32 *, u32 *, u32); void load_extras(afl_state_t *, u8 *); void dedup_extras(afl_state_t *); +void deunicode_extras(afl_state_t *); void add_extra(afl_state_t *afl, u8 *mem, u32 len); void maybe_add_auto(afl_state_t *, u8 *, u32); void save_auto(afl_state_t *); diff --git a/src/afl-fuzz-extras.c b/src/afl-fuzz-extras.c index 7ecad233..52100fa1 100644 --- a/src/afl-fuzz-extras.c +++ b/src/afl-fuzz-extras.c @@ -387,6 +387,130 @@ static inline u8 memcmp_nocase(u8 *m1, u8 *m2, u32 len) { } +/* add an extra/dict/token - no checks performed, no sorting */ + +static void add_extra_nocheck(afl_state_t *afl, u8 *mem, u32 len) { + + afl->extras = afl_realloc((void **)&afl->extras, + (afl->extras_cnt + 1) * sizeof(struct extra_data)); + + if (unlikely(!afl->extras)) { PFATAL("alloc"); } + + afl->extras[afl->extras_cnt].data = ck_alloc(len); + afl->extras[afl->extras_cnt].len = len; + memcpy(afl->extras[afl->extras_cnt].data, mem, len); + afl->extras_cnt++; + + /* We only want to print this once */ + + if (afl->extras_cnt == afl->max_det_extras + 1) { + + WARNF("More than %u tokens - will use them probabilistically.", + afl->max_det_extras); + + } + +} + +/* Sometimes strings in input is transformed to unicode internally, so for + fuzzing we should attempt to de-unicode if it looks like simple unicode */ + +void deunicode_extras(afl_state_t *afl) { + + if (!afl->extras_cnt) return; + + u32 i, j, orig_cnt = afl->extras_cnt; + u8 buf[64]; + + for (i = 0; i < orig_cnt; ++i) { + + if (afl->extras[i].len < 6 || afl->extras[i].len > 64 || + afl->extras[i].len % 2) { + + continue; + + } + + u32 k = 0, z1 = 0, z2 = 0, z3 = 0, z4 = 0, half = afl->extras[i].len >> 1; + u32 quarter = half >> 1; + + for (j = 0; j < afl->extras[i].len; ++j) { + + switch (j % 4) { + + case 2: + if (!afl->extras[i].data[j]) { ++z3; } + // fall through + case 0: + if (!afl->extras[i].data[j]) { ++z1; } + break; + case 3: + if (!afl->extras[i].data[j]) { ++z4; } + // fall through + case 1: + if (!afl->extras[i].data[j]) { ++z2; } + break; + + } + + } + + if ((z1 < half && z2 < half) || z1 + z2 == afl->extras[i].len) { continue; } + + // also maybe 32 bit unicode? + if (afl->extras[i].len % 4 == 0 && afl->extras[i].len >= 12 && + (z3 == quarter || z4 == quarter) && z1 + z2 == quarter * 3) { + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z4 < quarter) { + + if (j % 4 == 3) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z3 < quarter) { + + if (j % 4 == 2) { buf[k++] = afl->extras[i].data[j]; } + + } else if (z2 < half) { + + if (j % 4 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 4 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + k = 0; + + } + + for (j = 0; j < afl->extras[i].len; ++j) { + + if (z1 < half) { + + if (j % 2 == 0) { buf[k++] = afl->extras[i].data[j]; } + + } else { + + if (j % 2 == 1) { buf[k++] = afl->extras[i].data[j]; } + + } + + } + + add_extra_nocheck(afl, buf, k); + + } + + qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), + compare_extras_len); + +} + /* Removes duplicates from the loaded extras. This can happen if multiple files are loaded */ @@ -396,9 +520,9 @@ void dedup_extras(afl_state_t *afl) { u32 i, j, orig_cnt = afl->extras_cnt; - for (i = 0; i < afl->extras_cnt - 1; i++) { + for (i = 0; i < afl->extras_cnt - 1; ++i) { - for (j = i + 1; j < afl->extras_cnt; j++) { + for (j = i + 1; j < afl->extras_cnt; ++j) { restart_dedup: @@ -462,30 +586,11 @@ void add_extra(afl_state_t *afl, u8 *mem, u32 len) { } - afl->extras = afl_realloc((void **)&afl->extras, - (afl->extras_cnt + 1) * sizeof(struct extra_data)); - - if (unlikely(!afl->extras)) { PFATAL("alloc"); } - - afl->extras[afl->extras_cnt].data = ck_alloc(len); - afl->extras[afl->extras_cnt].len = len; - - memcpy(afl->extras[afl->extras_cnt].data, mem, len); - - afl->extras_cnt++; + add_extra_nocheck(afl, mem, len); qsort(afl->extras, afl->extras_cnt, sizeof(struct extra_data), compare_extras_len); - /* We only want to print this once */ - - if (afl->extras_cnt == afl->max_det_extras + 1) { - - WARNF("More than %u tokens - will use them probabilistically.", - afl->max_det_extras); - - } - } /* Maybe add automatic extra. */ diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index a02eadb2..90f77919 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1449,9 +1449,6 @@ int main(int argc, char **argv_orig, char **envp) { } - dedup_extras(afl); - OKF("Loaded a total of %u extras.", afl->extras_cnt); - } if (!afl->timeout_given) { find_timeout(afl); } // only for resumes! @@ -1681,6 +1678,10 @@ int main(int argc, char **argv_orig, char **envp) { } + deunicode_extras(afl); + dedup_extras(afl); + if (afl->extras_cnt) { OKF("Loaded a total of %u extras.", afl->extras_cnt); } + // after we have the correct bitmap size we can read the bitmap -B option // and set the virgin maps if (afl->in_bitmap) { -- cgit 1.4.1 From f0bc2e0e8b9d7b7e6e5371153f1b3bd7500cdae7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 2 Mar 2021 18:45:41 +0100 Subject: docu --- TODO.md | 8 +++++++- docs/Changelog.md | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/TODO.md b/TODO.md index 4615c456..e5a678cf 100644 --- a/TODO.md +++ b/TODO.md @@ -2,12 +2,15 @@ ## Roadmap 3.00+ - - AFL_MAP_SIZE for qemu_mode and unicorn_mode - CPU affinity for many cores? There seems to be an issue > 96 cores - afl-plot to support multiple plot_data - afl_custom_fuzz_splice_optin() - afl_custom_splice() - intel-pt tracer + - better autodetection of shifting runtime timeout values + - cmplog: use colorization input for havoc? + - cmplog: too much tainted bytes, directly add to dict and skip? + ## Further down the road @@ -23,9 +26,12 @@ qemu_mode: - add/implement AFL_QEMU_INST_LIBLIST and AFL_QEMU_NOINST_PROGRAM - add/implement AFL_QEMU_INST_REGIONS as a list of _START/_END addresses + ## Ideas - LTO/sancov: write current edge to prev_loc and use that information when using cmplog or __sanitizer_cov_trace_cmp*. maybe we can deduct by follow up edge numbers that both following cmp paths have been found and then disable working on this edge id -> cmplog_intelligence branch + - use cmplog colorization taint result for havoc locations? + diff --git a/docs/Changelog.md b/docs/Changelog.md index 376f5f06..1be41267 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,7 +12,7 @@ sending a mail to . - afl-fuzz - add non-unicode variants from unicode-looking dictionary entries - afl-cc - - fixed for a crash that can occur with ASAN + CMPLOG together plus + - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) ### Version ++3.10c (release) -- cgit 1.4.1 From 96c526cb78512737a980726dd32c95593edb8cd1 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 14:04:40 +0100 Subject: fix caller/ctx change, support dlopen in afl-compiler-rt --- docs/Changelog.md | 10 ++++ instrumentation/LLVMInsTrim.so.cc | 29 ++++----- instrumentation/afl-compiler-rt.o.c | 114 +++++++++++++++++++++++++++++++----- src/afl-cc.c | 31 ++++++---- 4 files changed, 144 insertions(+), 40 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1be41267..6fe3517a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,16 @@ sending a mail to . - afl-cc - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) + - handle erroneous setups in which multiple afl-compiler-rt are + compiled into the target. This now also supports dlopen instrumented + libs loaded before the forkserver. + - Renamed CTX to CALLER, added correct/real CTX implemenation to CLASSIC + - qemu_mode + - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! + - if no new/updated checkout is wanted, build with: + NO_CHECKOUT=1 ./build_qemu_support.sh + - we no longer perform a "git drop" + ### Version ++3.10c (release) - Mac OS ARM64 support diff --git a/instrumentation/LLVMInsTrim.so.cc b/instrumentation/LLVMInsTrim.so.cc index 948f8f3a..f0de6536 100644 --- a/instrumentation/LLVMInsTrim.so.cc +++ b/instrumentation/LLVMInsTrim.so.cc @@ -135,7 +135,7 @@ struct InsTrim : public ModulePass { unsigned int PrevLocSize = 0; char * ngram_size_str = getenv("AFL_LLVM_NGRAM_SIZE"); if (!ngram_size_str) ngram_size_str = getenv("AFL_NGRAM_SIZE"); - char *ctx_str = getenv("AFL_LLVM_CTX"); + char *caller_str = getenv("AFL_LLVM_CALLER"); #ifdef AFL_HAVE_VECTOR_INTRINSICS unsigned int ngram_size = 0; @@ -197,9 +197,9 @@ struct InsTrim : public ModulePass { GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); GlobalVariable *AFLPrevLoc; GlobalVariable *AFLContext = NULL; - LoadInst * PrevCtx = NULL; // for CTX sensitive coverage + LoadInst * PrevCaller = NULL; // for CALLER sensitive coverage - if (ctx_str) + if (caller_str) #if defined(__ANDROID__) || defined(__HAIKU__) AFLContext = new GlobalVariable( M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); @@ -398,11 +398,11 @@ struct InsTrim : public ModulePass { unsigned int cur_loc; // Context sensitive coverage - if (ctx_str && &BB == &F.getEntryBlock()) { + if (caller_str && &BB == &F.getEntryBlock()) { - PrevCtx = IRB.CreateLoad(AFLContext); - PrevCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); + PrevCaller = IRB.CreateLoad(AFLContext); + PrevCaller->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); // does the function have calls? and is any of the calls larger than // one basic block? @@ -441,7 +441,7 @@ struct InsTrim : public ModulePass { } - } // END of ctx_str + } // END of caller_str if (MarkSetOpt && MS.find(&BB) == MS.end()) { continue; } @@ -485,9 +485,9 @@ struct InsTrim : public ModulePass { #endif PrevLocTrans = IRB.CreateZExt(PrevLoc, IRB.getInt32Ty()); - if (ctx_str) + if (caller_str) PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); + IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCaller), Int32Ty); /* Load SHM pointer */ LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); @@ -535,16 +535,17 @@ struct InsTrim : public ModulePass { IRB.CreateStore(Incr, MapPtrIdx) ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - if (ctx_str && has_calls) { + if (caller_str && has_calls) { - // in CTX mode we have to restore the original context for the + // in CALLER mode we have to restore the original context for the // caller - she might be calling other functions which need the - // correct CTX + // correct CALLER Instruction *Inst = BB.getTerminator(); if (isa(Inst) || isa(Inst)) { IRBuilder<> Post_IRB(Inst); - StoreInst * RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + StoreInst * RestoreCtx = + Post_IRB.CreateStore(PrevCaller, AFLContext); RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index c9577a55..e3aa787f 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -123,6 +123,17 @@ static u8 is_persistent; static u8 _is_sancov; +/* Debug? */ + +static u32 __afl_debug; + +/* Already initialized markers */ + +static u32 __afl_already_initialized_shm; +static u32 __afl_already_initialized_forkserver; +static u32 __afl_already_initialized_first; +static u32 __afl_already_initialized_second; + /* Dummy pipe for area_is_valid() */ static int __afl_dummy_fd[2] = {2, 2}; @@ -176,7 +187,7 @@ static void __afl_map_shm_fuzz() { char *id_str = getenv(SHM_FUZZ_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: fuzzcase shmem %s\n", id_str ? id_str : "none"); @@ -222,7 +233,7 @@ static void __afl_map_shm_fuzz() { __afl_fuzz_len = (u32 *)map; __afl_fuzz_ptr = map + sizeof(u32); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: successfully got fuzzing shared memory\n"); @@ -242,7 +253,6 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { - static u32 __afl_already_initialized_shm = 0; if (__afl_already_initialized_shm) return; __afl_already_initialized_shm = 1; @@ -303,7 +313,7 @@ static void __afl_map_shm(void) { early-stage __afl_area_initial region that is needed to allow some really hacky .init code to work correctly in projects such as OpenSSL. */ - if (getenv("AFL_DEBUG")) + if (__afl_debug) fprintf(stderr, "DEBUG: id_str %s, __afl_area_ptr %p, __afl_area_initial %p, " "__afl_map_addr 0x%llx, MAP_SIZE %u, __afl_final_loc %u, " @@ -359,17 +369,18 @@ static void __afl_map_shm(void) { } + close(shm_fd); + if (shm_base == MAP_FAILED) { - close(shm_fd); shm_fd = -1; - fprintf(stderr, "mmap() failed\n"); + perror("mmap for map"); + if (__afl_map_addr) send_forkserver_error(FS_ERROR_MAP_ADDR); else send_forkserver_error(FS_ERROR_MMAP); - perror("mmap for map"); exit(2); @@ -476,7 +487,7 @@ static void __afl_map_shm(void) { id_str = getenv(CMPLOG_SHM_ENV_VAR); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "DEBUG: cmplog id_str %s\n", id_str == NULL ? "" : id_str); @@ -541,6 +552,58 @@ static void __afl_map_shm(void) { } +/* unmap SHM. */ + +static void __afl_unmap_shm(void) { + + if (!__afl_already_initialized_shm) return; + + char *id_str = getenv(SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_area_ptr, __afl_map_size); + +#else + + shmdt((void *)__afl_area_ptr); + +#endif + + } else if ((!__afl_area_ptr || __afl_area_ptr == __afl_area_initial) && + + __afl_map_addr) { + + munmap((void *)__afl_map_addr, __afl_map_size); + + } + + __afl_area_ptr = __afl_area_ptr_dummy; + + id_str = getenv(CMPLOG_SHM_ENV_VAR); + + if (id_str) { + +#ifdef USEMMAP + + munmap((void *)__afl_cmp_map, __afl_map_size); + +#else + + shmdt((void *)__afl_cmp_map); + +#endif + + __afl_cmp_map = NULL; + + } + + __afl_already_initialized_shm = 0; + +} + #ifdef __linux__ static void __afl_start_snapshots(void) { @@ -569,7 +632,7 @@ static void __afl_start_snapshots(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) { _exit(1); } - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -746,7 +809,6 @@ static void __afl_start_snapshots(void) { static void __afl_start_forkserver(void) { - static u32 __afl_already_initialized_forkserver = 0; if (__afl_already_initialized_forkserver) return; __afl_already_initialized_forkserver = 1; @@ -800,7 +862,7 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "target forkserver recv: %08x\n", was_killed); @@ -1035,7 +1097,7 @@ void __afl_manual_init(void) { __afl_sharedmem_fuzzing = 0; if (__afl_area_ptr == NULL) __afl_area_ptr = __afl_area_ptr_dummy; - if (getenv("AFL_DEBUG")) + if (__afl_debug) fprintf(stderr, "DEBUG: disabled instrumentation because of " "AFL_DISABLE_LLVM_INSTRUMENTATION\n"); @@ -1079,10 +1141,11 @@ __attribute__((constructor(CTOR_PRIO))) void __afl_auto_early(void) { __attribute__((constructor(1))) void __afl_auto_second(void) { - static u32 __afl_already_initialized_second = 0; if (__afl_already_initialized_second) return; __afl_already_initialized_second = 1; + if (getenv("AFL_DEBUG")) { __afl_debug = 1; } + if (getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) return; u8 *ptr; @@ -1114,7 +1177,6 @@ __attribute__((constructor(1))) void __afl_auto_second(void) { __attribute__((constructor(0))) void __afl_auto_first(void) { - static u32 __afl_already_initialized_first = 0; if (__afl_already_initialized_first) return; __afl_already_initialized_first = 1; @@ -1198,7 +1260,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { _is_sancov = 1; - if (getenv("AFL_DEBUG")) { + if (__afl_debug) { fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", @@ -1235,6 +1297,28 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + if (__afl_debug) { + + fprintf(stderr, + "Done __sanitizer_cov_trace_pc_guard_init: __afl_final_loc = %u\n", + __afl_final_loc); + + } + + if (__afl_already_initialized_shm && __afl_final_loc > __afl_map_size) { + + if (__afl_debug) { + + fprintf(stderr, "Reinit shm necessary (+%u)\n", + __afl_final_loc - __afl_map_size); + + } + + __afl_unmap_shm(); + __afl_map_shm(); + + } + } ///// CmpLog instrumentation diff --git a/src/afl-cc.c b/src/afl-cc.c index 0c689286..ab794877 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -89,7 +89,7 @@ char instrument_mode_string[18][18] = { "GCC", "CLANG", "CTX", - "", + "CALLER", "", "", "", @@ -1514,12 +1514,13 @@ int main(int argc, char **argv, char **envp) { " CLASSIC %s no yes module yes yes " "yes\n" " - NORMAL\n" + " - CALLER\n" " - CTX\n" " - NGRAM-{2-16}\n" " INSTRIM no yes module yes yes " " yes\n" " - NORMAL\n" - " - CTX\n" + " - CALLER\n" " - NGRAM-{2-16}\n" " [GCC_PLUGIN] gcc plugin: %s%s\n" " CLASSIC DEFAULT no yes no no no " @@ -1566,7 +1567,10 @@ int main(int argc, char **argv, char **envp) { NATIVE_MSG " CLASSIC: decision target instrumentation (README.llvm.md)\n" - " CTX: CLASSIC + callee context (instrumentation/README.ctx.md)\n" + " CALLER: CLASSIC + single callee context " + "(instrumentation/README.ctx.md)\n" + " CTX: CLASSIC + full callee context " + "(instrumentation/README.ctx.md)\n" " NGRAM-x: CLASSIC + previous path " "((instrumentation/README.ngram.md)\n" " INSTRIM: Dominator tree (for LLVM <= 6.0) " @@ -1660,15 +1664,17 @@ int main(int argc, char **argv, char **envp) { " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CTX, NGRAM-2 ... " - "NGRAM-16\n" + " CLASSIC, INSTRIM, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, " + "NGRAM-2 ..-16\n" " You can also use the old environment variables instead:\n" " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" " AFL_LLVM_INSTRIM: use light weight instrumentation InsTrim\n" " AFL_LLVM_INSTRIM_LOOPHEAD: optimize loop tracing for speed " "(option to INSTRIM)\n" - " AFL_LLVM_CTX: use context sensitive coverage (for CLASSIC and " - "INSTRIM)\n" + " AFL_LLVM_CALLER: use single context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_CTX: use full context sensitive coverage (for " + "CLASSIC)\n" " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " "CLASSIC & INSTRIM)\n"); @@ -1814,11 +1820,14 @@ int main(int argc, char **argv, char **envp) { "(requires LLVM 11 or higher)"); #endif - if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC && - instrument_mode != INSTRUMENT_CFG) + if (instrument_opt_mode && instrument_mode == INSTRUMENT_CFG && + instrument_opt_mode & INSTRUMENT_OPT_CTX) + FATAL("CFG instrumentation mode supports NGRAM and CALLER, but not CTX."); + else if (instrument_opt_mode && instrument_mode != INSTRUMENT_CLASSIC) + // we will drop CFG/INSTRIM in the future so do not advertise FATAL( - "CTX and NGRAM instrumentation options can only be used with LLVM and " - "CFG or CLASSIC instrumentation modes!"); + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) FATAL( -- cgit 1.4.1 From 79d75d8e42e5adf64e149ab6e1fe197cb1d4f303 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Thu, 4 Mar 2021 14:19:00 +0100 Subject: even support dlopen instrumented libs after the forkserver --- docs/Changelog.md | 5 +++-- instrumentation/afl-compiler-rt.o.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 6fe3517a..b1c991ff 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,8 +15,9 @@ sending a mail to . - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) - handle erroneous setups in which multiple afl-compiler-rt are - compiled into the target. This now also supports dlopen instrumented - libs loaded before the forkserver. + compiled into the target. This now also supports dlopen + instrumented libs loaded before the forkserver and even after the + forkserver is started (then with collisions though) - Renamed CTX to CALLER, added correct/real CTX implemenation to CLASSIC - qemu_mode - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index e3aa787f..87bf6486 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1263,8 +1263,10 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { if (__afl_debug) { fprintf(stderr, - "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges)\n", - start, stop, (unsigned long)(stop - start)); + "Running __sanitizer_cov_trace_pc_guard_init: %p-%p (%lu edges) " + "after_fs=%u\n", + start, stop, (unsigned long)(stop - start), + __afl_already_initialized_forkserver); } @@ -1280,6 +1282,36 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { } + /* instrumented code is loaded *after* our forkserver is up. this is a + problem. We cannot prevent collisions then :( */ + if (__afl_already_initialized_forkserver && + __afl_final_loc + 1 + stop - start > __afl_map_size) { + + if (__afl_debug) + fprintf(stderr, "Warning: new instrumneted code after the forkserver!\n"); + __afl_final_loc = 2; + + if (1 + stop - start > __afl_map_size) { + + *(start++) = ++__afl_final_loc; + + while (start < stop) { + + if (R(100) < inst_ratio) + *start = ++__afl_final_loc % __afl_map_size; + else + *start = 0; + + start++; + + } + + return; + + } + + } + /* Make sure that the first element in the range is always set - we use that to avoid duplicate calls (which can happen as an artifact of the underlying implementation in LLVM). */ -- cgit 1.4.1 From 3342aa751d8e9102449e1739b38a25c40ab18e81 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Fri, 5 Mar 2021 10:05:43 +0100 Subject: fix laf string transform crash --- docs/Changelog.md | 1 + instrumentation/SanitizerCoverageLTO.so.cc | 3 +++ instrumentation/afl-llvm-dict2file.so.cc | 4 ++++ instrumentation/afl-llvm-lto-instrumentation.so.cc | 3 +++ instrumentation/compare-transform-pass.so.cc | 11 +++++++++-- 5 files changed, 20 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index b1c991ff..c5b275de 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,7 @@ sending a mail to . - afl-cc - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) + - fixed a crash in LAF transform for empty strings - handle erroneous setups in which multiple afl-compiler-rt are compiled into the target. This now also supports dlopen instrumented libs loaded before the forkserver and even after the diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 849b6eef..13a5e5fd 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -849,15 +849,18 @@ bool ModuleSanitizerCoverage::instrumentModule( thestring = Str2; optLen = thestring.length(); + if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; } if (isMemcmp || isStrncmp || isStrncasecmp) { Value * op2 = callInst->getArgOperand(2); ConstantInt *ilen = dyn_cast(op2); + if (ilen) { uint64_t literalLength = optLen; optLen = ilen->getZExtValue(); + if (optLen < 2) { continue; } if (literalLength + 1 == optLen) { // add null byte thestring.append("\0", 1); addedNull = true; diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index 885aa035..c954054b 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -521,14 +521,18 @@ bool AFLdict2filePass::runOnModule(Module &M) { optLen = thestring.length(); + if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; } + if (isMemcmp || isStrncmp || isStrncasecmp) { Value * op2 = callInst->getArgOperand(2); ConstantInt *ilen = dyn_cast(op2); + if (ilen) { uint64_t literalLength = optLen; optLen = ilen->getZExtValue(); + if (optLen < 2) { continue; } if (literalLength + 1 == optLen) { // add null byte thestring.append("\0", 1); addedNull = true; diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index ef270a1f..50306224 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -635,15 +635,18 @@ bool AFLLTOPass::runOnModule(Module &M) { thestring = Str2; optLen = thestring.length(); + if (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; } if (isMemcmp || isStrncmp || isStrncasecmp) { Value * op2 = callInst->getArgOperand(2); ConstantInt *ilen = dyn_cast(op2); + if (ilen) { uint64_t literalLength = optLen; optLen = ilen->getZExtValue(); + if (optLen < 2) { continue; } if (literalLength + 1 == optLen) { // add null byte thestring.append("\0", 1); addedNull = true; diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index a85522a2..3ecba4e6 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -316,7 +316,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, uint64_t len = ilen->getZExtValue(); // if len is zero this is a pointless call but allow real // implementation to worry about that - if (!len) continue; + if (len < 2) continue; if (isMemcmp) { @@ -420,8 +420,15 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, } + if (TmpConstStr.length() < 2 || + (TmpConstStr.length() == 2 && !TmpConstStr[1])) { + + continue; + + } + // add null termination character implicit in c strings - if (TmpConstStr[TmpConstStr.length() - 1] != 0) { + if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) { TmpConstStr.append("\0", 1); -- cgit 1.4.1 From c9854ec8cbd87dc09d7a6ab1b75f3fce2f7bc9ca Mon Sep 17 00:00:00 2001 From: hexcoder Date: Fri, 5 Mar 2021 18:07:16 +0100 Subject: typo --- docs/Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c5b275de..4e76fedd 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -16,10 +16,10 @@ sending a mail to . better support for unicode (thanks to @stbergmann for reporting!) - fixed a crash in LAF transform for empty strings - handle erroneous setups in which multiple afl-compiler-rt are - compiled into the target. This now also supports dlopen + compiled into the target. This now also supports dlopen() instrumented libs loaded before the forkserver and even after the forkserver is started (then with collisions though) - - Renamed CTX to CALLER, added correct/real CTX implemenation to CLASSIC + - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC - qemu_mode - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! - if no new/updated checkout is wanted, build with: -- cgit 1.4.1 From e8d580f54d0ba4cc096f084ed7c52e772b66039c Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 5 Mar 2021 19:45:48 +0100 Subject: rust mutator changes added to changelog --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 4e76fedd..3ca4a20b 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,6 +11,7 @@ sending a mail to . ### Version ++3.11a (dev) - afl-fuzz - add non-unicode variants from unicode-looking dictionary entries + - Rust custom mutator API improvements - afl-cc - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) -- cgit 1.4.1 From e226d1bbb390fb2f81e192d5a8077ec386504807 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 8 Mar 2021 15:16:43 +0100 Subject: update changelog --- docs/Changelog.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 3ca4a20b..ab0e2da2 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,10 +9,10 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.11a (dev) - - afl-fuzz + - afl-fuzz: - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - - afl-cc + - afl-cc: - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) - fixed a crash in LAF transform for empty strings @@ -21,11 +21,12 @@ sending a mail to . instrumented libs loaded before the forkserver and even after the forkserver is started (then with collisions though) - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC - - qemu_mode + - qemu_mode: - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! - if no new/updated checkout is wanted, build with: NO_CHECKOUT=1 ./build_qemu_support.sh - we no longer perform a "git drop" + - afl-cmin: support filenames with spaces ### Version ++3.10c (release) -- cgit 1.4.1 From 74a6044b3fba496c1255f9aedbf5b7253ae29f0e Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 14:11:52 +0100 Subject: fix sanitizer settings --- docs/Changelog.md | 1 + src/afl-forkserver.c | 17 +++++++++-------- src/afl-fuzz-init.c | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index ab0e2da2..b47b03ba 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,6 +10,7 @@ sending a mail to . ### Version ++3.11a (dev) - afl-fuzz: + - fix sanitizer settings (bug since 3.10c) - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - afl-cc: diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 6f08f9f4..82ec3069 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -481,11 +481,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* This should improve performance a bit, since it stops the linker from doing extra work post-fork(). */ - if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 0); } + if (!getenv("LD_BIND_LAZY")) { setenv("LD_BIND_NOW", "1", 1); } /* Set sane defaults for ASAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("ASAN_OPTIONS")) + if (!getenv("ASAN_OPTIONS")) setenv("ASAN_OPTIONS", "abort_on_error=1:" "detect_leaks=0:" @@ -498,11 +498,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Set sane defaults for UBSAN if nothing else specified. */ - if (fsrv->debug == true && !getenv("UBSAN_OPTIONS")) + if (!getenv("UBSAN_OPTIONS")) setenv("UBSAN_OPTIONS", "halt_on_error=1:" "abort_on_error=1:" @@ -514,7 +514,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); /* Envs for QASan */ setenv("QASAN_MAX_CALL_STACK", "0", 0); @@ -523,7 +523,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, /* MSAN is tricky, because it doesn't support abort_on_error=1 at this point. So, we do this in a very hacky way. */ - if (fsrv->debug == true && !getenv("MSAN_OPTIONS")) + if (!getenv("MSAN_OPTIONS")) setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" @@ -536,7 +536,7 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "handle_abort=0:" "handle_sigfpe=0:" "handle_sigill=0", - 0); + 1); fsrv->init_child_func(fsrv, argv); @@ -931,7 +931,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, "%s" - " - Most likely the target has a huge coverage map, retry with setting the\n" + " - Most likely the target has a huge coverage map, retry with " + "setting the\n" " environment variable AFL_MAP_SIZE=4194304\n\n" " - The current memory limit (%s) is too restrictive, causing an " diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 3dbc4c65..2d5f32a7 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2457,7 +2457,7 @@ void check_asan_opts(afl_state_t *afl) { } - if (!strstr(x, "symbolize=0")) { + if (!afl->debug && !strstr(x, "symbolize=0")) { FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!"); -- cgit 1.4.1 From 47f2650a32470172a32c4ebd446003cb8a4b80e8 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 16:53:56 +0100 Subject: add AFL_NOOPT --- README.md | 17 +++++++++++++---- docs/env_variables.md | 11 +++++++++++ include/envs.h | 4 +++- src/afl-cc.c | 21 +++++++++++++++++++-- src/afl-common.c | 1 + 5 files changed, 47 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 6b11fee4..d4e39566 100644 --- a/README.md +++ b/README.md @@ -399,10 +399,19 @@ How to do this is described below. Then build the target. (Usually with `make`) -**NOTE**: sometimes configure and build systems are fickle and do not like -stderr output (and think this means a test failure) - which is something -afl++ like to do to show statistics. It is recommended to disable them via -`export AFL_QUIET=1`. +**NOTES** + +1. sometimes configure and build systems are fickle and do not like + stderr output (and think this means a test failure) - which is something + afl++ like to do to show statistics. It is recommended to disable them via + `export AFL_QUIET=1`. + +2. sometimes configure and build systems error on warnings - these should be + disabled (e.g. `--disable-werror` for some configure scripts` + +3. in case the configure/build system complains about afl++'s compiler and + aborts then set `export AFL_NOOPT=1` which will then just behave like the + real compiler. This option has to be unset again before building the target! ##### configure diff --git a/docs/env_variables.md b/docs/env_variables.md index f6ed12d0..a20f1e42 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -26,6 +26,17 @@ Because (with the exception of the --afl-MODE command line option) the compile-time tools do not accept afl specific command-line options, they make fairly broad use of environmental variables instead: + - Some build/configure scripts break with afl++ compilers. To be able to + pass them, do: +``` + export CC=afl-cc + export CXX=afl-c++ + export AFL_NOOPT=1 + ./configure --disable-shared --disabler-werror + unset AFL_NOOPT + make +``` + - Most afl tools do not print any output if stdout/stderr are redirected. If you want to get the output into a file then set the `AFL_DEBUG` environment variable. diff --git a/include/envs.h b/include/envs.h index e8595ef7..37748a56 100644 --- a/include/envs.h +++ b/include/envs.h @@ -119,10 +119,12 @@ static char *afl_environment_variables[] = { "AFL_NO_PYTHON", "AFL_UNTRACER_FILE", "AFL_LLVM_USE_TRACE_PC", - "AFL_NO_X86", // not really an env but we dont want to warn on it "AFL_MAP_SIZE", "AFL_MAPSIZE", "AFL_MAX_DET_EXTRAS", + "AFL_NO_X86", // not really an env but we dont want to warn on it + "AFL_NOOPT", + "AFL_PASSTHROUGH", "AFL_PATH", "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", diff --git a/src/afl-cc.c b/src/afl-cc.c index bb136fb9..8f9bb397 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -1025,7 +1025,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { int main(int argc, char **argv, char **envp) { - int i; + int i, passthrough = 0; char *callname = argv[0], *ptr = NULL; if (getenv("AFL_DEBUG")) { @@ -1045,6 +1045,13 @@ int main(int argc, char **argv, char **envp) { } + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + passthrough = 1; + if (!debug) { be_quiet = 1; } + + } + if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; argvnull = (u8 *)argv[0]; check_environment_vars(envp); @@ -1665,6 +1672,7 @@ int main(int argc, char **argv, char **envp) { " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" " AFL_NO_BUILTIN: no builtins for string compare functions (for " "libtokencap.so)\n" + " AFL_NOOP: behave lik a normal compiler (to pass configure tests)\n" " AFL_PATH: path to instrumenting pass and runtime " "(afl-compiler-rt.*o)\n" " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" @@ -1977,7 +1985,16 @@ int main(int argc, char **argv, char **envp) { } - execvp(cc_params[0], (char **)cc_params); + if (passthrough) { + + argv[0] = cc_params[0]; + execvp(cc_params[0], (char **)argv); + + } else { + + execvp(cc_params[0], (char **)cc_params); + + } FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); diff --git a/src/afl-common.c b/src/afl-common.c index a306fe5e..68f82a5e 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -682,6 +682,7 @@ void check_environment_vars(char **envp) { env[strlen(afl_environment_variables[i])] == '=') { match = 1; + if ((val = getenv(afl_environment_variables[i])) && !*val) { WARNF( -- cgit 1.4.1 From 0868ea8348ebc682c370d763324b49536ee4c1f7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 9 Mar 2021 17:15:19 +0100 Subject: fix compiler rt on -c --- docs/Changelog.md | 8 +++++++- src/afl-cc.c | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index b47b03ba..c475911d 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,9 @@ sending a mail to . - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - afl-cc: + - added AFL_NOOPT that will just pass everything to the normal + gcc/clang compiler without any changes - to pass weird configure + scripts - fixed a crash that can occur with ASAN + CMPLOG together plus better support for unicode (thanks to @stbergmann for reporting!) - fixed a crash in LAF transform for empty strings @@ -21,7 +24,10 @@ sending a mail to . compiled into the target. This now also supports dlopen() instrumented libs loaded before the forkserver and even after the forkserver is started (then with collisions though) - - Renamed CTX to CALLER, added correct/real CTX implementation to CLASSIC + - the compiler rt was added also in object building (-c) which + should have been fixed years ago but somewhere got lost :( + - Renamed CTX to CALLER, added correct/real CTX implementation to + CLASSIC - qemu_mode: - added AFL_QEMU_EXCLUDE_RANGES env by @realmadsci, thanks! - if no new/updated checkout is wanted, build with: diff --git a/src/afl-cc.c b/src/afl-cc.c index 8f9bb397..a517124f 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -940,7 +940,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only) { + // prevent unnecessary build errors + cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + + if (preprocessor_only || have_c) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. @@ -1001,18 +1004,15 @@ static void edit_params(u32 argc, char **argv, char **envp) { } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking && !have_c) + if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif #if defined(USEMMAP) && !defined(__HAIKU__) - if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; + cc_params[cc_par_cnt++] = "-lrt"; #endif - // prevent unnecessary build errors - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; - } #endif -- cgit 1.4.1 From 6cc59a38be02d812e1f5bbb46b6a94c5a31b460a Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 10 Mar 2021 15:37:28 +0100 Subject: gsoc2021 clarification --- docs/ideas.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/ideas.md b/docs/ideas.md index 08cb16ef..0130cf61 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -35,7 +35,9 @@ and documents something about it. In traditional fuzzing this is the coverage in the target, however we want to add various more observers, e.g. stack depth, heap usage, etc. - this is a topic for an experienced Rust developer. -# Generic ideas and wishlist +# Generic ideas and wishlist - NOT PART OF GSoC 2021 ! + +The below list is not part of GSoC 2021. ## Analysis software -- cgit 1.4.1 From f1d8a01047246cd5981cac6ad51850e98c8382c1 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 12 Mar 2021 03:55:18 +0100 Subject: added ui change to changelog --- docs/Changelog.md | 1 + 1 file changed, 1 insertion(+) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index c475911d..8f3e588c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -13,6 +13,7 @@ sending a mail to . - fix sanitizer settings (bug since 3.10c) - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements + - Imported crash stats painted yellow on resume (only new ones are red) - afl-cc: - added AFL_NOOPT that will just pass everything to the normal gcc/clang compiler without any changes - to pass weird configure -- cgit 1.4.1 From d4fb7f8b4015297e1c74b28d671eba058cfb6366 Mon Sep 17 00:00:00 2001 From: realmadsci <71108352+realmadsci@users.noreply.github.com> Date: Fri, 12 Mar 2021 15:53:42 -0500 Subject: Add AFL_QEMU_CUSTOM_BIN environment flag In QEMU mode (-Q), setting AFL_QEMU_CUSTOM_BIN cause afl-fuzz to skip prepending afl-qemu-trace to your command line. Use this if you wish to use a custom afl-qemu-trace or if you need to modify the afl-qemu-trace arguments. --- docs/env_variables.md | 4 ++++ include/envs.h | 1 + src/afl-common.c | 7 +++++++ src/afl-fuzz-init.c | 1 + 4 files changed, 13 insertions(+) (limited to 'docs') diff --git a/docs/env_variables.md b/docs/env_variables.md index a20f1e42..c6ad0aa4 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -393,6 +393,10 @@ checks or alter some of the more exotic semantics of the tool: - In QEMU mode (-Q), `AFL_PATH` will be searched for afl-qemu-trace. + - In QEMU mode (-Q), setting `AFL_QEMU_CUSTOM_BIN` cause afl-fuzz to skip + prepending `afl-qemu-trace` to your command line. Use this if you wish to use a + custom afl-qemu-trace or if you need to modify the afl-qemu-trace arguments. + - Setting `AFL_CYCLE_SCHEDULES` will switch to a different schedule everytime a cycle is finished. diff --git a/include/envs.h b/include/envs.h index 4d4d6b0e..e92bee2a 100644 --- a/include/envs.h +++ b/include/envs.h @@ -130,6 +130,7 @@ static char *afl_environment_variables[] = { "AFL_PERFORMANCE_FILE", "AFL_PRELOAD", "AFL_PYTHON_MODULE", + "AFL_QEMU_CUSTOM_BIN", "AFL_QEMU_COMPCOV", "AFL_QEMU_COMPCOV_DEBUG", "AFL_QEMU_DEBUG_MAPS", diff --git a/src/afl-common.c b/src/afl-common.c index 9f6eb564..58fbf765 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -149,6 +149,13 @@ void argv_cpy_free(char **argv) { char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) { + if (unlikely(getenv("AFL_QEMU_CUSTOM_BIN"))) { + WARNF( + "AFL_QEMU_CUSTOM_BIN is enabled. " + "You must run your target under afl-qemu-trace on your own!"); + return argv; + } + if (!unlikely(own_loc)) { FATAL("BUG: param own_loc is NULL"); } u8 *tmp, *cp = NULL, *rsl, *own_copy; diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index ca2f75f1..82c1799e 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2592,6 +2592,7 @@ void check_binary(afl_state_t *afl, u8 *fname) { } if (afl->afl_env.afl_skip_bin_check || afl->use_wine || afl->unicorn_mode || + (afl->fsrv.qemu_mode && getenv("AFL_QEMU_CUSTOM_BIN")) || afl->non_instrumented_mode) { return; -- cgit 1.4.1 From ac795ae1e154df87d422eb9a307ee1f40fcb701f Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Mar 2021 23:08:28 +0100 Subject: v3.11c release --- README.md | 4 ++-- docs/Changelog.md | 4 +++- include/config.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/README.md b/README.md index 0deaca22..69e5bb74 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ AFL++ Logo - Release Version: [3.10c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.11c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.11a + Github Version: 3.12a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) diff --git a/docs/Changelog.md b/docs/Changelog.md index 8f3e588c..1c735a70 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,9 +8,11 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . -### Version ++3.11a (dev) +### Version ++3.11c (release) - afl-fuzz: + - better auto detection of map size - fix sanitizer settings (bug since 3.10c) + - fix an off-by-one overwrite in cmplog - add non-unicode variants from unicode-looking dictionary entries - Rust custom mutator API improvements - Imported crash stats painted yellow on resume (only new ones are red) diff --git a/include/config.h b/include/config.h index 6ada0fbe..cc8024ea 100644 --- a/include/config.h +++ b/include/config.h @@ -26,7 +26,7 @@ /* Version string: */ // c = release, a = volatile github dev, e = experimental branch -#define VERSION "++3.11a" +#define VERSION "++3.11c" /****************************************************** * * -- cgit 1.4.1 From 1dcc3549b6ecc3925825c00ac63274b83e76ec8a Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Mon, 15 Mar 2021 23:21:03 +0100 Subject: v3.12a init --- docs/Changelog.md | 5 ++++- include/config.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 1c735a70..8222f942 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,10 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . +### Version ++3.12a (dev) + - ... + + ### Version ++3.11c (release) - afl-fuzz: - better auto detection of map size @@ -38,7 +42,6 @@ sending a mail to . - we no longer perform a "git drop" - afl-cmin: support filenames with spaces - ### Version ++3.10c (release) - Mac OS ARM64 support - Android support fixed and updated by Joey Jiaojg - thanks! diff --git a/include/config.h b/include/config.h index cc8024ea..b049fee0 100644 --- a/include/config.h +++ b/include/config.h @@ -26,7 +26,7 @@ /* Version string: */ // c = release, a = volatile github dev, e = experimental branch -#define VERSION "++3.11c" +#define VERSION "++3.12a" /****************************************************** * * -- cgit 1.4.1 From f5420e737a1ed1dbeb81783836d0449c06aa0fcc Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Mar 2021 16:15:29 +0100 Subject: rtn fix --- docs/Changelog.md | 2 +- instrumentation/afl-compiler-rt.o.c | 23 ++++++++++++----------- qemu_mode/qemuafl | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 8222f942..9aea3638 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.12a (dev) - - ... + - fix cmplog rtn (rare crash and not being able to gather ptr data) ### Version ++3.11c (release) diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 50b4e2c5..892118fb 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -1734,25 +1734,26 @@ static int area_is_valid(void *ptr, size_t len) { long r = syscall(SYS_write, __afl_dummy_fd[1], ptr, len); - if (unlikely(r <= 0 || r > len)) { // fail - maybe hitting asan boundary? + if (r <= 0 || r > len) return 0; - char *p = (char *)ptr; - long page_size = sysconf(_SC_PAGE_SIZE); - char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size; - if (page >= p + len) { return 0; } // no isnt, return fail - len = page - p - len; - r = syscall(SYS_write, __afl_dummy_fd[1], page, len); + // even if the write succeed this can be a false positive if we cross + // a page boundary. who knows why. - } + char *p = (char *)ptr; + long page_size = sysconf(_SC_PAGE_SIZE); + char *page = (char *)((uintptr_t)p & ~(page_size - 1)) + page_size; - // partial writes - we return what was written. - if (likely(r >= 0 && r <= len)) { + if (page > p + len) { + // no, not crossing a page boundary return (int)r; } else { - return 0; + // yes it crosses a boundary, hence we can only return the length of + // rest of the first page, we cannot detect if the next page is valid + // or not, neither by SYS_write nor msync() :-( + return (int)(page - p); } diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl index 0fb212da..d1ca56b8 160000 --- a/qemu_mode/qemuafl +++ b/qemu_mode/qemuafl @@ -1 +1 @@ -Subproject commit 0fb212daab492411b3e323bc18a3074c1aecfd37 +Subproject commit d1ca56b84e78f821406eef28d836918edfc8d610 -- cgit 1.4.1 From 175a275a3db2c9a9f6c9307c56feb58f4cfe365f Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 16 Mar 2021 17:04:52 +0100 Subject: changelog --- docs/Changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 9aea3638..17d68b0a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -10,7 +10,9 @@ sending a mail to . ### Version ++3.12a (dev) - fix cmplog rtn (rare crash and not being able to gather ptr data) - + - qemu_mode (thanks @realmadsci): + - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace + - add AFL_QEMU_CUSTOM_BIN ### Version ++3.11c (release) - afl-fuzz: -- cgit 1.4.1 From 7c2436c7114e21f029b06346421a73910deac578 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 16 Mar 2021 19:34:34 +0100 Subject: no runtime for shared linking --- docs/Changelog.md | 4 +++- src/afl-cc.c | 59 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 29 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index 17d68b0a..bf04c58e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,9 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to . ### Version ++3.12a (dev) - - fix cmplog rtn (rare crash and not being able to gather ptr data) + - afl-cc: + - fix cmplog rtn (rare crash and not being able to gather ptr data) + - link runtime not to shared libs - qemu_mode (thanks @realmadsci): - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace - add AFL_QEMU_CUSTOM_BIN diff --git a/src/afl-cc.c b/src/afl-cc.c index 44654de0..18401d0d 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -959,56 +959,59 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode != GCC && compiler_mode != CLANG) { - switch (bit_mode) { + if (!shared_linking) { - case 0: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt.o", obj_path); - if (lto_mode) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); - break; + switch (bit_mode) { - case 32: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); - if (lto_mode) { + case 0: + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-compiler-rt.o", obj_path); + if (lto_mode) + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); + break; + case 32: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + alloc_printf("%s/afl-compiler-rt-32.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m32 is not supported by your compiler"); + if (lto_mode) { - } + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m32 is not supported by your compiler"); - break; + } - case 64: - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); - if (lto_mode) { + break; + case 64: cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + alloc_printf("%s/afl-compiler-rt-64.o", obj_path); if (access(cc_params[cc_par_cnt - 1], R_OK)) FATAL("-m64 is not supported by your compiler"); + if (lto_mode) { - } + cc_params[cc_par_cnt++] = + alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); + if (access(cc_params[cc_par_cnt - 1], R_OK)) + FATAL("-m64 is not supported by your compiler"); - break; + } - } + break; + + } #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking) cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif + } + #if defined(USEMMAP) && !defined(__HAIKU__) cc_params[cc_par_cnt++] = "-lrt"; #endif -- cgit 1.4.1 From 94312796f936ba1830b61432a0f958e192dd212f Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Wed, 17 Mar 2021 13:16:02 +0100 Subject: better map variability --- docs/Changelog.md | 1 + src/afl-fuzz.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/Changelog.md b/docs/Changelog.md index bf04c58e..8dc218af 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -12,6 +12,7 @@ sending a mail to . - afl-cc: - fix cmplog rtn (rare crash and not being able to gather ptr data) - link runtime not to shared libs + - ensure shared libraries are properly built and instrumented - qemu_mode (thanks @realmadsci): - move AFL_PRELOAD and AFL_USE_QASAN logic inside afl-qemu-trace - add AFL_QEMU_CUSTOM_BIN diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 8318a92e..ff4c5281 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1584,12 +1584,14 @@ int main(int argc, char **argv_orig, char **envp) { afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary; afl->cmplog_fsrv.init_child_func = cmplog_exec_child; - if (map_size <= DEFAULT_SHMEM_SIZE && !afl->non_instrumented_mode && - !afl->fsrv.qemu_mode && !afl->unicorn_mode) { + if ((map_size <= DEFAULT_SHMEM_SIZE || + afl->cmplog_fsrv.map_size < map_size) && + !afl->non_instrumented_mode && !afl->fsrv.qemu_mode && + !afl->unicorn_mode) { - afl->fsrv.map_size = DEFAULT_SHMEM_SIZE; // dummy temporary value + afl->cmplog_fsrv.map_size = MAX(map_size, (u32)DEFAULT_SHMEM_SIZE); char vbuf[16]; - snprintf(vbuf, sizeof(vbuf), "%u", DEFAULT_SHMEM_SIZE); + snprintf(vbuf, sizeof(vbuf), "%u", afl->cmplog_fsrv.map_size); setenv("AFL_MAP_SIZE", vbuf, 1); } -- cgit 1.4.1