diff options
47 files changed, 1524 insertions, 1467 deletions
diff --git a/.gitmodules b/.gitmodules index cd9d73e9..6569c0b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,4 +18,4 @@ url = https://github.com/NixOS/patchelf.git [submodule "coresight_mode/coresight-trace"] path = coresight_mode/coresight-trace - url = git@github.com:RICSecLab/coresight-trace.git + url = https://github.com/RICSecLab/coresight-trace.git diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index 64e5beb2..1e2c411d 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -308,7 +308,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 ./SanitizerCoveragePCGUARD.so ./split-compares-pass.so ./split-switches-pass.so ./cmplog-routines-pass.so ./cmplog-instructions-pass.so ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.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 ./cmplog-switches-pass.so ./afl-llvm-dict2file.so ./compare-transform-pass.so ./afl-ld-lto ./afl-llvm-lto-instrumentlist.so ./SanitizerCoverageLTO.so # If prerequisites are not given, warn, do not build anything, and exit with code 0 ifeq "$(LLVMVER)" "" @@ -410,11 +410,6 @@ endif ./SanitizerCoverageLTO.so: instrumentation/SanitizerCoverageLTO.so.cc ifeq "$(LLVM_LTO)" "1" $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o -endif - -./afl-llvm-lto-instrumentation.so: instrumentation/afl-llvm-lto-instrumentation.so.cc instrumentation/afl-llvm-common.o -ifeq "$(LLVM_LTO)" "1" - $(CXX) $(CLANG_CPPFL) -Wno-writable-strings -fno-rtti -fPIC -std=$(LLVM_STDCXX) -shared $< -o $@ $(CLANG_LFL) instrumentation/afl-llvm-common.o $(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto.o @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m64 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-64.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi @$(CLANG_BIN) $(CFLAGS_SAFE) $(CPPFLAGS) -Wno-unused-result -O0 $(AFL_CLANG_FLTO) -m32 -fPIC -c instrumentation/afl-llvm-rt-lto.o.c -o ./afl-llvm-rt-lto-32.o 2>/dev/null; if [ "$$?" = "0" ]; then : ; fi @@ -480,7 +475,7 @@ install: all @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 @rm -f $${DESTDIR}$(HELPER_PATH)/afl-llvm-rt*.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt*.o @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-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-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); 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 diff --git a/coresight_mode/README.md b/coresight_mode/README.md index dac44076..cd1bccab 100644 --- a/coresight_mode/README.md +++ b/coresight_mode/README.md @@ -1,12 +1,16 @@ # AFL++ CoreSight mode -CoreSight mode enables binary-only fuzzing on ARM64 Linux using CoreSight. +CoreSight mode enables binary-only fuzzing on ARM64 Linux using CoreSight (ARM's hardware tracing technology). NOTE: CoreSight mode is in the early development stage. Not applicable for production use. +Currently the following hardware boards are supported: +* NVIDIA Jetson TX2 (NVIDIA Parker) +* NVIDIA Jetson Nano (NVIDIA Tegra X1) +* GIGABYTE R181-T90 (Marvell ThunderX2 CN99XX) ## Getting started -Please read the [RICSec/coresight-trace README](https://github.com/RICSecLab/coresight-trace/blob/master/README.md) and check the prerequisites before getting started. +Please read the [RICSec/coresight-trace README](https://github.com/RICSecLab/coresight-trace/blob/master/README.md) and check the prerequisites (capstone) before getting started. CoreSight mode supports the AFL fork server mode to reduce `exec` system call overhead. To support it for binary-only fuzzing, it needs to modify the target ELF binary to re-link to the patched glibc. We employ this design from [PTrix](https://github.com/junxzm1990/afl-pt). diff --git a/docs/technical_details.md b/docs/technical_details.md index b9d271d9..994ffe9f 100644 --- a/docs/technical_details.md +++ b/docs/technical_details.md @@ -1,7 +1,7 @@ # Technical "whitepaper" for afl-fuzz -NOTE: this document is rather outdated! +NOTE: this document is mostly outdated! This document provides a quick overview of the guts of American Fuzzy Lop. diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index c0abe14c..52439979 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -103,7 +103,7 @@ ifndef OS $(error "Operating system unsupported") endif -GUM_DEVKIT_VERSION=15.1.10 +GUM_DEVKIT_VERSION=15.1.11 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)" diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md index 691b03d1..2ee0c858 100644 --- a/frida_mode/Scripting.md +++ b/frida_mode/Scripting.md @@ -246,7 +246,7 @@ FRIDA mode supports the replacement of any function, with an implementation generated by CModule. This allows for a bespoke harness to be written as follows: -``` +```js const slow = DebugSymbol.fromName('slow').address; Afl.print(`slow: ${slow}`); @@ -281,13 +281,90 @@ Afl.done(); Here, we replace the function `slow` with our own code. This code is then selected as the entry point as well as the persistent loop address. -**WARNING** There are two key limitations in replacing a function in this way: -- The function which is to be replaced must not be `main` this is because this -is the point at which FRIDA mode is initialized and at the point the the JS has -been run, the start of the `main` function has already been instrumented and -cached. -- The replacement function must not call itself. e.g. in this example we -couldn't replace `LLVMFuzzerTestOneInput` and call itself. +## Replacing LLVMFuzzerTestOneInput +The function `LLVMFuzzerTestOneInput` can be replaced just like any other. Also +any replaced function can also call itself. In the example below, we replace +`LLVMFuzzerTestOneInput` with `My_LLVMFuzzerTestOneInput` which ignores the +parameters `buf` and `len` and then calls the original `LLVMFuzzerTestOneInput` +with the paramaters `__afl_fuzz_ptr` and `__afl_fuzz_len`. This allows us to +carry out in-memory fuzzing without the need for any hook function. It should be +noted that the replacement function and the original can *NOT* share the same +name, since otherwise the `C` code in the `CModule` will not compile due to a +symbol name collision. + +```js +const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address; +Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`); + +const cm = new CModule(` + + extern unsigned char * __afl_fuzz_ptr; + extern unsigned int * __afl_fuzz_len; + extern void LLVMFuzzerTestOneInput(char *buf, int len); + + void My_LLVMFuzzerTestOneInput(char *buf, int len) { + + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + `, + { + LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput, + __afl_fuzz_ptr: Afl.getAflFuzzPtr(), + __afl_fuzz_len: Afl.getAflFuzzLen() + }); + +Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput); +Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput); +Afl.setInMemoryFuzzing(); +Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput); +``` + +## Hooking `main` +Lastly, it should be noted that using FRIDA mode's scripting support to hook +the `main` function is a special case. This is because the `main` function is +already hooked by the FRIDA mode engine itself and hence the function `main` (or +at least the first basic block already been compiled by Stalker ready for +execution). Hence any attempt to use `Interceptor.replace` like in the example +above will not work. Instead the JS bindings provide a function `setJsMainHook` +for just this scenario as demonstrated in the example below. + +```js +const main = DebugSymbol.fromName('main').address; +Afl.print(`main: ${main}`); + +const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address; +Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`); + +const cm = new CModule(` + + extern unsigned char * __afl_fuzz_ptr; + extern unsigned int * __afl_fuzz_len; + extern void LLVMFuzzerTestOneInput(char *buf, int len); + + int main(int argc, char **argv) { + + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + `, + { + LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput, + __afl_fuzz_ptr: Afl.getAflFuzzPtr(), + __afl_fuzz_len: Afl.getAflFuzzLen() + }); + +Afl.setEntryPoint(cm.main); +Afl.setPersistentAddress(cm.main); +Afl.setInMemoryFuzzing(); +Afl.setJsMainHook(cm.main); +``` +## Library Fuzzing + +It doesn't take too much imagination to see that the above example can be +extended to use FRIDA's `Module.load` API so that the replaced `main` function +can then call an arbitrary function. In this way, if we have a library which we +wish to fuzz rather than an execuatble, then a surrogate executable can be used. # Patching Consider the [following](test/js/test2.c) test code... @@ -620,41 +697,31 @@ value of the `-t` flag passed to `afl-fuzz`. # API ```js class Afl { - - /** - * Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode - * implementation). - */ - public static module: Module = Process.getModuleByName("afl-frida-trace.so"); - /** * This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`, * it takes as arguments a `NativePointer` and a `number`. It can be * called multiple times to exclude several ranges. */ - public static addExcludedRange(addressess: NativePointer, size: number): void { - Afl.jsApiAddExcludeRange(addressess, size); + static addExcludedRange(addressess, size) { + Afl.jsApiAddExcludeRange(addressess, size); } - /** * This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`, * it takes as arguments a `NativePointer` and a `number`. It can be * called multiple times to include several ranges. */ - public static addIncludedRange(addressess: NativePointer, size: number): void { - Afl.jsApiAddIncludeRange(addressess, size); + static addIncludedRange(addressess, size) { + Afl.jsApiAddIncludeRange(addressess, size); } - /** * This must always be called at the end of your script. This lets * FRIDA mode know that your configuration is finished and that * execution has reached the end of your script. Failure to call * this will result in a fatal error. */ - public static done(): void { - Afl.jsApiDone(); + static done() { + Afl.jsApiDone(); } - /** * This function can be called within your script to cause FRIDA * mode to trigger a fatal error. This is useful if for example you @@ -662,49 +729,48 @@ class Afl { * stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view * this error message. */ - public static error(msg: string): void { - const buf = Memory.allocUtf8String(msg); - Afl.jsApiError(buf); + static error(msg) { + const buf = Memory.allocUtf8String(msg); + Afl.jsApiError(buf); } - /** * Function used to provide access to `__afl_fuzz_ptr`, which contains the length of * fuzzing data when using in-memory test case fuzzing. */ - public static getAflFuzzLen(): NativePointer { - - return Afl.jsApiGetSymbol("__afl_fuzz_len"); + static getAflFuzzLen() { + return Afl.jsApiGetSymbol("__afl_fuzz_len"); } - /** * Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing * data when using in-memory test case fuzzing. */ - public static getAflFuzzPtr(): NativePointer { - - return Afl.jsApiGetSymbol("__afl_fuzz_ptr"); + static getAflFuzzPtr() { + return Afl.jsApiGetSymbol("__afl_fuzz_ptr"); } - /** * Print a message to the STDOUT. This should be preferred to * FRIDA's `console.log` since FRIDA will queue it's log messages. * If `console.log` is used in a callback in particular, then there * may no longer be a thread running to service this queue. */ - public static print(msg: string): void { - const STDOUT_FILENO = 2; - const log = `${msg}\n`; - const buf = Memory.allocUtf8String(log); - Afl.jsApiWrite(STDOUT_FILENO, buf, log.length); + static print(msg) { + const STDOUT_FILENO = 2; + const log = `${msg}\n`; + const buf = Memory.allocUtf8String(log); + Afl.jsApiWrite(STDOUT_FILENO, buf, log.length); + } + /** + * See `AFL_FRIDA_INST_NO_BACKPATCH`. + */ + static setBackpatchDisable() { + Afl.jsApiSetBackpatchDisable(); } - /** * See `AFL_FRIDA_DEBUG_MAPS`. */ - public static setDebugMaps(): void { - Afl.jsApiSetDebugMaps(); + static setDebugMaps() { + Afl.jsApiSetDebugMaps(); } - /** * This has the same effect as setting `AFL_ENTRYPOINT`, but has the * convenience of allowing you to use FRIDAs APIs to determine the @@ -713,143 +779,198 @@ class Afl { * function should be called with a `NativePointer` as its * argument. */ - public static setEntryPoint(address: NativePointer): void { - Afl.jsApiSetEntryPoint(address); + static setEntryPoint(address) { + Afl.jsApiSetEntryPoint(address); } - /** * Function used to enable in-memory test cases for fuzzing. */ - public static setInMemoryFuzzing(): void { - Afl.jsApiAflSharedMemFuzzing.writeInt(1); + static setInMemoryFuzzing() { + Afl.jsApiAflSharedMemFuzzing.writeInt(1); + } + /** + * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string` + * as an argument. + */ + static setInstrumentCoverageFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentCoverageFile(buf); } - /** * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as * an argument. */ - public static setInstrumentDebugFile(file: string): void { - const buf = Memory.allocUtf8String(file); - Afl.jsApiSetInstrumentDebugFile(buf); + static setInstrumentDebugFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentDebugFile(buf); } - /** * See `AFL_FRIDA_INST_TRACE`. */ - public static setInstrumentEnableTracing(): void { - Afl.jsApiSetInstrumentTrace(); + static setInstrumentEnableTracing() { + Afl.jsApiSetInstrumentTrace(); + } + /** + * See `AFL_FRIDA_INST_JIT`. + */ + static setInstrumentJit() { + Afl.jsApiSetInstrumentJit(); } - /** * See `AFL_INST_LIBS`. */ - public static setInstrumentLibraries(): void { - Afl.jsApiSetInstrumentLibraries(); + static setInstrumentLibraries() { + Afl.jsApiSetInstrumentLibraries(); } - /** * See `AFL_FRIDA_INST_NO_OPTIMIZE` */ - public static setInstrumentNoOptimize(): void { - Afl.jsApiSetInstrumentNoOptimize(); + static setInstrumentNoOptimize() { + Afl.jsApiSetInstrumentNoOptimize(); + } + /* + * See `AFL_FRIDA_INST_SEED` + */ + static setInstrumentSeed(seed) { + Afl.jsApiSetInstrumentSeed(seed); } - /** * See `AFL_FRIDA_INST_TRACE_UNIQUE`. */ - public static setInstrumentTracingUnique(): void { - Afl.jsApiSetInstrumentTraceUnique(); + static setInstrumentTracingUnique() { + Afl.jsApiSetInstrumentTraceUnique(); + } + /** + * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single + * `string` as an argument. + */ + static setInstrumentUnstableCoverageFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentUnstableCoverageFile(buf); + } + /* + * Set a callback to be called in place of the usual `main` function. This see + * `Scripting.md` for details. + */ + static setJsMainHook(address) { + Afl.jsApiSetJsMainHook(address); } - /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. */ - public static setPersistentAddress(address: NativePointer): void { - Afl.jsApiSetPersistentAddress(address); + static setPersistentAddress(address) { + Afl.jsApiSetPersistentAddress(address); } - /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a * `number` should be provided as it's argument. */ - public static setPersistentCount(count: number): void { - Afl.jsApiSetPersistentCount(count); + static setPersistentCount(count) { + Afl.jsApiSetPersistentCount(count); } - /** * See `AFL_FRIDA_PERSISTENT_DEBUG`. */ - public static setPersistentDebug(): void { - Afl.jsApiSetPersistentDebug(); + static setPersistentDebug() { + Afl.jsApiSetPersistentDebug(); } - /** * See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an * argument. See above for examples of use. */ - public static setPersistentHook(address: NativePointer): void { - Afl.jsApiSetPersistentHook(address); + static setPersistentHook(address) { + Afl.jsApiSetPersistentHook(address); } - /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a * `NativePointer` should be provided as it's argument. */ - public static setPersistentReturn(address: NativePointer): void { - Afl.jsApiSetPersistentReturn(address); + static setPersistentReturn(address) { + Afl.jsApiSetPersistentReturn(address); + } + /** + * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`. + */ + static setPrefetchBackpatchDisable() { + Afl.jsApiSetPrefetchBackpatchDisable(); } - /** * See `AFL_FRIDA_INST_NO_PREFETCH`. */ - public static setPrefetchDisable(): void { - Afl.jsApiSetPrefetchDisable(); + static setPrefetchDisable() { + Afl.jsApiSetPrefetchDisable(); + } + /** + * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as + * an argument. + */ + static setSeccompFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetSeccompFile(buf); + } + /** + * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`. + */ + static setStalkerAdjacentBlocks(val) { + Afl.jsApiSetStalkerAdjacentBlocks(val); } - /* - * Set a function to be called for each instruction which is instrumented - * by AFL FRIDA mode. + * Set a function to be called for each instruction which is instrumented + * by AFL FRIDA mode. + */ + static setStalkerCallback(callback) { + Afl.jsApiSetStalkerCallback(callback); + } + /** + * See `AFL_FRIDA_STALKER_IC_ENTRIES`. */ - public static setStalkerCallback(callback: NativePointer): void { - Afl.jsApiSetStalkerCallback(callback); + static setStalkerIcEntries(val) { + Afl.jsApiSetStalkerIcEntries(val); } - /** * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as * an argument. */ - public static setStatsFile(file: string): void { - const buf = Memory.allocUtf8String(file); - Afl.jsApiSetStatsFile(buf); + static setStatsFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetStatsFile(buf); } - /** * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an * argument */ - public static setStatsInterval(interval: number): void { - Afl.jsApiSetStatsInterval(interval); + static setStatsInterval(interval) { + Afl.jsApiSetStatsInterval(interval); } - /** * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as * an argument. */ - public static setStdErr(file: string): void { - const buf = Memory.allocUtf8String(file); - Afl.jsApiSetStdErr(buf); + static setStdErr(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetStdErr(buf); } - /** * See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as * an argument. */ - public static setStdOut(file: string): void { - const buf = Memory.allocUtf8String(file); - Afl.jsApiSetStdOut(buf); + static setStdOut(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetStdOut(buf); + } + /** + * See `AFL_FRIDA_TRACEABLE`. + */ + static setTraceable() { + Afl.jsApiSetTraceable(); + } + static jsApiGetFunction(name, retType, argTypes) { + const addr = Afl.module.getExportByName(name); + return new NativeFunction(addr, retType, argTypes); + } + static jsApiGetSymbol(name) { + return Afl.module.getExportByName(name); } - } - ``` diff --git a/frida_mode/frida.map b/frida_mode/frida.map index 61eb19ee..5276db91 100644 --- a/frida_mode/frida.map +++ b/frida_mode/frida.map @@ -20,6 +20,7 @@ js_api_set_instrument_trace; js_api_set_instrument_trace_unique; js_api_set_instrument_unstable_coverage_file; + js_api_set_js_main_hook; js_api_set_persistent_address; js_api_set_persistent_count; js_api_set_persistent_debug; diff --git a/frida_mode/include/asan.h b/frida_mode/include/asan.h index 67d33591..6745eb02 100644 --- a/frida_mode/include/asan.h +++ b/frida_mode/include/asan.h @@ -9,6 +9,7 @@ void asan_config(void); void asan_init(void); void asan_arch_init(void); void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator); +void asan_exclude_module_by_symbol(gchar *symbol_name); #endif diff --git a/frida_mode/include/js.h b/frida_mode/include/js.h index a5ecb712..39aa0573 100644 --- a/frida_mode/include/js.h +++ b/frida_mode/include/js.h @@ -7,11 +7,14 @@ typedef gboolean (*js_api_stalker_callback_t)(const cs_insn *insn, gboolean begin, gboolean excluded, GumStalkerOutput *output); +typedef int (*js_main_hook_t)(int argc, char **argv, char **envp); + extern unsigned char api_js[]; extern unsigned int api_js_len; extern gboolean js_done; extern js_api_stalker_callback_t js_user_callback; +extern js_main_hook_t js_main_hook; /* Frida Mode */ diff --git a/frida_mode/src/asan/asan.c b/frida_mode/src/asan/asan.c index d649bd76..884bec53 100644 --- a/frida_mode/src/asan/asan.c +++ b/frida_mode/src/asan/asan.c @@ -1,6 +1,7 @@ #include "frida-gumjs.h" #include "asan.h" +#include "ranges.h" #include "util.h" static gboolean asan_enabled = FALSE; @@ -32,3 +33,23 @@ void asan_init(void) { } +static gboolean asan_exclude_module(const GumModuleDetails *details, + gpointer user_data) { + + gchar * symbol_name = (gchar *)user_data; + GumAddress address; + + address = gum_module_find_export_by_name(details->name, symbol_name); + if (address == 0) { return TRUE; } + + ranges_add_exclude((GumMemoryRange *)details->range); + return FALSE; + +} + +void asan_exclude_module_by_symbol(gchar *symbol_name) { + + gum_process_enumerate_modules(asan_exclude_module, symbol_name); + +} + diff --git a/frida_mode/src/asan/asan_arm64.c b/frida_mode/src/asan/asan_arm64.c index 88c76535..b2adfa52 100644 --- a/frida_mode/src/asan/asan_arm64.c +++ b/frida_mode/src/asan/asan_arm64.c @@ -88,6 +88,8 @@ void asan_arch_init(void) { } + asan_exclude_module_by_symbol("__asan_loadN"); + } #endif diff --git a/frida_mode/src/asan/asan_x64.c b/frida_mode/src/asan/asan_x64.c index c7b70967..a287ea34 100644 --- a/frida_mode/src/asan/asan_x64.c +++ b/frida_mode/src/asan/asan_x64.c @@ -85,6 +85,8 @@ void asan_arch_init(void) { } + asan_exclude_module_by_symbol("__asan_loadN"); + } #endif diff --git a/frida_mode/src/asan/asan_x86.c b/frida_mode/src/asan/asan_x86.c index afc89936..331d026b 100644 --- a/frida_mode/src/asan/asan_x86.c +++ b/frida_mode/src/asan/asan_x86.c @@ -85,6 +85,8 @@ void asan_arch_init(void) { } + asan_exclude_module_by_symbol("__asan_loadN"); + } #endif diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c index 81e1a4b0..443baa1d 100644 --- a/frida_mode/src/cmplog/cmplog.c +++ b/frida_mode/src/cmplog/cmplog.c @@ -33,8 +33,22 @@ static gboolean cmplog_range(const GumRangeDetails *details, static gint cmplog_sort(gconstpointer a, gconstpointer b) { - return ((GumMemoryRange *)b)->base_address - - ((GumMemoryRange *)a)->base_address; + GumMemoryRange *ra = (GumMemoryRange *)a; + GumMemoryRange *rb = (GumMemoryRange *)b; + + if (ra->base_address < rb->base_address) { + + return -1; + + } else if (ra->base_address > rb->base_address) { + + return 1; + + } else { + + return 0; + + } } diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c index 049b5548..28fc706b 100644 --- a/frida_mode/src/ctx/ctx_arm32.c +++ b/frida_mode/src/ctx/ctx_arm32.c @@ -1,6 +1,7 @@ #include "frida-gumjs.h" #include "ctx.h" +#include "util.h" #if defined(__arm__) diff --git a/frida_mode/src/ctx/ctx_arm64.c b/frida_mode/src/ctx/ctx_arm64.c index 01f321e3..63b6cf09 100644 --- a/frida_mode/src/ctx/ctx_arm64.c +++ b/frida_mode/src/ctx/ctx_arm64.c @@ -1,6 +1,7 @@ #include "frida-gumjs.h" #include "ctx.h" +#include "util.h" #if defined(__aarch64__) diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c index abfeafc8..438e1fde 100644 --- a/frida_mode/src/ctx/ctx_x86.c +++ b/frida_mode/src/ctx/ctx_x86.c @@ -1,6 +1,7 @@ #include "frida-gumjs.h" #include "ctx.h" +#include "util.h" #if defined(__i386__) diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c index c271adc1..41162f2a 100644 --- a/frida_mode/src/instrument/instrument_x64.c +++ b/frida_mode/src/instrument/instrument_x64.c @@ -5,7 +5,7 @@ #if defined(__linux__) #if !defined(__ANDROID__) - #include <asm/prctl.h> + #include <sys/prctl.h> #include <sys/syscall.h> #else #include <linux/ashmem.h> @@ -68,10 +68,10 @@ typedef struct { // sahf // mov rax,QWORD PTR [rsp-0x80] - uint8_t mov_rax_rsp_80[5]; - uint8_t lahf; uint8_t mov_rax_rsp_88[8]; - uint8_t mov_rbx_rsp_90[8]; + uint8_t lahf; + uint8_t mov_rax_rsp_90[8]; + uint8_t mov_rbx_rsp_98[8]; uint8_t mov_eax_prev_loc[6]; uint8_t mov_prev_loc_curr_loc_shr1[10]; @@ -83,10 +83,10 @@ typedef struct { uint8_t adc_bl_0[3]; uint8_t mov_ptr_rax_rbx[2]; - uint8_t mov_rsp_90_rbx[8]; - uint8_t mov_rsp_88_rax[8]; + uint8_t mov_rsp_98_rbx[8]; + uint8_t mov_rsp_90_rax[8]; uint8_t sahf; - uint8_t mov_rsp_80_rax[5]; + uint8_t mov_rsp_88_rax[8]; } afl_log_code_asm_t; @@ -102,10 +102,10 @@ typedef union { static const afl_log_code_asm_t template = { - .mov_rax_rsp_80 = {0x48, 0x89, 0x44, 0x24, 0x80}, - .lahf = 0x9f, .mov_rax_rsp_88 = {0x48, 0x89, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF}, - .mov_rbx_rsp_90 = {0x48, 0x89, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF}, + .lahf = 0x9f, + .mov_rax_rsp_90 = {0x48, 0x89, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF}, + .mov_rbx_rsp_98 = {0x48, 0x89, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF}, .mov_eax_prev_loc = {0x8b, 0x05}, .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05}, @@ -116,10 +116,10 @@ static const afl_log_code_asm_t template = .adc_bl_0 = {0x80, 0xd3, 0x00}, .mov_ptr_rax_rbx = {0x88, 0x18}, - .mov_rsp_90_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF}, - .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF}, + .mov_rsp_98_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF}, + .mov_rsp_90_rax = {0x48, 0x8B, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF}, .sahf = 0x9e, - .mov_rsp_80_rax = {0x48, 0x8B, 0x44, 0x24, 0x80}, + .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF}, } diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c index 79664afa..ad837e2d 100644 --- a/frida_mode/src/instrument/instrument_x86.c +++ b/frida_mode/src/instrument/instrument_x86.c @@ -1,67 +1,144 @@ #include "frida-gumjs.h" #include "instrument.h" +#include "stalker.h" #include "util.h" #if defined(__i386__) -static GumAddress current_log_impl = GUM_ADDRESS(0); +static GHashTable *coverage_blocks = NULL; -static void instrument_coverage_function(GumX86Writer *cw) { + #pragma pack(push, 1) +typedef struct { - gum_x86_writer_put_pushfx(cw); - gum_x86_writer_put_push_reg(cw, GUM_REG_ECX); - gum_x86_writer_put_push_reg(cw, GUM_REG_EDX); + // cur_location = (block_address >> 4) ^ (block_address << 8); + // shared_mem[cur_location ^ prev_location]++; + // prev_location = cur_location >> 1; - gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX, - GUM_ADDRESS(&instrument_previous_pc)); - gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_EDX, GUM_REG_ECX); - gum_x86_writer_put_xor_reg_reg(cw, GUM_REG_EDX, GUM_REG_EDI); + uint8_t mov_eax_esp_4[4]; + uint8_t lahf; + uint8_t mov_eax_esp_8[4]; + uint8_t mov_ebx_esp_c[4]; - gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EDX, GUM_ADDRESS(__afl_area_ptr)); + uint8_t mov_eax_prev_loc[5]; + uint8_t mov_prev_loc_curr_loc_shr1[10]; - /* add byte ptr [edx], 1 */ - uint8_t add_byte_ptr_edx_1[] = {0x80, 0x02, 0x01}; - gum_x86_writer_put_bytes(cw, add_byte_ptr_edx_1, sizeof(add_byte_ptr_edx_1)); + uint8_t xor_eax_curr_loc[5]; + uint8_t add_eax_area_ptr[5]; - /* adc byte ptr [edx], 0 */ - uint8_t adc_byte_ptr_edx_0[] = {0x80, 0x12, 0x00}; - gum_x86_writer_put_bytes(cw, adc_byte_ptr_edx_0, sizeof(adc_byte_ptr_edx_0)); + uint8_t mov_ebx_ptr_eax[2]; + uint8_t add_bl_1[3]; + uint8_t adc_bl_0[3]; + uint8_t mov_ptr_eax_ebx[2]; - uint8_t ror_di_1[] = {0x66, 0xd1, 0xcf}; - gum_x86_writer_put_bytes(cw, ror_di_1, sizeof(ror_di_1)); - gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_ECX, GUM_REG_EDI); + uint8_t mov_esp_c_ebx[4]; + uint8_t mov_esp_8_eax[4]; + uint8_t sahf; + uint8_t mov_esp_4_eax[4]; - gum_x86_writer_put_pop_reg(cw, GUM_REG_EDX); - gum_x86_writer_put_pop_reg(cw, GUM_REG_ECX); - gum_x86_writer_put_popfx(cw); - gum_x86_writer_put_ret(cw); +} afl_log_code_asm_t; + + #pragma pack(pop) + +typedef union { + + afl_log_code_asm_t code; + uint8_t bytes[0]; + +} afl_log_code; + +static const afl_log_code_asm_t template = + { + + .mov_eax_esp_4 = {0x89, 0x44, 0x24, 0xFC}, + .lahf = 0x9f, + .mov_eax_esp_8 = {0x89, 0x44, 0x24, 0xF8}, + .mov_ebx_esp_c = {0x89, 0x5C, 0x24, 0xF4}, + + .mov_eax_prev_loc = {0xA1}, + .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05}, + + .xor_eax_curr_loc = {0x35}, + .add_eax_area_ptr = {0x05}, + .mov_ebx_ptr_eax = {0x8a, 0x18}, + .add_bl_1 = {0x80, 0xc3, 0x01}, + .adc_bl_0 = {0x80, 0xd3, 0x00}, + .mov_ptr_eax_ebx = {0x88, 0x18}, + + .mov_esp_c_ebx = {0x8B, 0x5C, 0x24, 0xF4}, + .mov_esp_8_eax = {0x8B, 0x44, 0x24, 0xF8}, + .sahf = 0x9e, + .mov_esp_4_eax = {0x8B, 0x44, 0x24, 0xFC}, } +; + gboolean instrument_is_coverage_optimize_supported(void) { return true; } -static void instrument_coverate_write_function(GumStalkerOutput *output) { +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer start_address, + const cs_insn * from_insn, + gpointer * target) { - GumX86Writer *cw = output->writer.x86; + UNUSED_PARAMETER(self); + UNUSED_PARAMETER(start_address); + + cs_x86 * x86; + cs_x86_op *op; + if (from_insn == NULL) { return; } + + x86 = &from_insn->detail->x86; + op = x86->operands; + + if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) { + + return; + + } + + switch (from_insn->id) { + + case X86_INS_CALL: + case X86_INS_JMP: + if (x86->op_count != 1) { + + FATAL("Unexpected operand count: %d", x86->op_count); + + } + + if (op[0].type != X86_OP_IMM) { return; } + + break; + case X86_INS_RET: + break; + default: + return; - if (current_log_impl == 0 || - !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) || - !gum_x86_writer_can_branch_directly_between(cw->pc + 128, - current_log_impl)) { + } + + *target = (guint8 *)*target + sizeof(afl_log_code); + +} + +static void instrument_coverage_suppress_init(void) { - gconstpointer after_log_impl = cw->code + 1; + static gboolean initialized = false; + if (initialized) { return; } + initialized = true; - gum_x86_writer_put_jmp_near_label(cw, after_log_impl); + GumStalkerObserver * observer = stalker_get_observer(); + GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer); + iface->switch_callback = instrument_coverage_switch; - current_log_impl = cw->pc; - instrument_coverage_function(cw); + coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal); + if (coverage_blocks == NULL) { - gum_x86_writer_put_label(cw, after_log_impl); + FATAL("Failed to g_hash_table_new, errno: %d", errno); } @@ -70,14 +147,61 @@ static void instrument_coverate_write_function(GumStalkerOutput *output) { void instrument_coverage_optimize(const cs_insn * instr, GumStalkerOutput *output) { + afl_log_code code = {0}; GumX86Writer *cw = output->writer.x86; guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address)); - instrument_coverate_write_function(output); + gsize map_size_pow2; + gsize area_offset_ror; + + code.code = template; + + instrument_coverage_suppress_init(); + + // gum_x86_writer_put_breakpoint(cw); + + if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) { + + FATAL("Failed - g_hash_table_add"); + + } + + gssize prev_loc_value_offset2 = + offsetof(afl_log_code, code.mov_eax_prev_loc) + + sizeof(code.code.mov_eax_prev_loc) - sizeof(gint); + + *((gint *)&code.bytes[prev_loc_value_offset2]) = + (gint)GPOINTER_TO_SIZE(&instrument_previous_pc); + + gssize curr_loc_shr_1_offset = + offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) + + sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(guint32); + + map_size_pow2 = util_log2(__afl_map_size); + area_offset_ror = util_rotate(area_offset, 1, map_size_pow2); + + *((guint32 *)&code.bytes[curr_loc_shr_1_offset]) = (guint32)(area_offset_ror); + + gssize prev_loc_value_offset = + offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) + + sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(gint) - + sizeof(guint32); + + *((gint *)&code.bytes[prev_loc_value_offset]) = + (gint)GPOINTER_TO_SIZE(&instrument_previous_pc); + + gssize xor_curr_loc_offset = offsetof(afl_log_code, code.xor_eax_curr_loc) + + sizeof(code.code.xor_eax_curr_loc) - + sizeof(guint32); + + *((guint32 *)&code.bytes[xor_curr_loc_offset]) = (guint32)area_offset; + + gssize add_area_ptr_offset = offsetof(afl_log_code, code.add_eax_area_ptr) + + sizeof(code.code.add_eax_area_ptr) - + sizeof(guint32); + + *((guint32 *)&code.bytes[add_area_ptr_offset]) = (guint32)__afl_area_ptr; - gum_x86_writer_put_push_reg(cw, GUM_REG_EDI); - gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDI, area_offset); - gum_x86_writer_put_call_address(cw, current_log_impl); - gum_x86_writer_put_pop_reg(cw, GUM_REG_EDI); + gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code)); } diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js index 8e810d09..215fbdaf 100644 --- a/frida_mode/src/js/api.js +++ b/frida_mode/src/js/api.js @@ -151,6 +151,13 @@ class Afl { const buf = Memory.allocUtf8String(file); Afl.jsApiSetInstrumentUnstableCoverageFile(buf); } + /* + * Set a callback to be called in place of the usual `main` function. This see + * `Scripting.md` for details. + */ + static setJsMainHook(address) { + Afl.jsApiSetJsMainHook(address); + } /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. @@ -291,6 +298,7 @@ Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []); Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]); +Afl.jsApiSetJsMainHook = Afl.jsApiGetFunction("js_api_set_js_main_hook", "void", ["pointer"]); Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]); Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]); Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []); @@ -299,8 +307,8 @@ Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_retur Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []); Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []); Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]); -Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]); Afl.jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction("js_api_set_stalker_adjacent_blocks", "void", ["uint32"]); +Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]); Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]); Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]); Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]); diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c index 37cd377b..5f477388 100644 --- a/frida_mode/src/js/js.c +++ b/frida_mode/src/js/js.c @@ -3,10 +3,11 @@ #include "js.h" #include "util.h" -static char * js_script = NULL; gboolean js_done = FALSE; js_api_stalker_callback_t js_user_callback = NULL; +js_main_hook_t js_main_hook = NULL; +static char * js_script = NULL; static gchar * filename = "afl.js"; static gchar * contents; static GumScriptBackend * backend; diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c index 102423d9..5021b531 100644 --- a/frida_mode/src/js/js_api.c +++ b/frida_mode/src/js/js_api.c @@ -11,6 +11,10 @@ #include "stats.h" #include "util.h" +typedef uint8_t u8; + +extern void __afl_set_persistent_mode(u8 mode); + __attribute__((visibility("default"))) void js_api_done() { js_done = TRUE; @@ -47,13 +51,7 @@ __attribute__((visibility("default"))) void js_api_set_persistent_address( persistent_start = GPOINTER_TO_SIZE(address); - if (getenv("__AFL_PERSISTENT") == NULL) { - - FATAL( - "You must set __AFL_PERSISTENT manually if using persistent mode " - "configured using JS"); - - } + __afl_set_persistent_mode(1); } @@ -257,3 +255,10 @@ __attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks( } +__attribute__((visibility("default"))) void js_api_set_js_main_hook( + const js_main_hook_t hook) { + + js_main_hook = hook; + +} + diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c index cb88eabe..913e3a46 100644 --- a/frida_mode/src/main.c +++ b/frida_mode/src/main.c @@ -36,13 +36,13 @@ extern mach_port_t mach_task_self(); extern GumAddress gum_darwin_find_entrypoint(mach_port_t task); #else -extern int __libc_start_main(int *(main)(int, char **, char **), int argc, +extern int __libc_start_main(int (*main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void(*stack_end)); #endif -typedef int *(*main_fn_t)(int argc, char **argv, char **envp); +typedef int (*main_fn_t)(int argc, char **argv, char **envp); static main_fn_t main_fn = NULL; @@ -217,7 +217,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) { } -static int *on_main(int argc, char **argv, char **envp) { +static int on_main(int argc, char **argv, char **envp) { on_main_os(argc, argv, envp); @@ -225,12 +225,20 @@ static int *on_main(int argc, char **argv, char **envp) { afl_frida_start(); - return main_fn(argc, argv, envp); + if (js_main_hook != NULL) { + + return js_main_hook(argc, argv, envp); + + } else { + + return main_fn(argc, argv, envp); + + } } #if defined(EMBEDDED) -extern int *main(int argc, char **argv, char **envp); +extern int main(int argc, char **argv, char **envp); static void intercept_main(void) { @@ -253,7 +261,7 @@ static void intercept_main(void) { } #else -static int on_libc_start_main(int *(main)(int, char **, char **), int argc, +static int on_libc_start_main(int (*main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void(*stack_end)) { diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c index 902274b3..5425b01b 100644 --- a/frida_mode/src/persistent/persistent_x86.c +++ b/frida_mode/src/persistent/persistent_x86.c @@ -4,6 +4,7 @@ #include "instrument.h" #include "persistent.h" +#include "util.h" #if defined(__i386__) diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c index 027417ee..9844c74c 100644 --- a/frida_mode/src/ranges.c +++ b/frida_mode/src/ranges.c @@ -166,8 +166,22 @@ static void convert_token(gchar *token, GumMemoryRange *range) { gint range_sort(gconstpointer a, gconstpointer b) { - return ((GumMemoryRange *)a)->base_address - - ((GumMemoryRange *)b)->base_address; + GumMemoryRange *ra = (GumMemoryRange *)a; + GumMemoryRange *rb = (GumMemoryRange *)b; + + if (ra->base_address < rb->base_address) { + + return -1; + + } else if (ra->base_address > rb->base_address) { + + return 1; + + } else { + + return 0; + + } } @@ -249,7 +263,7 @@ static void check_for_overlaps(GArray *array) { GumAddress curr_limit = curr->base_address + curr->size; if (prev_limit > curr->base_address) { - FFATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER + FFATAL("Overlapping ranges 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x", prev->base_address, prev_limit, curr->base_address, curr_limit); diff --git a/frida_mode/src/seccomp/seccomp_event.c b/frida_mode/src/seccomp/seccomp_event.c index aca0967a..0907eff8 100644 --- a/frida_mode/src/seccomp/seccomp_event.c +++ b/frida_mode/src/seccomp/seccomp_event.c @@ -10,7 +10,13 @@ int seccomp_event_create(void) { +#ifdef SYS_eventfd int fd = syscall(SYS_eventfd, 0, 0); +#else +# ifdef SYS_eventfd2 + int fd = syscall(SYS_eventfd2, 0, 0); +# endif +#endif if (fd < 0) { FFATAL("seccomp_event_create"); } return fd; diff --git a/frida_mode/src/seccomp/seccomp_filter.c b/frida_mode/src/seccomp/seccomp_filter.c index a7c0926c..5aee398f 100644 --- a/frida_mode/src/seccomp/seccomp_filter.c +++ b/frida_mode/src/seccomp/seccomp_filter.c @@ -72,7 +72,13 @@ static struct sock_filter filter[] = { /* Allow us to make anonymous maps */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), +#ifdef __NR_mmap BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 3), +#else +# ifdef __NR_mmap2 + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap2, 0, 3), +# endif +#endif BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, args[4]))), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, -1, 0, 1), diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c index 65ed5d50..b4dd5a47 100644 --- a/frida_mode/src/stalker.c +++ b/frida_mode/src/stalker.c @@ -111,10 +111,23 @@ void stalker_init(void) { } #endif - if (stalker_ic_entries == 0) { stalker_ic_entries = 32; } - if (stalker_adjacent_blocks == 0) { stalker_adjacent_blocks = 32; } + if (instrument_coverage_filename == NULL) { + + if (stalker_adjacent_blocks == 0) { stalker_adjacent_blocks = 32; } + + } else { + + if (stalker_adjacent_blocks != 0) { + + FFATAL( + "AFL_FRIDA_STALKER_ADJACENT_BLOCKS and AFL_FRIDA_INST_COVERAGE_FILE " + "are incompatible"); + + } + + } #if defined(__x86_64__) || defined(__i386__) stalker = g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, diff --git a/frida_mode/test/freetype2/GNUmakefile b/frida_mode/test/freetype2/GNUmakefile index 891660ca..f7a50de2 100644 --- a/frida_mode/test/freetype2/GNUmakefile +++ b/frida_mode/test/freetype2/GNUmakefile @@ -84,7 +84,7 @@ all: $(TEST_BIN) make -C $(ROOT)frida_mode/ 32: - CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all + CFLAGS="-m32" CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all $(BUILD_DIR): mkdir -p $@ @@ -126,7 +126,7 @@ $(HARNESS_SRC): wget -O $@ $(HARNESS_URL) $(HARNESS_OBJ): $(HARNESS_SRC) - $(CC) -o $@ -c $< + $(CC) $(CFLAGS) -o $@ -c $< ########## TEST ####### diff --git a/frida_mode/test/js/GNUmakefile b/frida_mode/test/js/GNUmakefile index aad81d08..c702ad98 100644 --- a/frida_mode/test/js/GNUmakefile +++ b/frida_mode/test/js/GNUmakefile @@ -10,6 +10,7 @@ TESTINSTSRC:=$(PWD)test.c TESTINSTBIN2:=$(BUILD_DIR)test2 TESTINSTSRC2:=$(PWD)test2.c +AFLPP_DRIVER_DUMMY_INPUT:=$(BUILD_DIR)dummy QEMU_OUT:=$(BUILD_DIR)qemu-out FRIDA_OUT:=$(BUILD_DIR)frida-out @@ -40,9 +41,36 @@ $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR) $(TESTINSTBIN2): $(TESTINSTSRC2) | $(BUILD_DIR) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< +$(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR) + dd if=/dev/zero bs=1048576 count=1 of=$@ + clean: rm -rf $(BUILD_DIR) +frida_js_main: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT) + AFL_PRELOAD=$(AFL_PRELOAD) \ + AFL_FRIDA_JS_SCRIPT=main.js \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(TEST_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -t 10000+ \ + -- \ + $(TESTINSTBIN) $(AFLPP_DRIVER_DUMMY_INPUT) + +frida_js_fuzz: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT) + AFL_PRELOAD=$(AFL_PRELOAD) \ + AFL_FRIDA_JS_SCRIPT=fuzz.js \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(TEST_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -t 10000+ \ + -- \ + $(TESTINSTBIN) $(AFLPP_DRIVER_DUMMY_INPUT) + frida_js_entry: $(TESTINSTBIN) $(TEST_DATA_FILE) AFL_PRELOAD=$(AFL_PRELOAD) \ AFL_FRIDA_JS_SCRIPT=entry.js \ diff --git a/frida_mode/test/js/fuzz.js b/frida_mode/test/js/fuzz.js new file mode 100644 index 00000000..24eca2b6 --- /dev/null +++ b/frida_mode/test/js/fuzz.js @@ -0,0 +1,41 @@ +Afl.print('******************'); +Afl.print('* AFL FRIDA MODE *'); +Afl.print('******************'); +Afl.print(''); + +Afl.print(`PID: ${Process.id}`); + +const name = Process.enumerateModules()[0].name; +Afl.print(`Name: ${name}`); + +new ModuleMap().values().forEach(m => { + Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`); +}); + +const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address; +Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`); + +const cm = new CModule(` + + extern unsigned char * __afl_fuzz_ptr; + extern unsigned int * __afl_fuzz_len; + extern void LLVMFuzzerTestOneInput(char *buf, int len); + + void My_LLVMFuzzerTestOneInput(char *buf, int len) { + + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + `, + { + LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput, + __afl_fuzz_ptr: Afl.getAflFuzzPtr(), + __afl_fuzz_len: Afl.getAflFuzzLen() + }); + +Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput); +Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput); +Afl.setInMemoryFuzzing(); +Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput); +Afl.print("done"); +Afl.done(); diff --git a/frida_mode/test/js/main.js b/frida_mode/test/js/main.js new file mode 100644 index 00000000..06306fc4 --- /dev/null +++ b/frida_mode/test/js/main.js @@ -0,0 +1,44 @@ +Afl.print('******************'); +Afl.print('* AFL FRIDA MODE *'); +Afl.print('******************'); +Afl.print(''); + +Afl.print(`PID: ${Process.id}`); + +const name = Process.enumerateModules()[0].name; +Afl.print(`Name: ${name}`); + +new ModuleMap().values().forEach(m => { + Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`); +}); + +const main = DebugSymbol.fromName('main').address; +Afl.print(`main: ${main}`); + +const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address; +Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`); + +const cm = new CModule(` + + extern unsigned char * __afl_fuzz_ptr; + extern unsigned int * __afl_fuzz_len; + extern void LLVMFuzzerTestOneInput(char *buf, int len); + + int main(int argc, char **argv) { + + LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len); + + } + `, + { + LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput, + __afl_fuzz_ptr: Afl.getAflFuzzPtr(), + __afl_fuzz_len: Afl.getAflFuzzLen() + }); + +Afl.setEntryPoint(cm.main); +Afl.setPersistentAddress(cm.main); +Afl.setInMemoryFuzzing(); +Afl.setJsMainHook(cm.main); +Afl.print("done"); +Afl.done(); diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile index 23aa94d0..5010662b 100644 --- a/frida_mode/test/png/persistent/hook/GNUmakefile +++ b/frida_mode/test/png/persistent/hook/GNUmakefile @@ -144,7 +144,6 @@ frida_entry_slow: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $ frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR) AFL_PRELOAD=$(AFL_PRELOAD) \ - __AFL_PERSISTENT=1 \ AFL_FRIDA_JS_SCRIPT=load.js \ $(ROOT)afl-fuzz \ -D \ diff --git a/frida_mode/test/vorbis/GNUmakefile b/frida_mode/test/vorbis/GNUmakefile new file mode 100644 index 00000000..59ae9a59 --- /dev/null +++ b/frida_mode/test/vorbis/GNUmakefile @@ -0,0 +1,200 @@ +PWD:=$(shell pwd)/ +ROOT:=$(PWD)../../../ +BUILD_DIR:=$(PWD)build/ + +AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so +AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so + +OGG_GIT_REPO:=https://github.com/xiph/ogg.git +OGG_BUILD_DIR:=$(BUILD_DIR)ogg/ +OGG_DIR:=$(OGG_BUILD_DIR)ogg/ +OGG_INSTALL:=$(OGG_BUILD_DIR)install/ +OGG_LIB:=$(OGG_INSTALL)lib/libogg.a + +VORBIS_GIT_REPO:=https://github.com/xiph/vorbis.git +VORBIS_BUILD_DIR:=$(BUILD_DIR)vorbis/ +VORBIS_DIR:=$(VORBIS_BUILD_DIR)vorbis/ +VORBIS_INSTALL:=$(VORBIS_BUILD_DIR)install/ +VORBIS_LIB:=$(VORBIS_INSTALL)lib/libvorbis.a +VORBISFILE_LIB:=$(VORBIS_INSTALL)lib/libvorbisfile.a + +DECODE_URL:=https://raw.githubusercontent.com/google/oss-fuzz/688aadaf44499ddada755562109e5ca5eb3c5662/projects/vorbis/decode_fuzzer.cc +DECODE_SRC:=$(BUILD_DIR)decode_fuzzer.cc +DECODE_OBJ:=$(BUILD_DIR)decode_fuzzer.o + +HARNESS_URL:=https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c +HARNESS_SRC:=$(BUILD_DIR)StandaloneFuzzTargetMain.c +HARNESS_OBJ:=$(BUILD_DIR)StandaloneFuzzTargetMain.o + +LDFLAGS += -lpthread + +TEST_BIN:=$(BUILD_DIR)decode_fuzzer +ifeq "$(shell uname)" "Darwin" +TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie +endif + +TEST_DATA_DIR:=$(BUILD_DIR)in/ +TEST_DATA_SRC:=https://github.com/google/fuzzbench/blob/master/benchmarks/vorbis-2017-12-11/seeds/sound.ogg?raw=true +TEST_DATA_FILE:=$(TEST_DATA_DIR)sound.ogg +DUMMY_DATA_FILE:=$(BUILD_DIR)default_seed + +FRIDA_OUT:=$(BUILD_DIR)frida-out +QEMU_OUT:=$(BUILD_DIR)qemu-out + +ifndef ARCH + +ARCH=$(shell uname -m) +ifeq "$(ARCH)" "aarch64" + ARCH:=arm64 +endif + +ifeq "$(ARCH)" "i686" + ARCH:=x86 +endif +endif + +GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh + +AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000) + +ifeq "$(ARCH)" "aarch64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000) +endif + +ifeq "$(ARCH)" "x86_64" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000) +endif + +ifeq "$(ARCH)" "x86" + AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000) +endif + +.PHONY: all clean frida hook + +all: $(TEST_BIN) + make -C $(ROOT)frida_mode/ + +32: + CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all + +$(BUILD_DIR): + mkdir -p $@ + +########## OGG ####### + +$(OGG_BUILD_DIR): | $(BUILD_DIR) + mkdir -p $@ + +$(OGG_DIR): | $(OGG_BUILD_DIR) + git clone $(OGG_GIT_REPO) $@ + git -C $(OGG_DIR) checkout c8391c2b267a7faf9a09df66b1f7d324e9eb7766 + +$(OGG_LIB): | $(OGG_DIR) + cd $(OGG_DIR) && ./autogen.sh + cd $(OGG_DIR) && ./configure \ + --prefix=$(OGG_INSTALL) \ + --enable-static \ + --disable-shared \ + --disable-crc + make -C $(OGG_DIR) install + +ogg: $(OGG_LIB) + +########## VORBIS ####### + +$(VORBIS_BUILD_DIR): | $(BUILD_DIR) + mkdir -p $@ + +$(VORBIS_DIR): | $(VORBIS_BUILD_DIR) + git clone $(VORBIS_GIT_REPO) $@ + git -C $(VORBIS_DIR) checkout c1c2831fc7306d5fbd7bc800324efd12b28d327f + +$(VORBIS_LIB): $(OGG_LIB) | $(VORBIS_DIR) + cd $(VORBIS_DIR) && ./autogen.sh + cd $(VORBIS_DIR) && ./configure \ + --prefix=$(VORBIS_INSTALL) \ + --enable-static \ + --disable-shared \ + --with-ogg=$(OGG_INSTALL) + make -C $(VORBIS_DIR) install + +vorbis: $(VORBIS_LIB) + +########## HARNESS ####### + +$(DECODE_SRC): + wget -O $@ $(DECODE_URL) + +$(DECODE_OBJ): $(DECODE_SRC) + $(CXX) -o $@ -c $< -I$(VORBIS_DIR)include/ -I$(OGG_DIR)include/ + +decode: $(DECODE_OBJ) + +########## HARNESS ####### + +$(HARNESS_SRC): + wget -O $@ $(HARNESS_URL) + +$(HARNESS_OBJ): $(HARNESS_SRC) + $(CC) -o $@ -c $< + +harness: $(HARNESS_OBJ) + +########## TEST ####### + +$(TEST_BIN): $(VORBIS_LIB) $(OGG_LIB) $(HARNESS_OBJ) $(DECODE_OBJ) + $(CXX) \ + $(CXXFLAGS) \ + -std=c++11 \ + $(DECODE_OBJ) \ + $(HARNESS_OBJ) \ + $(VORBISFILE_LIB) \ + $(VORBIS_LIB) \ + $(OGG_LIB) \ + -o $@ + +########## DUMMY ####### + +$(TEST_DATA_DIR): | $(BUILD_DIR) + mkdir -p $@ + +$(DUMMY_DATA_FILE): | $(TEST_DATA_DIR) + dd if=/dev/zero bs=1048576 count=1 of=$@ + +###### TEST DATA ####### + +$(TEST_DATA_FILE): + wget -O $@ $(TEST_DATA_SRC) + +clean: + rm -rf $(BUILD_DIR) + +frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE) $(DUMMY_DATA_FILE) + AFL_FRIDA_PERSISTENT_CNT=1000000 \ + AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \ + $(ROOT)afl-fuzz \ + -i $(TEST_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -m none \ + -d \ + -O \ + -V 30 \ + -- \ + $(TEST_BIN) $(DUMMY_DATA_FILE) + +qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE) $(DUMMY_DATA_FILE) + AFL_QEMU_PERSISTENT_CNT=1000000 \ + AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \ + AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \ + AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \ + $(ROOT)afl-fuzz \ + -i $(TEST_DATA_DIR) \ + -o $(QEMU_OUT) \ + -m none \ + -d \ + -Q \ + -V 30 \ + -- \ + $(TEST_BIN) $(DUMMY_DATA_FILE) diff --git a/frida_mode/test/vorbis/Makefile b/frida_mode/test/vorbis/Makefile new file mode 100644 index 00000000..07b139e9 --- /dev/null +++ b/frida_mode/test/vorbis/Makefile @@ -0,0 +1,13 @@ +all: + @echo trying to use GNU make... + @gmake all || echo please install GNUmake + +32: + @echo trying to use GNU make... + @gmake 32 || echo please install GNUmake + +clean: + @gmake clean + +frida: + @gmake frida diff --git a/frida_mode/test/vorbis/get_symbol_addr.py b/frida_mode/test/vorbis/get_symbol_addr.py new file mode 100755 index 00000000..1c46e010 --- /dev/null +++ b/frida_mode/test/vorbis/get_symbol_addr.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +import argparse +from elftools.elf.elffile import ELFFile + +def process_file(file, symbol, base): + with open(file, 'rb') as f: + elf = ELFFile(f) + symtab = elf.get_section_by_name('.symtab') + mains = symtab.get_symbol_by_name(symbol) + if len(mains) != 1: + print ("Failed to find main") + return 1 + + main_addr = mains[0]['st_value'] + main = base + main_addr + print ("0x%016x" % main) + return 0 + +def hex_value(x): + return int(x, 16) + +def main(): + parser = argparse.ArgumentParser(description='Process some integers.') + parser.add_argument('-f', '--file', dest='file', type=str, + help='elf file name', required=True) + parser.add_argument('-s', '--symbol', dest='symbol', type=str, + help='symbol name', required=True) + parser.add_argument('-b', '--base', dest='base', type=hex_value, + help='elf base address', required=True) + + args = parser.parse_args() + return process_file (args.file, args.symbol, args.base) + +if __name__ == "__main__": + ret = main() + exit(ret) diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts index e20ad3ec..0473cbf6 100644 --- a/frida_mode/ts/lib/afl.ts +++ b/frida_mode/ts/lib/afl.ts @@ -179,6 +179,14 @@ class Afl { Afl.jsApiSetInstrumentUnstableCoverageFile(buf); } + /* + * Set a callback to be called in place of the usual `main` function. This see + * `Scripting.md` for details. + */ + public static setJsMainHook(address: NativePointer): void { + Afl.jsApiSetJsMainHook(address); + } + /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. @@ -387,6 +395,11 @@ class Afl { "void", ["pointer"]); + private static readonly jsApiSetJsMainHook = Afl.jsApiGetFunction( + "js_api_set_js_main_hook", + "void", + ["pointer"]); + private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction( "js_api_set_persistent_address", "void", @@ -427,16 +440,16 @@ class Afl { "void", ["pointer"]); - private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction( - "js_api_set_stalker_callback", - "void", - ["pointer"]); - private static readonly jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction( "js_api_set_stalker_adjacent_blocks", "void", ["uint32"]); + private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction( + "js_api_set_stalker_callback", + "void", + ["pointer"]); + private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction( "js_api_set_stalker_ic_entries", "void", diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 4e25221a..37726607 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -621,7 +621,6 @@ bool ModuleSanitizerCoverage::instrumentModule( bool isStrncasecmp = true; bool isIntMemcpy = true; bool isStdString = true; - bool addedNull = false; size_t optLen = 0; Function *Callee = callInst->getCalledFunction(); @@ -801,7 +800,6 @@ bool ModuleSanitizerCoverage::instrumentModule( if (literalLength + 1 == optLength) { Str2.append("\0", 1); // add null byte - // addedNull = true; } @@ -909,8 +907,8 @@ bool ModuleSanitizerCoverage::instrumentModule( if (optLen < 2) { continue; } if (literalLength + 1 == optLen) { // add null byte + thestring.append("\0", 1); - addedNull = true; } @@ -922,14 +920,18 @@ bool ModuleSanitizerCoverage::instrumentModule( // was not already added if (!isMemcmp) { - if (addedNull == false && thestring[optLen - 1] != '\0') { + /* + if (addedNull == false && thestring[optLen - 1] != + '\0') { - thestring.append("\0", 1); // add null byte - optLen++; + thestring.append("\0", 1); // add null byte + optLen++; - } + } - if (!isStdString) { + */ + if (!isStdString && + thestring.find('\0', 0) != std::string::npos) { // ensure we do not have garbage size_t offset = thestring.find('\0', 0); diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index 759c813a..5d198ada 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -670,7 +670,7 @@ static void __afl_start_snapshots(void) { u8 child_stopped = 0; - void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); /* Phone home and tell the parent that we're OK. If parent isn't there, assume we're not running in forkserver mode and just execute program. */ @@ -926,7 +926,7 @@ static void __afl_start_forkserver(void) { u8 child_stopped = 0; - void (*old_sigchld_handler)(int) = 0; // = signal(SIGCHLD, SIG_DFL); + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) { @@ -1885,30 +1885,21 @@ static int area_is_valid(void *ptr, size_t len) { } -void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) { - - /* - u32 i; - if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; - fprintf(stderr, "rtn_n len=%u arg0=", len); - for (i = 0; i < len; i++) - fprintf(stderr, "%02x", ptr1[i]); - fprintf(stderr, " arg1="); - for (i = 0; i < len; i++) - fprintf(stderr, "%02x", ptr2[i]); - fprintf(stderr, "\n"); - */ +/* hook for string with length functions, eg. strncmp, strncasecmp etc. + Note that we ignore the len parameter and take longer strings if present. */ +void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) { - if (likely(!__afl_cmp_map)) return; // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len); + if (likely(!__afl_cmp_map)) return; if (unlikely(!len)) return; - int l = MIN(31, len); - - if ((l = area_is_valid(ptr1, l)) <= 0 || - (l = area_is_valid(ptr2, l)) <= 0) - return; + int len0 = MIN(len, 31); + int len1 = strnlen(ptr1, len0); + if (len1 < 31) len1 = area_is_valid(ptr1, len1 + 1); + int len2 = strnlen(ptr2, len0); + if (len2 < 31) len2 = area_is_valid(ptr1, len2 + 1); + int l = MAX(len1, len2); + if (l < 2) return; - // fprintf(stderr, "RTN2 %u\n", l); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -1936,27 +1927,25 @@ void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) { struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k]; hits &= CMP_MAP_RTN_H - 1; - cmpfn[hits].v0_len = l; - cmpfn[hits].v1_len = l; - __builtin_memcpy(cmpfn[hits].v0, ptr1, l); - __builtin_memcpy(cmpfn[hits].v1, ptr2, l); + cmpfn[hits].v0_len = 0x80 + l; + cmpfn[hits].v1_len = 0x80 + l; + __builtin_memcpy(cmpfn[hits].v0, ptr1, len1); + __builtin_memcpy(cmpfn[hits].v1, ptr2, len2); // fprintf(stderr, "RTN3\n"); } -void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) { - - /* - if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; - fprintf(stderr, "rtn_strn len=%u arg0=%s arg1=%s\n", len, ptr1, ptr2); - */ +/* hook for string functions, eg. strcmp, strcasecmp etc. */ +void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) { + // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); if (likely(!__afl_cmp_map)) return; - // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len); - if (unlikely(!len)) return; - int l = MIN(31, len + 1); + if (unlikely(!ptr1 || !ptr2)) return; + int len1 = strnlen(ptr1, 30) + 1; + int len2 = strnlen(ptr2, 30) + 1; + int l = MAX(len1, len2); + if (l < 3) return; - // fprintf(stderr, "RTN2 %u\n", l); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -1984,29 +1973,38 @@ void __cmplog_rtn_hook_strn(u8 *ptr1, u8 *ptr2, u64 len) { struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k]; hits &= CMP_MAP_RTN_H - 1; - cmpfn[hits].v0_len = 0x80 + l; - cmpfn[hits].v1_len = 0x80 + l; - __builtin_memcpy(cmpfn[hits].v0, ptr1, l); - __builtin_memcpy(cmpfn[hits].v1, ptr2, l); + cmpfn[hits].v0_len = 0x80 + len1; + cmpfn[hits].v1_len = 0x80 + len2; + __builtin_memcpy(cmpfn[hits].v0, ptr1, len1); + __builtin_memcpy(cmpfn[hits].v1, ptr2, len2); // fprintf(stderr, "RTN3\n"); } -void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) { +/* hook function for all other func(ptr, ptr, ...) variants */ +void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { /* + u32 i; if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; - fprintf(stderr, "rtn_str arg0=%s arg1=%s\n", ptr1, ptr2); + fprintf(stderr, "rtn arg0="); + for (i = 0; i < 32; i++) + fprintf(stderr, "%02x", ptr1[i]); + fprintf(stderr, " arg1="); + for (i = 0; i < 32; i++) + fprintf(stderr, "%02x", ptr2[i]); + fprintf(stderr, "\n"); */ - if (likely(!__afl_cmp_map)) return; // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); - if (unlikely(!ptr1 || !ptr2)) return; - int len1 = MIN(31, strlen(ptr1) + 1); - int len2 = MIN(31, strlen(ptr2) + 1); - int l = MIN(MAX(len1, len2), 31); + if (likely(!__afl_cmp_map)) return; + int l1, l2; + if ((l1 = area_is_valid(ptr1, 31)) <= 0 || + (l2 = area_is_valid(ptr2, 31)) <= 0) + return; + int len = MIN(31, MIN(l1, l2)); - // fprintf(stderr, "RTN2 %u\n", l); + // fprintf(stderr, "RTN2 %u\n", len); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2016,16 +2014,16 @@ void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) { __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; __afl_cmp_map->headers[k].hits = 1; - __afl_cmp_map->headers[k].shape = l - 1; + __afl_cmp_map->headers[k].shape = len - 1; hits = 0; } else { hits = __afl_cmp_map->headers[k].hits++; - if (__afl_cmp_map->headers[k].shape < l) { + if (__afl_cmp_map->headers[k].shape < len) { - __afl_cmp_map->headers[k].shape = l - 1; + __afl_cmp_map->headers[k].shape = len - 1; } @@ -2034,37 +2032,44 @@ void __cmplog_rtn_hook_str(u8 *ptr1, u8 *ptr2) { struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k]; hits &= CMP_MAP_RTN_H - 1; - cmpfn[hits].v0_len = 0x80 + len1; - cmpfn[hits].v1_len = 0x80 + len2; - __builtin_memcpy(cmpfn[hits].v0, ptr1, len1); - __builtin_memcpy(cmpfn[hits].v1, ptr2, len2); + cmpfn[hits].v0_len = len; + cmpfn[hits].v1_len = len; + __builtin_memcpy(cmpfn[hits].v0, ptr1, len); + __builtin_memcpy(cmpfn[hits].v1, ptr2, len); // fprintf(stderr, "RTN3\n"); } -void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { +/* hook for func(ptr, ptr, len, ...) looking functions. + Note that for the time being we ignore len as this could be wrong + information and pass it on to the standard binary rtn hook */ +void __cmplog_rtn_hook_n(u8 *ptr1, u8 *ptr2, u64 len) { + (void)(len); + __cmplog_rtn_hook(ptr1, ptr2); + +#if 0 /* u32 i; if (area_is_valid(ptr1, 31) <= 0 || area_is_valid(ptr2, 31) <= 0) return; - fprintf(stderr, "rtn arg0="); - for (i = 0; i < 32; i++) + fprintf(stderr, "rtn_n len=%u arg0=", len); + for (i = 0; i < len; i++) fprintf(stderr, "%02x", ptr1[i]); fprintf(stderr, " arg1="); - for (i = 0; i < 32; i++) + for (i = 0; i < len; i++) fprintf(stderr, "%02x", ptr2[i]); fprintf(stderr, "\n"); */ + // fprintf(stderr, "RTN1 %p %p %u\n", ptr1, ptr2, len); if (likely(!__afl_cmp_map)) return; - // fprintf(stderr, "RTN1 %p %p\n", ptr1, ptr2); - int l1, l2; - if ((l1 = area_is_valid(ptr1, 31)) <= 0 || - (l2 = area_is_valid(ptr2, 31)) <= 0) + if (unlikely(!len)) return; + int l = MIN(31, len); + + if ((l = area_is_valid(ptr1, l)) <= 0 || (l = area_is_valid(ptr2, l)) <= 0) return; - int len = MIN(31, MIN(l1, l2)); - // fprintf(stderr, "RTN2 %u\n", len); + // fprintf(stderr, "RTN2 %u\n", l); uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (uintptr_t)(default_hash((u8 *)&k, sizeof(uintptr_t)) & (CMP_MAP_W - 1)); @@ -2074,16 +2079,16 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; __afl_cmp_map->headers[k].hits = 1; - __afl_cmp_map->headers[k].shape = len - 1; + __afl_cmp_map->headers[k].shape = l - 1; hits = 0; } else { hits = __afl_cmp_map->headers[k].hits++; - if (__afl_cmp_map->headers[k].shape < len) { + if (__afl_cmp_map->headers[k].shape < l) { - __afl_cmp_map->headers[k].shape = len - 1; + __afl_cmp_map->headers[k].shape = l - 1; } @@ -2092,11 +2097,12 @@ void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { struct cmpfn_operands *cmpfn = (struct cmpfn_operands *)__afl_cmp_map->log[k]; hits &= CMP_MAP_RTN_H - 1; - cmpfn[hits].v0_len = len; - cmpfn[hits].v1_len = len; - __builtin_memcpy(cmpfn[hits].v0, ptr1, len); - __builtin_memcpy(cmpfn[hits].v1, ptr2, len); + cmpfn[hits].v0_len = l; + cmpfn[hits].v1_len = l; + __builtin_memcpy(cmpfn[hits].v0, ptr1, l); + __builtin_memcpy(cmpfn[hits].v1, ptr2, l); // fprintf(stderr, "RTN3\n"); +#endif } @@ -2247,5 +2253,11 @@ void __afl_coverage_interesting(u8 val, u32 id) { } +void __afl_set_persistent_mode(u8 mode) { + + is_persistent = mode; + +} + #undef write_error diff --git a/instrumentation/afl-llvm-dict2file.so.cc b/instrumentation/afl-llvm-dict2file.so.cc index 7c04c0c5..2ffa8fbb 100644 --- a/instrumentation/afl-llvm-dict2file.so.cc +++ b/instrumentation/afl-llvm-dict2file.so.cc @@ -291,7 +291,6 @@ bool AFLdict2filePass::runOnModule(Module &M) { bool isIntMemcpy = true; bool isStdString = true; bool isStrstr = true; - bool addedNull = false; size_t optLen = 0; Function *Callee = callInst->getCalledFunction(); @@ -591,7 +590,6 @@ bool AFLdict2filePass::runOnModule(Module &M) { if (optLen < 2) { continue; } if (literalLength + 1 == optLen) { // add null byte thestring.append("\0", 1); - addedNull = true; } @@ -603,18 +601,27 @@ bool AFLdict2filePass::runOnModule(Module &M) { // was not already added if (!isMemcmp) { - if (addedNull == false && thestring[optLen - 1] != '\0') { + /* + if (addedNull == false && thestring[optLen - 1] != '\0') + { - thestring.append("\0", 1); // add null byte - optLen++; + thestring.append("\0", 1); // add null byte + optLen++; - } + } + + */ if (!isStdString) { // ensure we do not have garbage size_t offset = thestring.find('\0', 0); - if (offset + 1 < optLen) optLen = offset + 1; + if (offset && offset < optLen && 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 deleted file mode 100644 index cd43b437..00000000 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ /dev/null @@ -1,1119 +0,0 @@ -/* - american fuzzy lop++ - LLVM LTO instrumentation pass - ---------------------------------------------------- - - Written by Marc Heuse <mh@mh-sec.de> - - 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: - - https://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-lto. - - */ - -#define AFL_LLVM_PASS - -#include "config.h" -#include "debug.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/time.h> - -#include <list> -#include <memory> -#include <string> -#include <fstream> -#include <set> -#include <iostream> - -#include "llvm/Config/llvm-config.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.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 "llvm/Pass.h" -#include "llvm/IR/Constants.h" - -#include "afl-llvm-common.h" - -using namespace llvm; - -namespace { - -class AFLLTOPass : public ModulePass { - - public: - static char ID; - - AFLLTOPass() : ModulePass(ID) { - - char *ptr; - - 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) - FATAL("AFL_LLVM_LTO_STARTID value of \"%s\" is not between 0 and %u\n", - ptr, MAP_SIZE - 1); - - skip_nozero = getenv("AFL_LLVM_SKIP_NEVERZERO"); - - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - - ModulePass::getAnalysisUsage(AU); - AU.addRequired<DominatorTreeWrapperPass>(); - AU.addRequired<LoopInfoWrapperPass>(); - - } - - bool runOnModule(Module &M) override; - - protected: - uint32_t afl_global_id = 1, autodictionary = 1; - uint32_t function_minimum_size = 1; - uint32_t inst_blocks = 0, inst_funcs = 0, total_instr = 0; - unsigned long long int map_addr = 0x10000; - const char * skip_nozero = NULL; - const char * use_threadsafe_counters = nullptr; - -}; - -} // namespace - -bool AFLLTOPass::runOnModule(Module &M) { - - LLVMContext & C = M.getContext(); - std::vector<std::string> dictionary; - // std::vector<CallInst *> calls; - DenseMap<Value *, std::string *> valueMap; - std::vector<BasicBlock *> BlockList; - std::ofstream dFile; - char * ptr; - size_t found = 0; - - srand((unsigned int)time(NULL)); - - unsigned long long int moduleID = - (((unsigned long long int)(rand() & 0xffffffff)) << 32) | getpid(); - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); - IntegerType *Int64Ty = IntegerType::getInt64Ty(C); - - /* Show a banner */ - setvbuf(stdout, NULL, _IONBF, 0); - - if ((isatty(2) && !getenv("AFL_QUIET")) || debug) { - - SAYF(cCYA "afl-llvm-lto" VERSION cRST - " by Marc \"vanHauser\" Heuse <mh@mh-sec.de>\n"); - - } else - - be_quiet = 1; - - use_threadsafe_counters = getenv("AFL_LLVM_THREADSAFE_INST"); - - if ((ptr = getenv("AFL_LLVM_DOCUMENT_IDS")) != NULL) { - - dFile.open(ptr, std::ofstream::out | std::ofstream::app); - if (!dFile.is_open()) WARNF("Cannot access document file %s", ptr); - - } - - // 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 ((ptr = getenv("AFL_LLVM_MAP_ADDR"))) { - - uint64_t val; - if (!*ptr || !strcmp(ptr, "0") || !strcmp(ptr, "0x0")) { - - map_addr = 0; - - } else if (getenv("AFL_LLVM_MAP_DYNAMIC")) { - - 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 0x%llx\n", map_addr); } - - /* Get/set the 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)); - - } - - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - // 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; - if (!GV.getName().str().empty()) { - - fprintf(stderr, "Global Variable: %s", GV.getName().str().c_str()); - if (GV.hasInitializer()) - if (auto *Val = dyn_cast<ConstantDataArray>(GV.getInitializer())) - fprintf(stderr, " Value: \"%s\"", Val->getAsString().str().c_str()); - fprintf(stderr, "\n"); - - } - - } - - */ - - scanForDangerousFunctions(&M); - - /* Instrument all the things! */ - - int inst_blocks = 0; - - for (auto &F : M) { - - /*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; - - // the instrument file list check - AttributeList Attrs = F.getAttributes(); -#if LLVM_VERSION_MAJOR < 14 - if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { - -#else - if (Attrs.hasFnAttr(StringRef("skipinstrument"))) { - -#endif - - if (debug) - fprintf(stderr, - "DEBUG: Function %s is not in a source file that was specified " - "in the instrument file list\n", - F.getName().str().c_str()); - continue; - - } - - std::vector<BasicBlock *> InsBlocks; - - 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 (auto &BB : F) { - - for (auto &IN : BB) { - - CallInst *callInst = nullptr; - CmpInst * cmpInst = nullptr; - - if ((cmpInst = dyn_cast<CmpInst>(&IN))) { - - Value * op = cmpInst->getOperand(1); - ConstantInt *ilen = dyn_cast<ConstantInt>(op); - - if (ilen && ilen->uge(0xffffffffffffffff) == false) { - - 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<CallInst>(&IN))) { - - bool isStrcmp = true; - bool isMemcmp = true; - bool isStrncmp = true; - bool isStrcasecmp = true; - bool isStrncasecmp = true; - bool isIntMemcpy = true; - bool isStdString = true; - bool addedNull = false; - size_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") || - !FuncName.compare("xmlStrcmp") || - !FuncName.compare("xmlStrEqual") || - !FuncName.compare("g_strcmp0") || - !FuncName.compare("curl_strequal") || - !FuncName.compare("strcsequal")); - isMemcmp &= - (!FuncName.compare("memcmp") || !FuncName.compare("bcmp") || - !FuncName.compare("CRYPTO_memcmp") || - !FuncName.compare("OPENSSL_memcmp") || - !FuncName.compare("memcmp_const_time") || - !FuncName.compare("memcmpct")); - isStrncmp &= (!FuncName.compare("strncmp") || - !FuncName.compare("xmlStrncmp") || - !FuncName.compare("curl_strnequal")); - isStrcasecmp &= (!FuncName.compare("strcasecmp") || - !FuncName.compare("stricmp") || - !FuncName.compare("ap_cstr_casecmp") || - !FuncName.compare("OPENSSL_strcasecmp") || - !FuncName.compare("xmlStrcasecmp") || - !FuncName.compare("g_strcasecmp") || - !FuncName.compare("g_ascii_strcasecmp") || - !FuncName.compare("Curl_strcasecompare") || - !FuncName.compare("Curl_safe_strcasecompare") || - !FuncName.compare("cmsstrcasecmp")); - isStrncasecmp &= (!FuncName.compare("strncasecmp") || - !FuncName.compare("strnicmp") || - !FuncName.compare("ap_cstr_casecmpn") || - !FuncName.compare("OPENSSL_strncasecmp") || - !FuncName.compare("xmlStrncasecmp") || - !FuncName.compare("g_ascii_strncasecmp") || - !FuncName.compare("Curl_strncasecompare") || - !FuncName.compare("g_strncasecmp")); - isIntMemcpy &= !FuncName.compare("llvm.memcpy.p0i8.p0i8.i64"); - 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 */ - 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 && !isStdString) - 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(); - isStdString &= FT->getNumParams() >= 2 && - FT->getParamType(0)->isPointerTy() && - FT->getParamType(1)->isPointerTy(); - - if (!isStrcmp && !isMemcmp && !isStrncmp && !isStrcasecmp && - !isStrncasecmp && !isIntMemcpy && !isStdString) - 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 { - - HasStr1 = true; - Str1 = TmpStr.str(); - - } - - bool HasStr2; - getConstantStringInfo(Str2P, TmpStr); - if (TmpStr.empty()) { - - HasStr2 = false; - - } else { - - HasStr2 = true; - 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<ConstantExpr>(Str2P); - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast<ConstantDataArray>( - Var->getInitializer())) { - - HasStr2 = true; - Str2 = Array->getRawDataValues().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<ConstantInt>(op2); - if (ilen) { - - uint64_t literalLength = Str2.size(); - uint64_t optLength = ilen->getZExtValue(); - if (optLength > literalLength + 1) { - - optLength = Str2.length() + 1; - - } - - 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<ConstantExpr>(Str1P); - - if (Ptr && Ptr->isGEPWithNoNotionalOverIndexing()) { - - if (auto *Var = dyn_cast<GlobalVariable>(Ptr->getOperand(0))) { - - if (Var->hasInitializer()) { - - if (auto *Array = dyn_cast<ConstantDataArray>( - Var->getInitializer())) { - - HasStr1 = true; - Str1 = Array->getRawDataValues().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 (optLen < 2 || (optLen == 2 && !thestring[1])) { continue; } - - if (isMemcmp || isStrncmp || isStrncasecmp) { - - Value * op2 = callInst->getArgOperand(2); - ConstantInt *ilen = dyn_cast<ConstantInt>(op2); - - if (ilen) { - - uint64_t literalLength = optLen; - optLen = ilen->getZExtValue(); - if (optLen > literalLength + 1) { optLen = literalLength + 1; } - if (optLen < 2) { continue; } - 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 (!isMemcmp) { - - if (addedNull == false && thestring[optLen - 1] != '\0') { - - thestring.append("\0", 1); // add null byte - 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); - - } - - } - - if (!be_quiet) { - - fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, - 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)); - - } - - } - - } - - } - - for (auto &BB : F) { - - if (F.size() == 1) { - - InsBlocks.push_back(&BB); - continue; - - } - - uint32_t succ = 0; - for (succ_iterator SI = succ_begin(&BB), SE = succ_end(&BB); SI != SE; - ++SI) - if ((*SI)->size() > 0) succ++; - if (succ < 2) // no need to instrument - continue; - - if (BlockList.size()) { - - int skip = 0; - for (uint32_t k = 0; k < BlockList.size(); k++) { - - if (&BB == 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; - - } - - InsBlocks.push_back(&BB); - - } - - if (InsBlocks.size() > 0) { - - uint32_t i = InsBlocks.size(); - - do { - - --i; - BasicBlock * newBB = NULL; - BasicBlock * origBB = &(*InsBlocks[i]); - std::vector<BasicBlock *> 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) { - - BasicBlock *succ = *SI; - Successors.push_back(succ); - - } - - 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 < countto; j++) { - - if (fs != 1) newBB = llvm::SplitEdge(origBB, Successors[j]); - - if (!newBB) { - - if (!be_quiet) WARNF("Split failed!"); - continue; - - } - - if (dFile.is_open()) { - - dFile << "ModuleID=" << moduleID - << " Function=" << F.getName().str() - << " edgeID=" << afl_global_id << "\n"; - - } - - BasicBlock::iterator IP = newBB->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - /* Set the ID of the inserted basic block */ - - ConstantInt *CurLoc = ConstantInt::get(Int32Ty, afl_global_id++); - - /* Load SHM pointer */ - - Value *MapPtrIdx; - - if (map_addr) { - - MapPtrIdx = IRB.CreateGEP(MapPtrFixed, CurLoc); - - } else { - - LoadInst *MapPtr = IRB.CreateLoad(AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - MapPtrIdx = IRB.CreateGEP(MapPtr, CurLoc); - - } - - /* Update bitmap */ - - if (use_threadsafe_counters) { - - IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One, -#if LLVM_VERSION_MAJOR >= 13 - llvm::MaybeAlign(1), -#endif - llvm::AtomicOrdering::Monotonic); - - } else { - - 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++; - - } - - } while (i > 0); - - } - - } - - if (dFile.is_open()) dFile.close(); - - // 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 %d 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 + 63) >> 6) << 6); - - 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; - - // 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(); - count++; - - } - - if (!be_quiet) - printf("AUTODICTIONARY: %zu string%s found\n", count, - count == 1 ? "" : "s"); - - if (count) { - - auto ptrhld = std::unique_ptr<char[]>(new char[memlen + count]); - - count = 0; - - size_t offset = 0; - for (auto token : dictionary) { - - if (offset + token.length() < 0xfffff0 && count < MAX_AUTO_EXTRAS) { - - ptrhld.get()[offset++] = (uint8_t)token.length(); - memcpy(ptrhld.get() + 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>(ptrhld.get(), offset))), - "__afl_internal_dictionary"); - AFLInternalDictionary->setInitializer(ConstantDataArray::get( - C, *(new ArrayRef<char>(ptrhld.get(), 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)); - - } - - } - - } - - /* 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 %d locations with no collisions (on average %llu " - "collisions would be in afl-gcc/vanilla AFL) (%s mode).", - inst_blocks, calculateCollisions(inst_blocks), modeline); - - } - - } - - return true; - -} - -char AFLLTOPass::ID = 0; - -static void registerAFLLTOPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - - PM.add(new AFLLTOPass()); - -} - -static RegisterPass<AFLLTOPass> X("afl-lto", "afl++ LTO instrumentation pass", - false, false); - -static RegisterStandardPasses RegisterAFLLTOPass( - PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerAFLLTOPass); - diff --git a/instrumentation/afl-llvm-pass.so.cc b/instrumentation/afl-llvm-pass.so.cc index 21ce0cf9..9430644e 100644 --- a/instrumentation/afl-llvm-pass.so.cc +++ b/instrumentation/afl-llvm-pass.so.cc @@ -45,12 +45,21 @@ typedef long double max_align_t; #endif #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#if LLVM_VERSION_MAJOR >= 14 /* how about stable interfaces? */ + #include "llvm/Passes/OptimizationLevel.h" +#endif #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) @@ -68,17 +77,30 @@ using namespace llvm; namespace { +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +class AFLCoverage : public PassInfoMixin<AFLCoverage> { + + public: + AFLCoverage() { + +#else class AFLCoverage : public ModulePass { public: static char ID; AFLCoverage() : ModulePass(ID) { +#endif + initInstrumentList(); } +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); +#else bool runOnModule(Module &M) override; +#endif protected: uint32_t ngram_size = 0; @@ -92,7 +114,55 @@ class AFLCoverage : public ModulePass { } // namespace +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return {LLVM_PLUGIN_API_VERSION, "AFLCoverage", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if 1 + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(AFLCoverage()); + + }); + + /* TODO LTO registration */ + #else + using PipelineElement = typename PassBuilder::PipelineElement; + PB.registerPipelineParsingCallback([](StringRef Name, + ModulePassManager &MPM, + ArrayRef<PipelineElement>) { + + if (Name == "AFLCoverage") { + + MPM.addPass(AFLCoverage()); + return true; + + } else { + + return false; + + } + + }); + + #endif + + }}; + +} + +#else + char AFLCoverage::ID = 0; +#endif /* needed up to 3.9.0 */ #if LLVM_VERSION_MAJOR == 3 && \ @@ -118,8 +188,15 @@ uint64_t PowerOf2Ceil(unsigned in) { (LLVM_VERSION_MAJOR == 4 && LLVM_VERSION_PATCH >= 1) #define AFL_HAVE_VECTOR_INTRINSICS 1 #endif + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +PreservedAnalyses AFLCoverage::run(Module &M, ModuleAnalysisManager &MAM) { + +#else bool AFLCoverage::runOnModule(Module &M) { +#endif + LLVMContext &C = M.getContext(); IntegerType *Int8Ty = IntegerType::getInt8Ty(C); @@ -133,6 +210,10 @@ bool AFLCoverage::runOnModule(Module &M) { u32 rand_seed; unsigned int cur_loc = 0; +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + auto PA = PreservedAnalyses::all(); +#endif + /* Setup random() so we get Actually Random(TM) outputs from AFL_R() */ gettimeofday(&tv, &tz); rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); @@ -970,10 +1051,15 @@ bool AFLCoverage::runOnModule(Module &M) { } +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + return PA; +#else return true; +#endif } +#if LLVM_VERSION_MAJOR < 11 /* use old pass manager */ static void registerAFLPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -986,4 +1072,5 @@ static RegisterStandardPasses RegisterAFLPass( static RegisterStandardPasses RegisterAFLPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); +#endif diff --git a/instrumentation/cmplog-instructions-pass.cc b/instrumentation/cmplog-instructions-pass.cc index 80af05f0..f2f0de82 100644 --- a/instrumentation/cmplog-instructions-pass.cc +++ b/instrumentation/cmplog-instructions-pass.cc @@ -28,11 +28,16 @@ #include "llvm/Config/llvm-config.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#if LLVM_MAJOR >= 11 +// #include "llvm/Passes/PassPlugin.h" +// #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" +#endif #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Pass.h" #include "llvm/Analysis/ValueTracking.h" @@ -54,6 +59,15 @@ using namespace llvm; namespace { +#if LLVM_MAJOR >= 11 /* use new pass manager */ +class CmpLogInstructions : public PassInfoMixin<CmpLogInstructions> { + public: + CmpLogInstructions() { + + initInstrumentList(); + + } +#else class CmpLogInstructions : public ModulePass { public: @@ -63,7 +77,11 @@ class CmpLogInstructions : public ModulePass { initInstrumentList(); } +#endif +#if LLVM_MAJOR >= 11 /* use new pass manager */ + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); +#else bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR < 4 @@ -76,6 +94,7 @@ class CmpLogInstructions : public ModulePass { return "cmplog instructions"; } +#endif private: bool hookInstrs(Module &M); @@ -84,7 +103,9 @@ class CmpLogInstructions : public ModulePass { } // namespace +#if LLVM_MAJOR <= 10 /* use old pass manager */ char CmpLogInstructions::ID = 0; +#endif template <class Iterator> Iterator Unique(Iterator first, Iterator last) { @@ -339,7 +360,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } #if LLVM_MAJOR > 11 - vector_cnt = tt->getElementCount().getFixedValue(); + vector_cnt = tt->getElementCount().getKnownMinValue(); ty0 = tt->getElementType(); #endif @@ -380,7 +401,7 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } - vector_cnt = tt->getElementCount().getFixedValue(); + vector_cnt = tt->getElementCount().getKnownMinValue(); ty1 = ty0 = tt->getElementType(); #endif @@ -567,7 +588,12 @@ bool CmpLogInstructions::hookInstrs(Module &M) { } +#if LLVM_MAJOR >= 11 /* use new pass manager */ +PreservedAnalyses CmpLogInstructions::run(Module & M, + ModuleAnalysisManager &MAM) { +#else bool CmpLogInstructions::runOnModule(Module &M) { +#endif if (getenv("AFL_QUIET") == NULL) printf("Running cmplog-instructions-pass by andreafioraldi@gmail.com\n"); @@ -576,10 +602,15 @@ bool CmpLogInstructions::runOnModule(Module &M) { hookInstrs(M); verifyModule(M); +#if LLVM_MAJOR >= 11 /* use new pass manager */ + return PreservedAnalyses::all(); +#else return true; +#endif } +#if LLVM_MAJOR < 11 /* use old pass manager */ static void registerCmpLogInstructionsPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -599,4 +630,4 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO( PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCmpLogInstructionsPass); #endif - +#endif diff --git a/instrumentation/compare-transform-pass.so.cc b/instrumentation/compare-transform-pass.so.cc index a1239040..2eb3d77b 100644 --- a/instrumentation/compare-transform-pass.so.cc +++ b/instrumentation/compare-transform-pass.so.cc @@ -26,14 +26,23 @@ #include "llvm/ADT/Statistic.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" +#if LLVM_MAJOR >= 11 /* use new pass manager */ + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Pass.h" #include "llvm/Analysis/ValueTracking.h" +#if LLVM_VERSION_MAJOR >= 14 /* how about stable interfaces? */ + #include "llvm/Passes/OptimizationLevel.h" +#endif #if LLVM_VERSION_MAJOR > 3 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) @@ -52,28 +61,30 @@ using namespace llvm; namespace { +#if LLVM_MAJOR >= 11 /* use new pass manager */ +class CompareTransform : public PassInfoMixin<CompareTransform> { + + public: + CompareTransform() { + +#else class CompareTransform : public ModulePass { public: static char ID; CompareTransform() : ModulePass(ID) { +#endif + initInstrumentList(); } - bool runOnModule(Module &M) override; - -#if LLVM_VERSION_MAJOR < 4 - const char *getPassName() const override { - +#if LLVM_MAJOR >= 11 /* use new pass manager */ + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - StringRef getPassName() const override { - + bool runOnModule(Module &M) override; #endif - return "transforms compare functions"; - - } private: bool transformCmps(Module &M, const bool processStrcmp, @@ -85,7 +96,54 @@ class CompareTransform : public ModulePass { } // namespace +#if LLVM_MAJOR >= 11 /* use new pass manager */ +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return {LLVM_PLUGIN_API_VERSION, "comparetransform", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if 1 + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(CompareTransform()); + + }); + + /* TODO LTO registration */ + #else + using PipelineElement = typename PassBuilder::PipelineElement; + PB.registerPipelineParsingCallback([](StringRef Name, + ModulePassManager &MPM, + ArrayRef<PipelineElement>) { + + if (Name == "comparetransform") { + + MPM.addPass(CompareTransform()); + return true; + + } else { + + return false; + + } + + }); + + #endif + + }}; + +} + +#else char CompareTransform::ID = 0; +#endif bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, const bool processMemcmp, @@ -384,6 +442,7 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, bool isSizedcmp = false; bool isCaseInsensitive = false; Function * Callee = callInst->getCalledFunction(); + if (Callee) { isMemcmp = Callee->getName().compare("memcmp") == 0; @@ -445,17 +504,22 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, } - // add null termination character implicit in c strings - if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) { + // the following is in general OK, but strncmp is sometimes used in binary + // data structures and this can result in crashes :( so it is commented out + /* + // add null termination character implicit in c strings + if (!isMemcmp && TmpConstStr[TmpConstStr.length() - 1]) { - TmpConstStr.append("\0", 1); + 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.find('\0') != std::string::npos) { TmpConstStr.assign(TmpConstStr, 0, TmpConstStr.find('\0') + 1); @@ -592,8 +656,14 @@ bool CompareTransform::transformCmps(Module &M, const bool processStrcmp, } +#if LLVM_MAJOR >= 11 /* use new pass manager */ +PreservedAnalyses CompareTransform::run(Module &M, ModuleAnalysisManager &MAM) { + +#else bool CompareTransform::runOnModule(Module &M) { +#endif + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) printf( "Running compare-transform-pass by laf.intel@gmail.com, extended by " @@ -601,13 +671,28 @@ bool CompareTransform::runOnModule(Module &M) { else be_quiet = 1; +#if LLVM_MAJOR >= 11 /* use new pass manager */ + auto PA = PreservedAnalyses::all(); +#endif + transformCmps(M, true, true, true, true, true); verifyModule(M); +#if LLVM_MAJOR >= 11 /* use new pass manager */ + /* if (modified) { + + PA.abandon<XX_Manager>(); + + }*/ + + return PA; +#else return true; +#endif } +#if LLVM_MAJOR < 11 /* use old pass manager */ static void registerCompTransPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -622,8 +707,9 @@ static RegisterStandardPasses RegisterCompTransPass( static RegisterStandardPasses RegisterCompTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass); -#if LLVM_VERSION_MAJOR >= 11 + #if LLVM_VERSION_MAJOR >= 11 static RegisterStandardPasses RegisterCompTransPassLTO( PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass); + #endif #endif diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index 7c652ca2..8637398f 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -1,6 +1,7 @@ /* * Copyright 2016 laf-intel * extended for floating point by Heiko Eißfeldt + * adapted to new pass manager by Heiko Eißfeldt * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,10 +29,20 @@ #include "llvm/Pass.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#if LLVM_MAJOR >= 11 + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/IR/Module.h" +#if LLVM_VERSION_MAJOR >= 14 /* how about stable interfaces? */ + #include "llvm/Passes/OptimizationLevel.h" +#endif #include "llvm/IR/IRBuilder.h" #if LLVM_VERSION_MAJOR > 3 || \ @@ -53,27 +64,31 @@ using namespace llvm; namespace { +#if LLVM_MAJOR >= 11 +class SplitComparesTransform : public PassInfoMixin<SplitComparesTransform> { + + public: + // static char ID; + SplitComparesTransform() : enableFPSplit(0) { + +#else class SplitComparesTransform : public ModulePass { public: static char ID; SplitComparesTransform() : ModulePass(ID), enableFPSplit(0) { +#endif + initInstrumentList(); } - bool runOnModule(Module &M) override; -#if LLVM_VERSION_MAJOR >= 4 - StringRef getPassName() const override { - +#if LLVM_MAJOR >= 11 + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - const char *getPassName() const override { - + bool runOnModule(Module &M) override; #endif - return "AFL_SplitComparesTransform"; - - } private: int enableFPSplit; @@ -162,7 +177,54 @@ class SplitComparesTransform : public ModulePass { } // namespace +#if LLVM_MAJOR >= 11 +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return {LLVM_PLUGIN_API_VERSION, "splitcompares", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if 1 + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(SplitComparesTransform()); + + }); + + /* TODO LTO registration */ + #else + using PipelineElement = typename PassBuilder::PipelineElement; + PB.registerPipelineParsingCallback([](StringRef Name, + ModulePassManager &MPM, + ArrayRef<PipelineElement>) { + + if (Name == "splitcompares") { + + MPM.addPass(SplitComparesTransform()); + return true; + + } else { + + return false; + + } + + }); + + #endif + + }}; + +} + +#else char SplitComparesTransform::ID = 0; +#endif /// This function splits FCMP instructions with xGE or xLE predicates into two /// FCMP instructions with predicate xGT or xLT and EQ @@ -675,7 +737,7 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, ReplaceInstWithInst(cmp_inst->getParent()->getInstList(), ii, PN); // We split the comparison into low and high. If this isn't our target - // bitwidth we recursivly split the low and high parts again until we have + // bitwidth we recursively split the low and high parts again until we have // target bitwidth. if ((bitw / 2) > target_bitwidth) { @@ -1316,8 +1378,15 @@ size_t SplitComparesTransform::splitFPCompares(Module &M) { } +#if LLVM_MAJOR >= 11 +PreservedAnalyses SplitComparesTransform::run(Module & M, + ModuleAnalysisManager &MAM) { + +#else bool SplitComparesTransform::runOnModule(Module &M) { +#endif + char *bitw_env = getenv("AFL_LLVM_LAF_SPLIT_COMPARES_BITW"); if (!bitw_env) bitw_env = getenv("LAF_SPLIT_COMPARES_BITW"); if (bitw_env) { target_bitwidth = atoi(bitw_env); } @@ -1327,7 +1396,7 @@ 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 " + errs() << "Split-compare-newpass by laf.intel@gmail.com, extended by " "heiko@hexco.de (splitting icmp to " << target_bitwidth << " bit)\n"; @@ -1339,6 +1408,10 @@ bool SplitComparesTransform::runOnModule(Module &M) { } +#if LLVM_MAJOR >= 11 + auto PA = PreservedAnalyses::all(); +#endif + if (enableFPSplit) { count = splitFPCompares(M); @@ -1371,7 +1444,16 @@ bool SplitComparesTransform::runOnModule(Module &M) { auto op0 = CI->getOperand(0); auto op1 = CI->getOperand(1); - if (!op0 || !op1) { return false; } + if (!op0 || !op1) { + +#if LLVM_MAJOR >= 11 + return PA; +#else + return false; +#endif + + } + auto iTy1 = dyn_cast<IntegerType>(op0->getType()); if (iTy1 && isa<IntegerType>(op1->getType())) { @@ -1420,10 +1502,29 @@ bool SplitComparesTransform::runOnModule(Module &M) { } + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || + getenv("AFL_DEBUG") != NULL) { + + errs() << count << " comparisons found\n"; + + } + +#if LLVM_MAJOR >= 11 + /* if (modified) { + + PA.abandon<XX_Manager>(); + + }*/ + + return PA; +#else return true; +#endif } +#if LLVM_MAJOR < 11 /* use old pass manager */ + static void registerSplitComparesPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -1437,14 +1538,15 @@ static RegisterStandardPasses RegisterSplitComparesPass( static RegisterStandardPasses RegisterSplitComparesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass); -#if LLVM_VERSION_MAJOR >= 11 + #if LLVM_VERSION_MAJOR >= 11 static RegisterStandardPasses RegisterSplitComparesTransPassLTO( PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerSplitComparesPass); -#endif + #endif static RegisterPass<SplitComparesTransform> X("splitcompares", "AFL++ split compares", true /* Only looks at CFG */, true /* Analysis Pass */); +#endif diff --git a/instrumentation/split-switches-pass.so.cc b/instrumentation/split-switches-pass.so.cc index 1e32a31d..e0a96ac9 100644 --- a/instrumentation/split-switches-pass.so.cc +++ b/instrumentation/split-switches-pass.so.cc @@ -27,14 +27,23 @@ #include "llvm/ADT/Statistic.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/LegacyPassManager.h" +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Pass.h" #include "llvm/Analysis/ValueTracking.h" +#if LLVM_VERSION_MAJOR >= 14 /* how about stable interfaces? */ + #include "llvm/Passes/OptimizationLevel.h" +#endif #include "llvm/IR/IRBuilder.h" #if LLVM_VERSION_MAJOR > 3 || \ @@ -54,29 +63,42 @@ using namespace llvm; namespace { +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +class SplitSwitchesTransform : public PassInfoMixin<SplitSwitchesTransform> { + + public: + SplitSwitchesTransform() { + +#else class SplitSwitchesTransform : public ModulePass { public: static char ID; SplitSwitchesTransform() : ModulePass(ID) { +#endif initInstrumentList(); } - bool runOnModule(Module &M) override; +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); +#else + bool runOnModule(Module &M) override; -#if LLVM_VERSION_MAJOR >= 4 + #if LLVM_VERSION_MAJOR >= 4 StringRef getPassName() const override { -#else + #else const char *getPassName() const override { -#endif + #endif return "splits switch constructs"; } +#endif + struct CaseExpr { ConstantInt *Val; @@ -103,7 +125,54 @@ class SplitSwitchesTransform : public ModulePass { } // namespace +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + + return {LLVM_PLUGIN_API_VERSION, "splitswitches", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if 1 + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + + MPM.addPass(SplitSwitchesTransform()); + + }); + + /* TODO LTO registration */ + #else + using PipelineElement = typename PassBuilder::PipelineElement; + PB.registerPipelineParsingCallback([](StringRef Name, + ModulePassManager &MPM, + ArrayRef<PipelineElement>) { + + if (Name == "splitswitches") { + + MPM.addPass(SplitSwitchesTransform()); + return true; + + } else { + + return false; + + } + + }); + + #endif + + }}; + +} + +#else char SplitSwitchesTransform::ID = 0; +#endif /* switchConvert - Transform simple list of Cases into list of CaseRange's */ BasicBlock *SplitSwitchesTransform::switchConvert( @@ -415,19 +484,42 @@ bool SplitSwitchesTransform::splitSwitches(Module &M) { } +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ +PreservedAnalyses SplitSwitchesTransform::run(Module & M, + ModuleAnalysisManager &MAM) { + +#else bool SplitSwitchesTransform::runOnModule(Module &M) { +#endif + if ((isatty(2) && getenv("AFL_QUIET") == NULL) || getenv("AFL_DEBUG") != NULL) printf("Running split-switches-pass by laf.intel@gmail.com\n"); else be_quiet = 1; + +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + auto PA = PreservedAnalyses::all(); +#endif + splitSwitches(M); verifyModule(M); +#if LLVM_VERSION_MAJOR >= 11 /* use new pass manager */ + /* if (modified) { + + PA.abandon<XX_Manager>(); + + }*/ + + return PA; +#else return true; +#endif } +#if LLVM_VERSION_MAJOR < 11 /* use old pass manager */ static void registerSplitSwitchesTransPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -442,9 +534,10 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass( static RegisterStandardPasses RegisterSplitSwitchesTransPass0( PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass); -#if LLVM_VERSION_MAJOR >= 11 + #if LLVM_VERSION_MAJOR >= 11 static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO( PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerSplitSwitchesTransPass); + #endif #endif diff --git a/src/afl-cc.c b/src/afl-cc.c index 8ff241ba..c70f193c 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -462,11 +462,17 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { +#if LLVM_MAJOR >= 11 /* use new pass manager */ + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%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); +#endif } @@ -482,11 +488,17 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { +#if LLVM_MAJOR >= 11 /* use new pass manager */ + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = alloc_printf( + "-fpass-plugin=%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); +#endif } @@ -502,11 +514,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { +#if LLVM_MAJOR >= 11 + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path); +// cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; +#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); +#endif } @@ -536,11 +555,17 @@ static void edit_params(u32 argc, char **argv, char **envp) { alloc_printf("%s/cmplog-switches-pass.so", obj_path); // reuse split switches from laf +#if LLVM_MAJOR >= 11 + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%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); +#endif } @@ -548,10 +573,6 @@ static void edit_params(u32 argc, char **argv, char **envp) { } -#if LLVM_MAJOR >= 13 - // fuck you llvm 13 - cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; -#endif if (lto_mode && !have_c) { @@ -566,15 +587,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { free(ld_path); cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - - if (instrument_mode == INSTRUMENT_CFG || - instrument_mode == INSTRUMENT_PCGUARD) - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); - else - - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path); + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); cc_params[cc_par_cnt++] = lto_flag; } else { @@ -598,6 +612,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { + cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; cc_params[cc_par_cnt++] = "-Xclang"; @@ -630,10 +645,16 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { +#if LLVM_MAJOR >= 11 + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%s/afl-llvm-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/afl-llvm-pass.so", obj_path); +#endif } @@ -650,11 +671,17 @@ static void edit_params(u32 argc, char **argv, char **envp) { } else { +#if LLVM_MAJOR >= 11 + cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; + cc_params[cc_par_cnt++] = + alloc_printf("-fpass-plugin=%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-instructions-pass.so", obj_path); +#endif cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; |