From 6d364dd2cb0ac31797b52e590b57bf9c10cc2302 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 01:13:51 +0200 Subject: add sancov-like allow/denylist instrument feature --- llvm_mode/README.instrument_file.md | 81 ----- llvm_mode/README.instrument_list.md | 86 +++++ llvm_mode/README.lto.md | 5 +- llvm_mode/README.md | 2 +- llvm_mode/afl-clang-fast.c | 32 +- llvm_mode/afl-llvm-common.cc | 485 +++++++++++++++++++++++----- llvm_mode/afl-llvm-lto-instrumentlist.so.cc | 156 +-------- 7 files changed, 539 insertions(+), 308 deletions(-) delete mode 100644 llvm_mode/README.instrument_file.md create mode 100644 llvm_mode/README.instrument_list.md (limited to 'llvm_mode') diff --git a/llvm_mode/README.instrument_file.md b/llvm_mode/README.instrument_file.md deleted file mode 100644 index 46e45ba2..00000000 --- a/llvm_mode/README.instrument_file.md +++ /dev/null @@ -1,81 +0,0 @@ -# Using afl++ with partial instrumentation - - This file describes how you can selectively instrument only the source files - that are interesting to you using the LLVM instrumentation provided by - afl++ - - Originally developed by Christian Holler (:decoder) . - -## 1) Description and purpose - -When building and testing complex programs where only a part of the program is -the fuzzing target, it often helps to only instrument the necessary parts of -the program, leaving the rest uninstrumented. This helps to focus the fuzzer -on the important parts of the program, avoiding undesired noise and -disturbance by uninteresting code being exercised. - -For this purpose, I have added a "partial instrumentation" support to the LLVM -mode of AFLFuzz that allows you to specify on a source file level which files -should be compiled with or without instrumentation. - -Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: -https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation - -## 2) Building the LLVM module - -The new code is part of the existing afl++ LLVM module in the llvm_mode/ -subdirectory. There is nothing specifically to do :) - - -## 3) How to use the partial instrumentation mode - -In order to build with partial instrumentation, you need to build with -afl-clang-fast and afl-clang-fast++ respectively. The only required change is -that you need to set the environment variable AFL_LLVM_INSTRUMENT_FILE when calling -the compiler. - -The environment variable must point to a file containing all the filenames -that should be instrumented. For matching, the filename that is being compiled -must end in the filename entry contained in this the instrument file list (to avoid breaking -the matching when absolute paths are used during compilation). - -For example if your source tree looks like this: - -``` -project/ -project/feature_a/a1.cpp -project/feature_a/a2.cpp -project/feature_b/b1.cpp -project/feature_b/b2.cpp -``` - -and you only want to test feature_a, then create a the instrument file list file containing: - -``` -feature_a/a1.cpp -feature_a/a2.cpp -``` - -However if the instrument file list file contains only this, it works as well: - -``` -a1.cpp -a2.cpp -``` - -but it might lead to files being unwantedly instrumented if the same filename -exists somewhere else in the project directories. - -The created the instrument file list file is then set to AFL_LLVM_INSTRUMENT_FILE when you compile -your program. For each file that didn't match the the instrument file list, the compiler will -issue a warning at the end stating that no blocks were instrumented. If you -didn't intend to instrument that file, then you can safely ignore that warning. - -For old LLVM versions this feature might require to be compiled with debug -information (-g), however at least from llvm version 6.0 onwards this is not -required anymore (and might hurt performance and crash detection, so better not -use -g). - -## 4) UNIX-style filename pattern matching -You can add UNIX-style pattern matching in the the instrument file list entries. See `man -fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md new file mode 100644 index 00000000..b0e0cc1e --- /dev/null +++ b/llvm_mode/README.instrument_list.md @@ -0,0 +1,86 @@ +# Using afl++ with partial instrumentation + + This file describes how you can selectively instrument only the source files + or functions that are interesting to you using the LLVM instrumentation + provided by afl++ + +## 1) Description and purpose + +When building and testing complex programs where only a part of the program is +the fuzzing target, it often helps to only instrument the necessary parts of +the program, leaving the rest uninstrumented. This helps to focus the fuzzer +on the important parts of the program, avoiding undesired noise and +disturbance by uninteresting code being exercised. + +For this purpose, a "partial instrumentation" support en par with llvm sancov +is provided by afl++ that allows you to specify on a source file and function +level which should be compiled with or without instrumentation. + +Note: When using PCGUARD mode - and have llvm 12+ - you can use this instead: +https://clang.llvm.org/docs/SanitizerCoverage.html#partially-disabling-instrumentation + +the llvm sancov list format is fully supported by afl++, however afl++ has +more flexbility. + +## 2) Building the LLVM module + +The new code is part of the existing afl++ LLVM module in the llvm_mode/ +subdirectory. There is nothing specifically to do :) + +## 3) How to use the partial instrumentation mode + +In order to build with partial instrumentation, you need to build with +afl-clang-fast/afl-clang-fast++ or afl-clang-lto/afl-clang-lto++. +The only required change is that you need to set either the environment variable +AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST set with a filename. + +That file then contains the filenames or functions that should be instrumented +(AFL_LLVM_ALLOWLIST) or should specifically NOT instrumentd (AFL_LLVM_DENYLIST). + +For matching, the function/filename that is being compiled must end in the +function/filename entry contained in this the instrument file list (to avoid +breaking the matching when absolute paths are used during compilation). + +**NOTE:** In optimization functions might be inlined and then not match! + +For example if your source tree looks like this: +``` +project/ +project/feature_a/a1.cpp +project/feature_a/a2.cpp +project/feature_b/b1.cpp +project/feature_b/b2.cpp +``` + +and you only want to test feature_a, then create a the instrument file list file containing: +``` +feature_a/a1.cpp +feature_a/a2.cpp +``` + +However if the instrument file list file contains only this, it works as well: +``` +a1.cpp +a2.cpp +``` +but it might lead to files being unwantedly instrumented if the same filename +exists somewhere else in the project directories. + +You can also specify function names. Note that for C++ the function names +must be mangled to match! + +afl++ is intelligent to identify if an entry is a filename or a function. +However if you want to be sure (and compliant to the sancov allow/blocklist +format), you can file entries like this: +``` +src: *malloc.c +``` +and function entries like this: +``` +fun: MallocFoo +``` +Note that whitespace is ignored and comments (`# foo`) supported. + +## 4) UNIX-style pattern matching +You can add UNIX-style pattern matching in the the instrument file list entries. +See `man fnmatch` for the syntax. We do not set any of the `fnmatch` flags. diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index e521ac82..4d643324 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -108,15 +108,12 @@ make install Just use afl-clang-lto like you did with afl-clang-fast or afl-gcc. -Also the instrument file listing (AFL_LLVM_INSTRUMENT_FILE -> [README.instrument_file.md](README.instrument_file.md)) and +Also the instrument file listing (AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST -> [README.instrument_list.md](README.instrument_list.md)) and laf-intel/compcov (AFL_LLVM_LAF_* -> [README.laf-intel.md](README.laf-intel.md)) work. -InsTrim (control flow graph instrumentation) is supported and recommended! - (set `AFL_LLVM_INSTRUMENT=CFG`) Example: ``` CC=afl-clang-lto CXX=afl-clang-lto++ RANLIB=llvm-ranlib AR=llvm-ar ./configure -export AFL_LLVM_INSTRUMENT=CFG make ``` diff --git a/llvm_mode/README.md b/llvm_mode/README.md index 22088dfd..f23d7150 100644 --- a/llvm_mode/README.md +++ b/llvm_mode/README.md @@ -109,7 +109,7 @@ Several options are present to make llvm_mode faster or help it rearrange the code to make afl-fuzz path discovery easier. If you need just to instrument specific parts of the code, you can the instrument file list -which C/C++ files to actually instrument. See [README.instrument_file](README.instrument_file.md) +which C/C++ files to actually instrument. See [README.instrument_list](README.instrument_list.md) For splitting memcmp, strncmp, etc. please see [README.laf-intel](README.laf-intel.md) diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index ef99e3f3..f75adf1e 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -229,7 +229,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode) { if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || - getenv("AFL_LLVM_WHITELIST")) { + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { cc_params[cc_par_cnt++] = "-Xclang"; cc_params[cc_par_cnt++] = "-load"; @@ -637,9 +638,13 @@ int main(int argc, char **argv, char **envp) { } - if ((getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) && + if ((getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) && getenv("AFL_DONT_OPTIMIZE")) - FATAL("AFL_LLVM_INSTRUMENT_FILE and AFL_DONT_OPTIMIZE cannot be combined"); + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || getenv("INSTRIM_LIB")) { @@ -787,15 +792,17 @@ int main(int argc, char **argv, char **envp) { #if LLVM_VERSION_MAJOR <= 6 instrument_mode = INSTRUMENT_AFL; #else - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST")) { + if (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST")) { instrument_mode = INSTRUMENT_AFL; WARNF( "switching to classic instrumentation because " - "AFL_LLVM_INSTRUMENT_FILE does not work with PCGUARD. Use " - "-fsanitize-coverage-allowlist=allowlist.txt if you want to use " - "PCGUARD. Requires llvm 12+. See " - "https://clang.llvm.org/docs/" + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD. Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt if you want to use " + "PCGUARD. Requires llvm 12+. See https://clang.llvm.org/docs/ " "SanitizerCoverage.html#partially-disabling-instrumentation"); } else @@ -846,11 +853,14 @@ int main(int argc, char **argv, char **envp) { "together"); if (instrument_mode == INSTRUMENT_PCGUARD && - (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST"))) + (getenv("AFL_LLVM_INSTRUMENT_FILE") != NULL || + getenv("AFL_LLVM_WHITELIST") || getenv("AFL_LLVM_ALLOWLIST") || + getenv("AFL_LLVM_DENYLIST") || getenv("AFL_LLVM_BLOCKLIST"))) FATAL( "Instrumentation type PCGUARD does not support " - "AFL_LLVM_INSTRUMENT_FILE! Use " - "-fsanitize-coverage-allowlist=allowlist.txt instead (requires llvm " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use " + "-fsanitize-coverage-allowlist=allowlist.txt or " + "-fsanitize-coverage-blocklist=denylist.txt instead (requires llvm " "12+), see " "https://clang.llvm.org/docs/" "SanitizerCoverage.html#partially-disabling-instrumentation"); diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 9a884ded..0b89c3b4 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -20,7 +20,10 @@ using namespace llvm; -static std::list myInstrumentList; +static std::list allowListFiles; +static std::list allowListFunctions; +static std::list denyListFiles; +static std::list denyListFunctions; char *getBBName(const llvm::BasicBlock *BB) { @@ -87,30 +90,166 @@ bool isIgnoreFunction(const llvm::Function *F) { void initInstrumentList() { - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); + char *allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - if (instrumentListFilename) { + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_LLVM_ALLOWLIST or AFL_LLVM_DENYLIST " + "but not both!"); + + if (allowlist) { std::string line; std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_ALLOWLIST"); getline(fileStream, line); + while (fileStream) { - myInstrumentList.push_back(line); - getline(fileStream, line); + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + getline(fileStream, line); + + } } + if (debug) + SAYF(cMGN "[D] " cRST + "loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + } - if (debug) - SAYF(cMGN "[D] " cRST "loaded instrument list with %zu entries\n", - myInstrumentList.size()); + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_LLVM_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_LLVM_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + getline(fileStream, line); + + } + + } + + if (debug) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } } @@ -121,42 +260,173 @@ bool isInInstrumentList(llvm::Function *F) { if (!F->size() || isIgnoreFunction(F)) return false; // if we do not have a the instrument file list return true - if (myInstrumentList.empty()) return true; + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + if (!allowListFunctions.empty()) { + + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ - // let's try to get the filename for the function - auto bb = &F->getEntryBlock(); - BasicBlock::iterator IP = bb->getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - DebugLoc Loc = IP->getDebugLoc(); + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allow function list, " + "instrumenting ... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); #if LLVM_VERSION_MAJOR >= 4 || \ (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) - if (Loc) { + if (Loc) { + + DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); + + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); + if (instFilename.str().empty()) { - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { + + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); + + } + + } - if (instFilename.str().empty()) { + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return true; + + } + + } + + } + + } + + } + + } + +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return true; + + } + + } + + } + + } } } - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { +#endif + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return false; + + } + + return false; + + } + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + std::string instFunction = F->getName().str(); + + for (std::list::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { /* We don't check for filename equality here because * filenames might actually be full paths. Instead we @@ -164,16 +434,16 @@ bool isInInstrumentList(llvm::Function *F) { * specified in the list. We also allow UNIX-style pattern * matching */ - if (instFilename.str().length() >= it->length()) { + if (instFunction.length() >= it->length()) { - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { if (debug) SAYF(cMGN "[D] " cRST - "Function %s is in the list (%s), instrumenting ... \n", - F->getName().str().c_str(), instFilename.str().c_str()); - return true; + "Function %s is in the deny function list, " + "not instrumenting ... \n", + instFunction.c_str()); + return false; } @@ -183,35 +453,64 @@ bool isInInstrumentList(llvm::Function *F) { } - } + if (!denyListFiles.empty()) { -#else - if (!Loc.isUnknown()) { + // let's try to get the filename for the function + auto bb = &F->getEntryBlock(); + BasicBlock::iterator IP = bb->getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + DebugLoc Loc = IP->getDebugLoc(); - DILocation cDILoc(Loc.getAsMDNode(F->getContext())); +#if LLVM_VERSION_MAJOR >= 4 || \ + (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR >= 7) + if (Loc) { - unsigned int instLine = cDILoc.getLineNumber(); - StringRef instFilename = cDILoc.getFilename(); + DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - (void)instLine; - /* Continue only if we know where we actually are */ - if (!instFilename.str().empty()) { + unsigned int instLine = cDILoc->getLine(); + StringRef instFilename = cDILoc->getFilename(); - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { + if (instFilename.str().empty()) { - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ + /* If the original location is empty, try using the inlined location + */ + DILocation *oDILoc = cDILoc->getInlinedAt(); + if (oDILoc) { - if (instFilename.str().length() >= it->length()) { + instFilename = oDILoc->getFilename(); + instLine = oDILoc->getLine(); - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { + } - return true; + } + + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + if (debug) + SAYF(cMGN "[D] " cRST + "Function %s is in the denylist (%s), not " + "instrumenting ... \n", + F->getName().str().c_str(), instFilename.str().c_str()); + return false; + + } + + } } @@ -221,23 +520,65 @@ bool isInInstrumentList(llvm::Function *F) { } - } +#else + if (!Loc.isUnknown()) { + + DILocation cDILoc(Loc.getAsMDNode(F->getContext())); + + unsigned int instLine = cDILoc.getLineNumber(); + StringRef instFilename = cDILoc.getFilename(); + + (void)instLine; + /* Continue only if we know where we actually are */ + if (!instFilename.str().empty()) { + + for (std::list::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFilename.str().length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == + 0) { + + return false; + + } + + } + + } + + } + + } + + } #endif - else { - - // we could not find out the location. in this case we say it is not - // in the the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - F->getName().str().c_str()); - return false; + else { + + // we could not find out the location. in this case we say it is not + // in the the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + F->getName().str().c_str()); + return true; + + } + + return true; } - return false; + return true; // not reached } diff --git a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc index ab7c0c58..a7331444 100644 --- a/llvm_mode/afl-llvm-lto-instrumentlist.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentlist.so.cc @@ -59,39 +59,9 @@ class AFLcheckIfInstrument : public ModulePass { static char ID; AFLcheckIfInstrument() : ModulePass(ID) { - int entries = 0; - if (getenv("AFL_DEBUG")) debug = 1; - char *instrumentListFilename = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!instrumentListFilename) - instrumentListFilename = getenv("AFL_LLVM_WHITELIST"); - if (instrumentListFilename) { - - std::string line; - std::ifstream fileStream; - fileStream.open(instrumentListFilename); - if (!fileStream) - report_fatal_error("Unable to open AFL_LLVM_INSTRUMENT_FILE"); - getline(fileStream, line); - while (fileStream) { - - myInstrumentList.push_back(line); - getline(fileStream, line); - entries++; - - } - - } else - - PFATAL( - "afl-llvm-lto-instrumentlist.so loaded without " - "AFL_LLVM_INSTRUMENT_FILE?!"); - - if (debug) - SAYF(cMGN "[D] " cRST - "loaded the instrument file list %s with %d entries\n", - instrumentListFilename, entries); + initInstrumentList(); } @@ -129,120 +99,28 @@ bool AFLcheckIfInstrument::runOnModule(Module &M) { for (auto &F : M) { if (F.size() < 1) continue; - // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - if (isIgnoreFunction(&F)) continue; - - BasicBlock::iterator IP = F.getEntryBlock().getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - if (!myInstrumentList.empty()) { - - bool instrumentFunction = false; - - /* Get the current location using debug information. - * For now, just instrument the block if we are not able - * to determine our location. */ - DebugLoc Loc = IP->getDebugLoc(); - if (Loc) { - - DILocation *cDILoc = dyn_cast(Loc.getAsMDNode()); - - unsigned int instLine = cDILoc->getLine(); - StringRef instFilename = cDILoc->getFilename(); - - if (instFilename.str().empty()) { - - /* If the original location is empty, try using the inlined location - */ - DILocation *oDILoc = cDILoc->getInlinedAt(); - if (oDILoc) { - - instFilename = oDILoc->getFilename(); - instLine = oDILoc->getLine(); - - } - - if (instFilename.str().empty()) { - if (!be_quiet) - WARNF( - "Function %s has no source file name information and will " - "not be instrumented.", - F.getName().str().c_str()); - continue; - - } - - } - - //(void)instLine; - - fprintf(stderr, "xxx %s %s\n", F.getName().str().c_str(), - instFilename.str().c_str()); - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in file %s\n", - F.getName().str().c_str(), instFilename.str().c_str()); - - for (std::list::iterator it = myInstrumentList.begin(); - it != myInstrumentList.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. */ - if (instFilename.str().length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFilename.str().c_str(), 0) == - 0) { - - instrumentFunction = true; - break; - - } - - } - - } - - } else { - - if (!be_quiet) - WARNF( - "No debug information found for function %s, recompile with -g " - "-O[1-3]", - F.getName().str().c_str()); - continue; - - } - - /* Either we couldn't figure out our location or the location is - * not the instrument file listed, so we skip instrumentation. - * We do this by renaming the function. */ - if (instrumentFunction == true) { - - if (debug) - SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", - F.getName().str().c_str()); - - } else { - - if (debug) - SAYF(cMGN "[D] " cRST - "function %s is NOT in the instrument file list\n", - F.getName().str().c_str()); + // fprintf(stderr, "F:%s\n", F.getName().str().c_str()); - auto & Ctx = F.getContext(); - AttributeList Attrs = F.getAttributes(); - AttrBuilder NewAttrs; - NewAttrs.addAttribute("skipinstrument"); - F.setAttributes( - Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); + if (isInInstrumentList(&F)) { - } + if (debug) + SAYF(cMGN "[D] " cRST "function %s is in the instrument file list\n", + F.getName().str().c_str()); } else { - PFATAL("InstrumentList is empty"); + if (debug) + SAYF(cMGN "[D] " cRST + "function %s is NOT in the instrument file list\n", + F.getName().str().c_str()); + + auto & Ctx = F.getContext(); + AttributeList Attrs = F.getAttributes(); + AttrBuilder NewAttrs; + NewAttrs.addAttribute("skipinstrument"); + F.setAttributes( + Attrs.addAttributes(Ctx, AttributeList::FunctionIndex, NewAttrs)); } -- cgit 1.4.1 From 8fc727e597f26161fde7f38af8b805a39497da52 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 5 Aug 2020 10:01:58 +0200 Subject: port patch from https://github.com/google/AFL/pull/112 --- llvm_mode/afl-clang-fast.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index f75adf1e..750a6fbd 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,7 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, + shared_linking = 0, preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -399,6 +400,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; + + if (!strcmp(cur, "-E")) preprocessor_only = 1; + if (!strcmp(cur, "-shared")) shared_linking = 1; cc_params[cc_par_cnt++] = cur; @@ -563,6 +567,22 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "none"; } + + if (preprocessor_only || shared_linking) { + /* In the preprocessor_only case (-E), we are not actually compiling at + all but requesting the compiler to output preprocessed sources only. + We must not add the runtime in this case because the compiler will + simply output its binary content back on stdout, breaking any build + systems that rely on a separate source preprocessing step. + The shared_linking case (-shared) is more complex. This flag should + only be passed when linking a shared object. When loading such a shared + object into a binary that has also been built with AFL, two AFL runtimes + will exist side-by-side. This is only a problem in the dynamic loading + case because for static linking, the compiler can de-duplicate the + runtime. We must hence avoid attaching the runtime to shared objects. */ + cc_params[cc_par_cnt] = NULL; + return; + } #ifndef __ANDROID__ switch (bit_mode) { -- cgit 1.4.1 From 4a859aff70cb4334c78bd6a13feb820bdad9d9d6 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 10:25:53 +0200 Subject: travis fixes --- GNUmakefile | 6 +++--- llvm_mode/afl-llvm-common.cc | 2 +- test/test-llvm.sh | 2 +- test/travis/bionic/Dockerfile | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'llvm_mode') diff --git a/GNUmakefile b/GNUmakefile index 510f4298..679ccc82 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -551,9 +551,9 @@ source-only: all -$(MAKE) -C gcc_plugin $(MAKE) -C libdislocator $(MAKE) -C libtokencap - #$(MAKE) -C examples/afl_network_proxy - #$(MAKE) -C examples/socket_fuzzing - #$(MAKE) -C examples/argv_fuzzing + @#$(MAKE) -C examples/afl_network_proxy + @#$(MAKE) -C examples/socket_fuzzing + @#$(MAKE) -C examples/argv_fuzzing %.8: % @echo .TH $* 8 $(BUILD_DATE) "afl++" > $@ diff --git a/llvm_mode/afl-llvm-common.cc b/llvm_mode/afl-llvm-common.cc index 0b89c3b4..0b50c547 100644 --- a/llvm_mode/afl-llvm-common.cc +++ b/llvm_mode/afl-llvm-common.cc @@ -60,7 +60,7 @@ bool isIgnoreFunction(const llvm::Function *F) { "asan.", "llvm.", "sancov.", - "__ubsan_handle_", + "__ubsan_", "ign.", "__afl_", "_fini", diff --git a/test/test-llvm.sh b/test/test-llvm.sh index 24fc6a34..02d23f9c 100755 --- a/test/test-llvm.sh +++ b/test/test-llvm.sh @@ -25,7 +25,7 @@ test -e ../afl-clang-fast -a -e ../split-switches-pass.so && { } || { $ECHO "$GREEN[+] llvm_mode instrumentation present and working correctly" TUPLES=`echo 0|../afl-showmap -m ${MEM_LIMIT} -o /dev/null -- ./test-instr.plain 2>&1 | grep Captur | awk '{print$3}'` - test "$TUPLES" -gt 3 -a "$TUPLES" -lt 7 && { + test "$TUPLES" -gt 3 -a "$TUPLES" -lt 8 && { $ECHO "$GREEN[+] llvm_mode run reported $TUPLES instrumented locations which is fine" } || { $ECHO "$RED[!] llvm_mode instrumentation produces weird numbers: $TUPLES" diff --git a/test/travis/bionic/Dockerfile b/test/travis/bionic/Dockerfile index d1b53e70..00ab96f9 100644 --- a/test/travis/bionic/Dockerfile +++ b/test/travis/bionic/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update && apt-get -y install \ ENV AFL_NO_UI=1 ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 +ENV LLVM_CONFIG=llvm-config-6.0 RUN cd / && \ git clone https://github.com/AFLplusplus/AFLplusplus && \ -- cgit 1.4.1 From bd36aac60a6d85fe83d072bf00a9ec0b2f3f135d Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 5 Aug 2020 10:27:48 +0200 Subject: remove shared_linking case --- llvm_mode/afl-clang-fast.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 750a6fbd..16f2c9c0 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -162,7 +162,7 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - shared_linking = 0, preprocessor_only = 0; + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -402,7 +402,6 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; if (!strcmp(cur, "-E")) preprocessor_only = 1; - if (!strcmp(cur, "-shared")) shared_linking = 1; cc_params[cc_par_cnt++] = cur; @@ -568,18 +567,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only || shared_linking) { + if (preprocessor_only) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will simply output its binary content back on stdout, breaking any build - systems that rely on a separate source preprocessing step. - The shared_linking case (-shared) is more complex. This flag should - only be passed when linking a shared object. When loading such a shared - object into a binary that has also been built with AFL, two AFL runtimes - will exist side-by-side. This is only a problem in the dynamic loading - case because for static linking, the compiler can de-duplicate the - runtime. We must hence avoid attaching the runtime to shared objects. */ + systems that rely on a separate source preprocessing step. */ cc_params[cc_par_cnt] = NULL; return; } -- cgit 1.4.1 From f30ca1476c2d4d08d46fe9657ad4aa1d828eb578 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 11:17:15 +0200 Subject: fix short write --- include/afl-fuzz.h | 4 ++-- llvm_mode/afl-clang-fast.c | 10 ++++++---- src/afl-fuzz-queue.c | 2 ++ src/afl-fuzz-run.c | 17 ++++++++++++++--- 4 files changed, 24 insertions(+), 9 deletions(-) (limited to 'llvm_mode') diff --git a/include/afl-fuzz.h b/include/afl-fuzz.h index 2324efa5..bb1bb314 100644 --- a/include/afl-fuzz.h +++ b/include/afl-fuzz.h @@ -986,7 +986,7 @@ uint64_t rand_next(afl_state_t *afl); static inline u32 rand_below(afl_state_t *afl, u32 limit) { - if (limit <= 1) return 0; + if (limit <= 1) return 0; /* The boundary not being necessarily a power of 2, we need to ensure the result uniformity. */ @@ -1008,7 +1008,7 @@ static inline u32 rand_below(afl_state_t *afl, u32 limit) { expand havoc mode */ static inline u32 rand_below_datalen(afl_state_t *afl, u32 limit) { - if (limit <= 1) return 0; + if (limit <= 1) return 0; switch (rand_below(afl, 3)) { diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 16f2c9c0..3038df30 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,8 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - preprocessor_only = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -400,7 +400,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; - + if (!strcmp(cur, "-E")) preprocessor_only = 1; cc_params[cc_par_cnt++] = cur; @@ -566,8 +566,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "none"; } - + if (preprocessor_only) { + /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will @@ -575,6 +576,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { systems that rely on a separate source preprocessing step. */ cc_params[cc_par_cnt] = NULL; return; + } #ifndef __ANDROID__ diff --git a/src/afl-fuzz-queue.c b/src/afl-fuzz-queue.c index 71874283..f35df914 100644 --- a/src/afl-fuzz-queue.c +++ b/src/afl-fuzz-queue.c @@ -112,8 +112,10 @@ static u8 check_if_text(struct queue_entry *q) { u8 buf[MAX_FILE]; s32 fd, len = q->len, offset = 0, ascii = 0, utf8 = 0, comp; + if (len >= MAX_FILE) len = MAX_FILE - 1; if ((fd = open(q->fname, O_RDONLY)) < 0) return 0; if ((comp = read(fd, buf, len)) != len) return 0; + buf[len] = 0; close(fd); while (offset < len) { diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 44d3c522..ed4a1081 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -819,16 +819,27 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) { fd = open(q->fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + + u32 written = 0; + while (written < q->len) { + + ssize_t result = write(fd, in_buf, q->len - written); + if (result > 0) written += result; + + } + } else { unlink(q->fname); /* ignore errors */ fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600); - } + if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } - if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } + ck_write(fd, in_buf, q->len, q->fname); + + } - ck_write(fd, in_buf, q->len, q->fname); close(fd); memcpy(afl->fsrv.trace_bits, afl->clean_trace, afl->fsrv.map_size); -- cgit 1.4.1 From 19631851f6c7ecac42fb76ff70314f4e0777d3f3 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 5 Aug 2020 12:53:46 +0200 Subject: dynamic symbols export for dlopen --- dynamic_list.txt | 8 ++++++++ llvm_mode/afl-clang-fast.c | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 dynamic_list.txt (limited to 'llvm_mode') diff --git a/dynamic_list.txt b/dynamic_list.txt new file mode 100644 index 00000000..399e48ec --- /dev/null +++ b/dynamic_list.txt @@ -0,0 +1,8 @@ +{ + "__afl_area_ptr"; + "__afl_manual_init"; + "__afl_persistent_loop"; + "__afl_auto_init"; + "__afl_area_initial"; + "__afl_prev_loc"; +}; diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 16f2c9c0..10717124 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -162,7 +162,7 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - preprocessor_only = 0; + shared_linking = 0, preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -402,6 +402,7 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; if (!strcmp(cur, "-E")) preprocessor_only = 1; + if (!strcmp(cur, "-shared")) shared_linking = 1; cc_params[cc_par_cnt++] = cur; @@ -567,12 +568,18 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only) { + if (preprocessor_only || shared_linking) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will simply output its binary content back on stdout, breaking any build - systems that rely on a separate source preprocessing step. */ + systems that rely on a separate source preprocessing step. + The shared_linking case (-shared) is more complex. This flag should + only be passed when linking a shared object. When loading such a shared + object into a binary that has also been built with AFL, two AFL runtimes + will exist side-by-side. This is only a problem in the dynamic loading + case because for static linking, the compiler can de-duplicate the + runtime. We must hence avoid attaching the runtime to shared objects. */ cc_params[cc_par_cnt] = NULL; return; } @@ -619,6 +626,10 @@ static void edit_params(u32 argc, char **argv, char **envp) { } + if (!shared_linking) + cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); + + #endif cc_params[cc_par_cnt] = NULL; -- cgit 1.4.1 From 0281872ddf5f7b5058770eee610ea5250b82cc01 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 5 Aug 2020 13:05:30 +0200 Subject: remove shared_linking check --- llvm_mode/afl-clang-fast.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index 1aa42150..e0b33475 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -568,18 +568,12 @@ static void edit_params(u32 argc, char **argv, char **envp) { } - if (preprocessor_only || shared_linking) { + if (preprocessor_only) { /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will simply output its binary content back on stdout, breaking any build - systems that rely on a separate source preprocessing step. - The shared_linking case (-shared) is more complex. This flag should - only be passed when linking a shared object. When loading such a shared - object into a binary that has also been built with AFL, two AFL runtimes - will exist side-by-side. This is only a problem in the dynamic loading - case because for static linking, the compiler can de-duplicate the - runtime. We must hence avoid attaching the runtime to shared objects. */ + systems that rely on a separate source preprocessing step. */ cc_params[cc_par_cnt] = NULL; return; -- cgit 1.4.1 From 1064c7114e2b4f69a6a3ef63d69900e96e7d4164 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 5 Aug 2020 13:30:43 +0200 Subject: code format --- llvm_mode/afl-clang-fast.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c index e0b33475..efaba122 100644 --- a/llvm_mode/afl-clang-fast.c +++ b/llvm_mode/afl-clang-fast.c @@ -161,8 +161,8 @@ static void find_obj(u8 *argv0) { static void edit_params(u32 argc, char **argv, char **envp) { - u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - shared_linking = 0, preprocessor_only = 0; + u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, shared_linking = 0, + preprocessor_only = 0; u8 have_pic = 0; u8 *name; @@ -567,8 +567,9 @@ static void edit_params(u32 argc, char **argv, char **envp) { cc_params[cc_par_cnt++] = "none"; } - + if (preprocessor_only) { + /* In the preprocessor_only case (-E), we are not actually compiling at all but requesting the compiler to output preprocessed sources only. We must not add the runtime in this case because the compiler will @@ -622,8 +623,8 @@ static void edit_params(u32 argc, char **argv, char **envp) { } if (!shared_linking) - cc_params[cc_par_cnt++] = alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); - + cc_params[cc_par_cnt++] = + alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); #endif -- cgit 1.4.1 From c4e5f75728f18949f754634b669a14c92ad0f5c4 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 5 Aug 2020 15:02:21 +0200 Subject: install dynamic list --- llvm_mode/GNUmakefile | 1 + 1 file changed, 1 insertion(+) (limited to 'llvm_mode') diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index a030b910..49af8e11 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -427,6 +427,7 @@ install: all if [ -f ../split-compares-pass.so ]; then set -e; install -m 755 ../split-compares-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../split-switches-pass.so ]; then set -e; install -m 755 ../split-switches-pass.so $${DESTDIR}$(HELPER_PATH); fi if [ -f ../cmplog-instructions-pass.so ]; then set -e; install -m 755 ../cmplog-*-pass.so $${DESTDIR}$(HELPER_PATH); fi + set -e; install -m 644 ../dynamic_list.txt $${DESTDIR}$(HELPER_PATH) set -e; if [ -f ../afl-clang-fast ] ; then ln -sf ../afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ../afl-clang-fast $${DESTDIR}$(BIN_PATH)/afl-clang++ ; else ln -sf ../afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang ; ln -sf ../afl-gcc $${DESTDIR}$(BIN_PATH)/afl-clang++; fi install -m 644 README.*.md $${DESTDIR}$(DOC_PATH)/ install -m 644 -T README.md $${DESTDIR}$(DOC_PATH)/README.llvm_mode.md -- cgit 1.4.1 From 7e0c9a36ef468dcc74e5658d33cf06b5798648fc Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 6 Aug 2020 19:42:33 +0200 Subject: update persistent doc --- llvm_mode/README.persistent_mode.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'llvm_mode') diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md index 4f0bcb2e..5ed59a58 100644 --- a/llvm_mode/README.persistent_mode.md +++ b/llvm_mode/README.persistent_mode.md @@ -115,6 +115,32 @@ will keep working normally when compiled with a tool other than afl-clang-fast. Finally, recompile the program with afl-clang-fast (afl-gcc or afl-clang will *not* generate a deferred-initialization binary) - and you should be all set! +*NOTE:* In the code between `main` and `__AFL_INIT()` should not be any code +run that is instrumented - otherwise a crash might occure. +In case this is useful (e.g. for expensive one time initialization) you can +try to do the following: + +Add after the includes: +``` +extern unsigned char *__afl_area_ptr; +#define MAX_DUMMY_SIZE 256000 + +__attribute__((constructor(10))) void __afl_protect(void) { +#ifdef MAP_FIXED_NOREPLACE + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) +#endif + __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if ((uint64_t)__afl_area_ptr == -1) + __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); +} + +``` +and just before `__AFL_INIT()`: +``` + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); +``` + ## 4) persistent mode Some libraries provide APIs that are stateless, or whose state can be reset in -- cgit 1.4.1 From 716eb226b282c06c47853d20e46a62bc2f75435a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 6 Aug 2020 20:06:57 +0200 Subject: enforce __afl_cmp_map points to a dummy by default --- llvm_mode/afl-llvm-rt.o.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index fcacc675..8ccd5a8b 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -101,7 +101,7 @@ __thread u32 __afl_cmp_counter; int __afl_sharedmem_fuzzing __attribute__((weak)); -struct cmp_map *__afl_cmp_map; +struct cmp_map *__afl_cmp_map = (struct cmp_map *) __afl_area_initial; /* Running in persistent mode? */ -- cgit 1.4.1 From 58a710d192167dfd9b5e8805c050588e9c0b0baa Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 6 Aug 2020 20:25:57 +0200 Subject: check cmp map null in sancov trace switch --- llvm_mode/afl-llvm-rt.o.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index fcacc675..47a2881b 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -1056,6 +1056,8 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { + if (!__afl_cmp_map) return; + for (uint64_t i = 0; i < cases[0]; i++) { uintptr_t k = (uintptr_t)__builtin_return_address(0) + i; -- cgit 1.4.1 From e2434cf8c6db86e1e7b67cb3b73e417c2a7fd3bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Thu, 6 Aug 2020 23:27:50 +0200 Subject: remove datalen in havoc --- examples/aflpp_driver/aflpp_driver.c | 35 ++++++++++------- llvm_mode/afl-llvm-rt.o.c | 2 +- src/afl-fuzz-one.c | 73 +++++++++++++++++------------------- 3 files changed, 56 insertions(+), 54 deletions(-) (limited to 'llvm_mode') diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 6ec37cda..90f9cf99 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -66,7 +66,7 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #endif #ifndef MAP_FIXED_NOREPLACE -#define MAP_FIXED_NOREPLACE 0x100000 + #define MAP_FIXED_NOREPLACE 0x100000 #endif #define MAX_DUMMY_SIZE 256000 @@ -106,10 +106,10 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #error "Support for your platform has not been implemented" #endif -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char *__afl_fuzz_ptr; -extern unsigned char *__afl_area_ptr; +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char * __afl_fuzz_ptr; +extern unsigned char * __afl_area_ptr; extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. @@ -249,17 +249,21 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } __attribute__((constructor(10))) void __afl_protect(void) { - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + __afl_area_ptr = (unsigned char *)mmap( + (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_area_ptr = (unsigned char *)mmap((void *)0x10000, MAX_DUMMY_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) - __afl_area_ptr = (unsigned char*) mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - __afl_cmp_map = (struct cmp_map *) __afl_area_ptr; -} + __afl_area_ptr = + (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; +} int main(int argc, char **argv) { @@ -272,7 +276,8 @@ int main(int argc, char **argv) { " %s INPUT_FILE1 [INPUT_FILE2 ... ]\n" "To fuzz with afl-fuzz execute this:\n" " afl-fuzz [afl-flags] -- %s [-N]\n" - "afl-fuzz will run N iterations before re-spawning the process (default: 1000)\n" + "afl-fuzz will run N iterations before re-spawning the process (default: " + "1000)\n" "======================================================\n", argv[0], argv[0]); @@ -280,9 +285,11 @@ int main(int argc, char **argv) { maybe_duplicate_stderr(); maybe_close_fd_mask(); if (LLVMFuzzerInitialize) { + fprintf(stderr, "Running LLVMFuzzerInitialize ...\n"); LLVMFuzzerInitialize(&argc, &argv); fprintf(stderr, "continue...\n"); + } // Do any other expensive one-time initialization here. diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index d67862f8..0d498de7 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -101,7 +101,7 @@ __thread u32 __afl_cmp_counter; int __afl_sharedmem_fuzzing __attribute__((weak)); -struct cmp_map *__afl_cmp_map = (struct cmp_map *) __afl_area_initial; +struct cmp_map *__afl_cmp_map = (struct cmp_map *)__afl_area_initial; /* Running in persistent mode? */ diff --git a/src/afl-fuzz-one.c b/src/afl-fuzz-one.c index 77bce7d0..1f0bf30e 100644 --- a/src/afl-fuzz-one.c +++ b/src/afl-fuzz-one.c @@ -1921,14 +1921,14 @@ havoc_stage: /* Flip a single bit somewhere. Spooky! */ - FLIP_BIT(out_buf, rand_below_datalen(afl, temp_len << 3)); + FLIP_BIT(out_buf, rand_below(afl, temp_len << 3)); break; case 1: /* Set byte to interesting value. */ - out_buf[rand_below_datalen(afl, temp_len)] = + out_buf[rand_below(afl, temp_len)] = interesting_8[rand_below(afl, sizeof(interesting_8))]; break; @@ -1940,12 +1940,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]; } else { - *(u16 *)(out_buf + rand_below_datalen(afl, temp_len - 1)) = SWAP16( + *(u16 *)(out_buf + rand_below(afl, temp_len - 1)) = SWAP16( interesting_16[rand_below(afl, sizeof(interesting_16) >> 1)]); } @@ -1960,12 +1960,12 @@ havoc_stage: if (rand_below(afl, 2)) { - *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]; } else { - *(u32 *)(out_buf + rand_below_datalen(afl, temp_len - 3)) = SWAP32( + *(u32 *)(out_buf + rand_below(afl, temp_len - 3)) = SWAP32( interesting_32[rand_below(afl, sizeof(interesting_32) >> 2)]); } @@ -1976,16 +1976,14 @@ havoc_stage: /* Randomly subtract from byte. */ - out_buf[rand_below_datalen(afl, temp_len)] -= - 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below(afl, temp_len)] -= 1 + rand_below(afl, ARITH_MAX); break; case 5: /* Randomly add to byte. */ - out_buf[rand_below_datalen(afl, temp_len)] += - 1 + rand_below(afl, ARITH_MAX); + out_buf[rand_below(afl, temp_len)] += 1 + rand_below(afl, ARITH_MAX); break; case 6: @@ -1996,13 +1994,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2020,13 +2018,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); *(u16 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 1); + u32 pos = rand_below(afl, temp_len - 1); u16 num = 1 + rand_below(afl, ARITH_MAX); *(u16 *)(out_buf + pos) = @@ -2044,13 +2042,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); *(u32 *)(out_buf + pos) -= 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2068,13 +2066,13 @@ havoc_stage: if (rand_below(afl, 2)) { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); *(u32 *)(out_buf + pos) += 1 + rand_below(afl, ARITH_MAX); } else { - u32 pos = rand_below_datalen(afl, temp_len - 3); + u32 pos = rand_below(afl, temp_len - 3); u32 num = 1 + rand_below(afl, ARITH_MAX); *(u32 *)(out_buf + pos) = @@ -2090,8 +2088,7 @@ havoc_stage: why not. We use XOR with 1-255 to eliminate the possibility of a no-op. */ - out_buf[rand_below_datalen(afl, temp_len)] ^= - 1 + rand_below(afl, 255); + out_buf[rand_below(afl, temp_len)] ^= 1 + rand_below(afl, 255); break; case 11 ... 12: { @@ -2108,7 +2105,7 @@ havoc_stage: del_len = choose_block_len(afl, temp_len - 1); - del_from = rand_below_datalen(afl, temp_len - del_len + 1); + del_from = rand_below(afl, temp_len - del_len + 1); memmove(out_buf + del_from, out_buf + del_from + del_len, temp_len - del_from - del_len); @@ -2132,7 +2129,7 @@ havoc_stage: if (actually_clone) { clone_len = choose_block_len(afl, temp_len); - clone_from = rand_below_datalen(afl, temp_len - clone_len + 1); + clone_from = rand_below(afl, temp_len - clone_len + 1); } else { @@ -2141,7 +2138,7 @@ havoc_stage: } - clone_to = rand_below_datalen(afl, temp_len); + clone_to = rand_below(afl, temp_len); new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2159,9 +2156,8 @@ havoc_stage: } else { memset(new_buf + clone_to, - rand_below(afl, 2) - ? rand_below(afl, 256) - : out_buf[rand_below_datalen(afl, temp_len)], + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], clone_len); } @@ -2190,8 +2186,8 @@ havoc_stage: copy_len = choose_block_len(afl, temp_len - 1); - copy_from = rand_below_datalen(afl, temp_len - copy_len + 1); - copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_from = rand_below(afl, temp_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); if (rand_below(afl, 4)) { @@ -2204,9 +2200,8 @@ havoc_stage: } else { memset(out_buf + copy_to, - rand_below(afl, 2) - ? rand_below(afl, 256) - : out_buf[rand_below_datalen(afl, temp_len)], + rand_below(afl, 2) ? rand_below(afl, 256) + : out_buf[rand_below(afl, temp_len)], copy_len); } @@ -2238,7 +2233,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); + insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->a_extras[use_extra].data, extra_len); @@ -2252,7 +2247,7 @@ havoc_stage: if (extra_len > temp_len) { break; } - insert_at = rand_below_datalen(afl, temp_len - extra_len + 1); + insert_at = rand_below(afl, temp_len - extra_len + 1); memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len); @@ -2263,7 +2258,7 @@ havoc_stage: } else { // case 16 u32 use_extra, extra_len, - insert_at = rand_below_datalen(afl, temp_len + 1); + insert_at = rand_below(afl, temp_len + 1); u8 *ptr; /* Insert an extra. Do the same dice-rolling stuff as for the @@ -2367,8 +2362,8 @@ havoc_stage: copy_len = choose_block_len(afl, new_len - 1); if (copy_len > temp_len) copy_len = temp_len; - copy_from = rand_below_datalen(afl, new_len - copy_len + 1); - copy_to = rand_below_datalen(afl, temp_len - copy_len + 1); + copy_from = rand_below(afl, new_len - copy_len + 1); + copy_to = rand_below(afl, temp_len - copy_len + 1); memmove(out_buf + copy_to, new_buf + copy_from, copy_len); @@ -2377,9 +2372,9 @@ havoc_stage: u32 clone_from, clone_to, clone_len; clone_len = choose_block_len(afl, new_len); - clone_from = rand_below_datalen(afl, new_len - clone_len + 1); + clone_from = rand_below(afl, new_len - clone_len + 1); - clone_to = rand_below_datalen(afl, temp_len); + clone_to = rand_below(afl, temp_len); u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len); @@ -2528,7 +2523,7 @@ retry_splicing: /* Split somewhere between the first and last differing byte. */ - split_at = f_diff + rand_below_datalen(afl, l_diff - f_diff); + split_at = f_diff + rand_below(afl, l_diff - f_diff); /* Do the thing. */ -- cgit 1.4.1 From 420b202124c729dbbc777f21c56b1f05c21ee9fe Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 10:37:28 +0200 Subject: temporary fix for cmplog --- llvm_mode/afl-llvm-rt.o.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 0d498de7..4cb99d0d 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -905,6 +905,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { u32 inst_ratio = 100; char *x; + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", start, stop); + if (start == stop || *start) return; x = getenv("AFL_INST_RATIO"); @@ -940,7 +942,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { - if (!__afl_cmp_map) return; + if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -984,7 +986,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { - if (!__afl_cmp_map) return; + if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1005,7 +1007,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { - if (!__afl_cmp_map) return; + if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1056,7 +1058,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - if (!__afl_cmp_map) return; + if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; for (uint64_t i = 0; i < cases[0]; i++) { @@ -1095,7 +1097,7 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { - if (!__afl_cmp_map) return; + if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; -- cgit 1.4.1 From a0fab35bbf8454c0421f7c5c5e6076f0e05b67a7 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 10:42:33 +0200 Subject: temporary fix for cmplog --- llvm_mode/afl-llvm-rt.o.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 4cb99d0d..3e5db87d 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -905,7 +905,8 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { u32 inst_ratio = 100; char *x; - fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", start, stop); + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", start, + stop); if (start == stop || *start) return; @@ -942,7 +943,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { - if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; + if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -986,7 +987,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { - if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; + if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1007,7 +1008,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { - if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; + if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1058,7 +1059,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; + if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; for (uint64_t i = 0; i < cases[0]; i++) { @@ -1097,7 +1098,7 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { - if (!__afl_cmp_map || __afl_cmp_map == __afl_area_initial) return; + if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; -- cgit 1.4.1 From 5b0616614433b87b96bc72951b9a0828974e8a4e Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 11:10:17 +0200 Subject: reverse cmplog temps --- examples/aflpp_driver/aflpp_driver.c | 23 +++++++++++------------ llvm_mode/afl-llvm-rt.o.c | 19 ++++++++++++------- 2 files changed, 23 insertions(+), 19 deletions(-) (limited to 'llvm_mode') diff --git a/examples/aflpp_driver/aflpp_driver.c b/examples/aflpp_driver/aflpp_driver.c index 90f9cf99..7d388799 100644 --- a/examples/aflpp_driver/aflpp_driver.c +++ b/examples/aflpp_driver/aflpp_driver.c @@ -106,11 +106,11 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #error "Support for your platform has not been implemented" #endif -int __afl_sharedmem_fuzzing = 1; -extern unsigned int * __afl_fuzz_len; -extern unsigned char * __afl_fuzz_ptr; -extern unsigned char * __afl_area_ptr; -extern struct cmp_map *__afl_cmp_map; +int __afl_sharedmem_fuzzing = 1; +extern unsigned int * __afl_fuzz_len; +extern unsigned char *__afl_fuzz_ptr; +extern unsigned char *__afl_area_ptr; +// extern struct cmp_map *__afl_cmp_map; // libFuzzer interface is thin, so we don't include any libFuzzer headers. int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); @@ -248,8 +248,9 @@ static int ExecuteFilesOnyByOne(int argc, char **argv) { } -__attribute__((constructor(10))) void __afl_protect(void) { +__attribute__((constructor(1))) void __afl_protect(void) { + setenv("__AFL_DEFER_FORKSRV", "1", 1); __afl_area_ptr = (unsigned char *)mmap( (void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); @@ -261,7 +262,7 @@ __attribute__((constructor(10))) void __afl_protect(void) { __afl_area_ptr = (unsigned char *)mmap(NULL, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; + // __afl_cmp_map = (struct cmp_map *)__afl_area_ptr; } @@ -305,14 +306,11 @@ int main(int argc, char **argv) { printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); else if (argc > 1) { - // if (!getenv("AFL_DRIVER_DONT_DEFER")) { - __afl_sharedmem_fuzzing = 0; - munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + munmap(__afl_area_ptr, MAX_DUMMY_SIZE); // we need to free 0x10000 + __afl_area_ptr = NULL; __afl_manual_init(); - // } return ExecuteFilesOnyByOne(argc, argv); - exit(0); } @@ -320,6 +318,7 @@ int main(int argc, char **argv) { // if (!getenv("AFL_DRIVER_DONT_DEFER")) munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + __afl_area_ptr = NULL; __afl_manual_init(); // Call LLVMFuzzerTestOneInput here so that coverage caused by initialization diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 3e5db87d..22c34ae8 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -101,7 +101,7 @@ __thread u32 __afl_cmp_counter; int __afl_sharedmem_fuzzing __attribute__((weak)); -struct cmp_map *__afl_cmp_map = (struct cmp_map *)__afl_area_initial; +struct cmp_map *__afl_cmp_map; /* Running in persistent mode? */ @@ -183,6 +183,11 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { + // we we are not running in afl ensure the map exists + fprintf(stderr, "Was: %p\n", __afl_area_ptr); + if (!__afl_area_ptr) __afl_area_ptr = __afl_area_initial; + fprintf(stderr, "Is: %p\n", __afl_area_ptr); + char *id_str = getenv(SHM_ENV_VAR); if (__afl_final_loc) { @@ -943,7 +948,7 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { - if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -966,7 +971,7 @@ void __cmplog_ins_hook1(uint8_t arg1, uint8_t arg2) { void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { - if (!__afl_cmp_map) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -987,7 +992,7 @@ void __cmplog_ins_hook2(uint16_t arg1, uint16_t arg2) { void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { - if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1008,7 +1013,7 @@ void __cmplog_ins_hook4(uint32_t arg1, uint32_t arg2) { void __cmplog_ins_hook8(uint64_t arg1, uint64_t arg2) { - if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; + if (unlikely(!__afl_cmp_map)) return; uintptr_t k = (uintptr_t)__builtin_return_address(0); k = (k >> 4) ^ (k << 8); @@ -1059,7 +1064,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; + if (unlikely(!__afl_cmp_map)) return; for (uint64_t i = 0; i < cases[0]; i++) { @@ -1098,7 +1103,7 @@ static int area_is_mapped(void *ptr, size_t len) { void __cmplog_rtn_hook(u8 *ptr1, u8 *ptr2) { - if (unlikely(!__afl_cmp_map || (u8 *)__afl_cmp_map == __afl_area_ptr)) return; + if (unlikely(!__afl_cmp_map)) return; if (!area_is_mapped(ptr1, 32) || !area_is_mapped(ptr2, 32)) return; -- cgit 1.4.1 From 4a6d66d8c5dcbec8b5014ff0445d9292b3958e1d Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 14:43:17 +0200 Subject: fix typos --- docs/Changelog.md | 1 + llvm_mode/README.instrument_list.md | 2 +- llvm_mode/README.persistent_mode.md | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'llvm_mode') diff --git a/docs/Changelog.md b/docs/Changelog.md index f98f8b9b..f8742b1c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -37,6 +37,7 @@ sending a mail to . - LTO: AFL_LLVM_SKIP_NEVERZERO behaviour was inversed, fixed - setting AFL_LLVM_LAF_SPLIT_FLOATS now activates AFL_LLVM_LAF_SPLIT_COMPARES + - support for -E and -shared compilation runs - added honggfuzz mangle as a custom mutator in custom_mutators/honggfuzz - added afl-frida gum solution to examples/afl_frida (mostly imported from https://github.com/meme/hotwax/) diff --git a/llvm_mode/README.instrument_list.md b/llvm_mode/README.instrument_list.md index b0e0cc1e..d4739dda 100644 --- a/llvm_mode/README.instrument_list.md +++ b/llvm_mode/README.instrument_list.md @@ -71,7 +71,7 @@ must be mangled to match! afl++ is intelligent to identify if an entry is a filename or a function. However if you want to be sure (and compliant to the sancov allow/blocklist -format), you can file entries like this: +format), you can specify source file entries like this: ``` src: *malloc.c ``` diff --git a/llvm_mode/README.persistent_mode.md b/llvm_mode/README.persistent_mode.md index 5ed59a58..7d2fd93b 100644 --- a/llvm_mode/README.persistent_mode.md +++ b/llvm_mode/README.persistent_mode.md @@ -125,7 +125,7 @@ Add after the includes: extern unsigned char *__afl_area_ptr; #define MAX_DUMMY_SIZE 256000 -__attribute__((constructor(10))) void __afl_protect(void) { +__attribute__((constructor(1))) void __afl_protect(void) { #ifdef MAP_FIXED_NOREPLACE __afl_area_ptr = (unsigned char*) mmap((void *)0x10000, MAX_DUMMY_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0); if ((uint64_t)__afl_area_ptr == -1) @@ -139,6 +139,7 @@ __attribute__((constructor(10))) void __afl_protect(void) { and just before `__AFL_INIT()`: ``` munmap(__afl_area_ptr, MAX_DUMMY_SIZE); + __afl_area_ptr = NULL; ``` ## 4) persistent mode -- cgit 1.4.1 From 44ad516edd175725e68677ecf5924643f357cf4b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Fri, 7 Aug 2020 17:23:11 +0200 Subject: remove debug --- llvm_mode/afl-llvm-rt.o.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 22c34ae8..8cc59cbb 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -184,9 +184,7 @@ static void __afl_map_shm_fuzz() { static void __afl_map_shm(void) { // we we are not running in afl ensure the map exists - fprintf(stderr, "Was: %p\n", __afl_area_ptr); if (!__afl_area_ptr) __afl_area_ptr = __afl_area_initial; - fprintf(stderr, "Is: %p\n", __afl_area_ptr); char *id_str = getenv(SHM_ENV_VAR); -- cgit 1.4.1 From 45d0e4765e9b60f4107fcf87a128ce521bf2665b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Sat, 8 Aug 2020 19:37:15 +0200 Subject: fix stderr output --- llvm_mode/afl-llvm-rt.o.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 8cc59cbb..623e3a20 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -327,10 +327,13 @@ static void __afl_map_shm(void) { id_str = getenv(CMPLOG_SHM_ENV_VAR); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "DEBUG: cmplog id_str %s\n", id_str == NULL ? "" : id_str); + } + if (id_str) { #ifdef USEMMAP @@ -402,9 +405,12 @@ static void __afl_start_snapshots(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + } + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { @@ -611,9 +617,12 @@ static void __afl_start_forkserver(void) { if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if (getenv("AFL_DEBUG")) + if (getenv("AFL_DEBUG")) { + fprintf(stderr, "target forkserver recv: %08x\n", was_killed); + } + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { @@ -908,8 +917,12 @@ void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { u32 inst_ratio = 100; char *x; - fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", start, - stop); + if (getenv("AFL_DEBUG")) { + + fprintf(stderr, "Running __sanitizer_cov_trace_pc_guard_init: %p-%p\n", + start, stop); + + } if (start == stop || *start) return; -- cgit 1.4.1 From 701fb95d24cd754e9c116d81502b6057a29eb2bd Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 23:42:33 +0200 Subject: LTO: make dynamic map the default --- docs/Changelog.md | 2 ++ llvm_mode/README.lto.md | 20 ++++------------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 33 +++++++++++++++++++++++----- src/afl-forkserver.c | 13 +++++------ 4 files changed, 40 insertions(+), 28 deletions(-) (limited to 'llvm_mode') diff --git a/docs/Changelog.md b/docs/Changelog.md index f8742b1c..182a15b8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -28,6 +28,8 @@ sending a mail to . sancov, and also supports function matching! - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) + - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR + for a fixed map address (eg. 0x10000) - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/llvm_mode/README.lto.md b/llvm_mode/README.lto.md index 4d643324..9046c5a8 100644 --- a/llvm_mode/README.lto.md +++ b/llvm_mode/README.lto.md @@ -17,9 +17,6 @@ This version requires a current llvm 11+ compiled from the github master. 5. If any problems arise be sure to set `AR=llvm-ar RANLIB=llvm-ranlib`. Some targets might need `LD=afl-clang-lto` and others `LD=afl-ld-lto`. -6. If a target uses _init functions or early constructors then additionally - set `AFL_LLVM_MAP_DYNAMIC=1` as your target will crash otherwise! - ## Introduction and problem description A big issue with how afl/afl++ works is that the basic block IDs that are @@ -128,14 +125,14 @@ on start. This improves coverage statistically by 5-10% :) ## Fixed memory map -To speed up fuzzing, the shared memory map is hard set to a specific address, -by default 0x10000. In most cases this will work without any problems. +To speed up fuzzing, it is possible to set a fixed shared memory map. +Recommened is the value 0x10000. +In most cases this will work without any problems. However if a target uses +early constructors, ifuncs or a deferred forkserver this can crash the target. On unusual operating systems/processors/kernels or weird libraries this might fail so to change the fixed address at compile time set AFL_LLVM_MAP_ADDR with a better value (a value of 0 or empty sets the map address to be dynamic - the original afl way, which is slower). -AFL_LLVM_MAP_DYNAMIC can be set so the shared memory address is dynamic (which -is safer but also slower). ## Document edge IDs @@ -262,15 +259,6 @@ If this succeeeds then there is an issue with afl-clang-lto. Please report at Even some targets where clang-12 fails can be build if the fail is just in `./configure`, see `Solving difficult targets` above. -### Target crashes immediately - -If the target is using early constructors (priority values smaller than 6) -or have their own _init/.init functions and these are instrumented then the -target will likely crash when started. This can be avoided by compiling with -`AFL_LLVM_MAP_DYNAMIC=1` . - -This can e.g. happen with OpenSSL. - ## History This was originally envisioned by hexcoder- in Summer 2019, however we saw no diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 38c3f202..ddfcb400 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -49,6 +49,7 @@ #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/Pass.h" +#include "llvm/IR/Constants.h" #include "afl-llvm-common.h" @@ -135,7 +136,10 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_AUTODICTIONARY")) autodictionary = 1; - if (getenv("AFL_LLVM_MAP_DYNAMIC")) map_addr = 0; + // we make this the default as the fixed map has problems with + // defered forkserver, early constructors, ifuncs and maybe more + /*if (getenv("AFL_LLVM_MAP_DYNAMIC"))*/ + map_addr = 0; if (getenv("AFL_LLVM_SKIPSINGLEBLOCK")) function_minimum_size = 2; @@ -196,7 +200,8 @@ bool AFLLTOPass::runOnModule(Module &M) { ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); ConstantInt *One = ConstantInt::get(Int8Ty, 1); - /* This dumps all inialized global strings - might be useful in the future + // This dumps all inialized global strings - might be useful in the future + /* for (auto G=M.getGlobalList().begin(); G!=M.getGlobalList().end(); G++) { GlobalVariable &GV=*G; @@ -212,7 +217,21 @@ bool AFLLTOPass::runOnModule(Module &M) { } - */ + */ + + if (map_addr) + for (GlobalIFunc &IF : M.ifuncs()) { + + // No clue how to follow these up and find the resolver function. + // If we would know that resolver function name we could just skip + // instrumenting it and everything would be fine :-( + // StringRef ifunc_name = IF.getName(); + // Constant *r = IF.getResolver(); + FATAL( + "Target uses ifunc attribute, dynamic map cannot be used, remove " + "AFL_LLVM_MAP_DYNAMIC"); + + } /* Instrument all the things! */ @@ -220,8 +239,12 @@ bool AFLLTOPass::runOnModule(Module &M) { for (auto &F : M) { - // fprintf(stderr, "DEBUG: Module %s Function %s\n", - // M.getName().str().c_str(), F.getName().str().c_str()); + /*For debugging + AttributeSet X = F.getAttributes().getFnAttributes(); + fprintf(stderr, "DEBUG: Module %s Function %s attributes %u\n", + M.getName().str().c_str(), F.getName().str().c_str(), + X.getNumAttributes()); + */ if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 752641d7..1ececf27 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -293,8 +293,8 @@ static void report_error_and_exit(int error) { FATAL( "the fuzzing target reports that hardcoded map address might be the " "reason the mmap of the shared memory failed. Solution: recompile " - "the target with either afl-clang-lto and the environment variable " - "AFL_LLVM_MAP_DYNAMIC set or recompile with afl-clang-fast."); + "the target with either afl-clang-lto and do not set " + "AFL_LLVM_MAP_ADDR or recompile with afl-clang-fast."); break; case FS_ERROR_SHM_OPEN: FATAL("the fuzzing target reports that the shm_open() call failed."); @@ -828,8 +828,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, SAYF("\n" cLRD "[-] " cRST "Hmm, looks like the target binary terminated before we could" " complete a handshake with the injected code.\n" - "If the target was compiled with afl-clang-lto then recompiling with" - " AFL_LLVM_MAP_DYNAMIC might solve your problem.\n" + "If the target was compiled with afl-clang-lto and AFL_LLVM_MAP_ADDR" + " then recompiling without this parameter.\n" "Otherwise there is a horrible bug in the fuzzer.\n" "Poke for troubleshooting tips.\n"); @@ -860,9 +860,8 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, " - the target was compiled with afl-clang-lto and a constructor " "was\n" - " instrumented, recompiling with AFL_LLVM_MAP_DYNAMIC might solve " - "your\n" - " problem\n\n" + " instrumented, recompiling without AFL_LLVM_MAP_ADDR might solve " + "your problem\n\n" " - Less likely, there is a horrible bug in the fuzzer. If other " "options\n" -- cgit 1.4.1 From 1e38c10efb572efac2638366f18a1cf23acd7c2b Mon Sep 17 00:00:00 2001 From: van Hauser Date: Mon, 10 Aug 2020 23:48:13 +0200 Subject: remove warning, fix text --- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index ddfcb400..1933ed8d 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -228,8 +228,9 @@ bool AFLLTOPass::runOnModule(Module &M) { // StringRef ifunc_name = IF.getName(); // Constant *r = IF.getResolver(); FATAL( - "Target uses ifunc attribute, dynamic map cannot be used, remove " - "AFL_LLVM_MAP_DYNAMIC"); + "Target uses ifunc attribute for %s, fixed map cannot be used, " + "remove AFL_LLVM_MAP_ADDR", + IF.getName().str().c_str()); } -- cgit 1.4.1 From 432638404f40594ae163b6e1b92fcfd51b59d59a Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 01:31:44 +0200 Subject: ignoring ifuncs on fixed map LTO --- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 47 ++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index 1933ed8d..abc836aa 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -219,21 +219,30 @@ bool AFLLTOPass::runOnModule(Module &M) { */ - if (map_addr) + std::vector module_block_list; + + if (map_addr) { + for (GlobalIFunc &IF : M.ifuncs()) { + + StringRef ifunc_name = IF.getName(); + Constant *r = IF.getResolver(); + StringRef r_name = cast(r->getOperand(0))->getName(); + if (!be_quiet) + fprintf(stderr, "Found an ifunc with name %s that points to resolver function %s, we cannot instrument this, putting it into a block list.\n", + ifunc_name.str().c_str(), r_name.str().c_str()); - // No clue how to follow these up and find the resolver function. - // If we would know that resolver function name we could just skip - // instrumenting it and everything would be fine :-( - // StringRef ifunc_name = IF.getName(); - // Constant *r = IF.getResolver(); - FATAL( - "Target uses ifunc attribute for %s, fixed map cannot be used, " - "remove AFL_LLVM_MAP_ADDR", - IF.getName().str().c_str()); + module_block_list.push_back(r_name.str()); } + // next up: ctors run before __afl_init() + + // TODO + + + } + /* Instrument all the things! */ int inst_blocks = 0; @@ -250,6 +259,24 @@ bool AFLLTOPass::runOnModule(Module &M) { if (F.size() < function_minimum_size) continue; if (isIgnoreFunction(&F)) continue; + if (module_block_list.size()) { + + for (auto bname : module_block_list) { + + std::string fname = F.getName().str(); + + if (fname.compare(bname) == 0) { + + if (!be_quiet) + WARNF("Skipping instrumentation of ifunc resolver function %s", + fname.c_str()); + + } + + } + + } + // the instrument file list check AttributeList Attrs = F.getAttributes(); if (Attrs.hasAttribute(-1, StringRef("skipinstrument"))) { -- cgit 1.4.1 From 50e76fce123f01ec83024f3bbd3190f2e1a6d387 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Tue, 11 Aug 2020 02:05:39 +0200 Subject: adding ctor function skipping in LTO fixed map mode --- docs/Changelog.md | 1 + include/debug.h | 52 +++++++++---------- llvm_mode/afl-llvm-lto-instrumentation.so.cc | 76 +++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 39 deletions(-) (limited to 'llvm_mode') diff --git a/docs/Changelog.md b/docs/Changelog.md index 182a15b8..25c7a761 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -30,6 +30,7 @@ sending a mail to . reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR for a fixed map address (eg. 0x10000) + - LTO: skipping ctors and ifuncs in fix map address instrumentation - LTO: autodictionary mode is a default - LTO: instrim instrumentation disabled, only classic support used as it is always better diff --git a/include/debug.h b/include/debug.h index 6cc26ec2..f9ebce58 100644 --- a/include/debug.h +++ b/include/debug.h @@ -218,43 +218,43 @@ /* Die with a verbose non-OS fatal error message. */ -#define FATAL(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ +#define FATAL(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", __func__, \ - __FILE__, __LINE__); \ - exit(1); \ - \ + __FILE__, __LINE__); \ + exit(1); \ + \ } while (0) /* Die by calling abort() to provide a core dump. */ -#define ABORT(x...) \ - do { \ - \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] PROGRAM ABORT : " cRST x); \ +#define ABORT(x...) \ + do { \ + \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] PROGRAM ABORT : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", __func__, \ - __FILE__, __LINE__); \ - abort(); \ - \ + __FILE__, __LINE__); \ + abort(); \ + \ } while (0) /* Die while also including the output of perror(). */ -#define PFATAL(x...) \ - do { \ - \ - fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ - "\n[-] SYSTEM ERROR : " cRST x); \ +#define PFATAL(x...) \ + do { \ + \ + fflush(stdout); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD \ + "\n[-] SYSTEM ERROR : " cRST x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", __func__, \ - __FILE__, __LINE__); \ - SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ - exit(1); \ - \ + __FILE__, __LINE__); \ + SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ + exit(1); \ + \ } while (0) /* Die with FATAL() or PFATAL() depending on the value of res (used to diff --git a/llvm_mode/afl-llvm-lto-instrumentation.so.cc b/llvm_mode/afl-llvm-lto-instrumentation.so.cc index abc836aa..fd8e48a7 100644 --- a/llvm_mode/afl-llvm-lto-instrumentation.so.cc +++ b/llvm_mode/afl-llvm-lto-instrumentation.so.cc @@ -224,22 +224,70 @@ bool AFLLTOPass::runOnModule(Module &M) { if (map_addr) { for (GlobalIFunc &IF : M.ifuncs()) { - + StringRef ifunc_name = IF.getName(); Constant *r = IF.getResolver(); StringRef r_name = cast(r->getOperand(0))->getName(); if (!be_quiet) - fprintf(stderr, "Found an ifunc with name %s that points to resolver function %s, we cannot instrument this, putting it into a block list.\n", + fprintf(stderr, + "Warning: Found an ifunc with name %s that points to resolver " + "function %s, we cannot instrument this, putting it into a " + "block list.\n", ifunc_name.str().c_str(), r_name.str().c_str()); - module_block_list.push_back(r_name.str()); } - // next up: ctors run before __afl_init() - - // TODO + GlobalVariable *GV = M.getNamedGlobal("llvm.global_ctors"); + if (GV && !GV->isDeclaration() && !GV->hasLocalLinkage()) { + + ConstantArray *InitList = dyn_cast(GV->getInitializer()); + + if (InitList) { + + for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { + + if (ConstantStruct *CS = + dyn_cast(InitList->getOperand(i))) { + + if (CS->getNumOperands() >= 2) { + if (CS->getOperand(1)->isNullValue()) + break; // Found a null terminator, stop here. + + ConstantInt *CI = dyn_cast(CS->getOperand(0)); + int Priority = CI ? CI->getSExtValue() : 0; + + Constant *FP = CS->getOperand(1); + if (ConstantExpr *CE = dyn_cast(FP)) + if (CE->isCast()) FP = CE->getOperand(0); + if (Function *F = dyn_cast(FP)) { + + if (!F->isDeclaration() && + strncmp(F->getName().str().c_str(), "__afl", 5) != 0 && + Priority <= 5) { + + if (!be_quiet) + fprintf(stderr, + "Warning: Found constructor function %s with prio " + "%u, we cannot instrument this, putting it into a " + "block list.\n", + F->getName().str().c_str(), Priority); + module_block_list.push_back(F->getName().str()); + + } + + } + + } + + } + + } + + } + + } } @@ -260,21 +308,23 @@ bool AFLLTOPass::runOnModule(Module &M) { if (isIgnoreFunction(&F)) continue; if (module_block_list.size()) { - + for (auto bname : module_block_list) { std::string fname = F.getName().str(); if (fname.compare(bname) == 0) { - + if (!be_quiet) - WARNF("Skipping instrumentation of ifunc resolver function %s", - fname.c_str()); - + WARNF( + "Skipping instrumentation of dangerous early running function " + "%s", + fname.c_str()); + } - + } - + } // the instrument file list check -- cgit 1.4.1 From 132b57cf030fd206bc54d6c810868c48b17445bb Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 10:41:09 +0200 Subject: nozero for llvm pcguard --- docs/Changelog.md | 1 + llvm_mode/afl-llvm-rt.o.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'llvm_mode') diff --git a/docs/Changelog.md b/docs/Changelog.md index 25c7a761..eda57a1a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -26,6 +26,7 @@ sending a mail to . AFL_LLVM_WHITELIST and AFL_LLVM_INSTRUMENT_FILE are deprecated and are matched to AFL_LLVM_ALLOWLIST). The format is compatible to llvm sancov, and also supports function matching! + - added nozero counting to trace-pc/pcgard - fixes for laf-intel float splitting (thanks to mark-griffin for reporting) - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 623e3a20..8a073318 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -904,7 +904,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { */ - __afl_area_ptr[*guard]++; + __afl_area_ptr[*guard] = __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); } -- cgit 1.4.1 From e9b3da5d9633e24fee6759462b2f1151f6fadfad Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 11:03:33 +0200 Subject: llvm 9 workaround --- llvm_mode/afl-llvm-rt.o.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'llvm_mode') diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 8a073318..895028b0 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -903,9 +903,16 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { } */ +#if LLVM_VERSION_MAJOR < 9 + + __afl_area_ptr[*guard]++; + +#else __afl_area_ptr[*guard] = __afl_area_ptr[*guard] + 1 + (__afl_area_ptr[*guard] == 255 ? 1 : 0); +#endif + } /* Init callback. Populates instrumentation IDs. Note that we're using -- cgit 1.4.1 From c25479264d8334abcd9850e3d2c6a0200220b625 Mon Sep 17 00:00:00 2001 From: van Hauser Date: Wed, 12 Aug 2020 13:28:31 +0200 Subject: fix LLVM version check --- llvm_mode/GNUmakefile | 12 ++++++------ llvm_mode/afl-llvm-rt.o.c | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'llvm_mode') diff --git a/llvm_mode/GNUmakefile b/llvm_mode/GNUmakefile index 49af8e11..0fa9b12e 100644 --- a/llvm_mode/GNUmakefile +++ b/llvm_mode/GNUmakefile @@ -387,20 +387,20 @@ endif $(CXX) $(CLANG_CPPFL) -shared $< -o $@ $(CLANG_LFL) afl-llvm-common.o document: - $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi - @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + $(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt.o + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -m32 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-32.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) -D_AFL_DOCUMENT_MUTATIONS $(CFLAGS_SAFE) $(CLANG_CFL) -O3 -Wno-unused-result -m64 -fPIC -c afl-llvm-rt.o.c -o ../afl-llvm-rt-64.o 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi ../afl-llvm-rt.o: afl-llvm-rt.o.c | test_deps - $(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c $< -o $@ + $(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -fPIC -c $< -o $@ ../afl-llvm-rt-32.o: afl-llvm-rt.o.c | test_deps @printf "[*] Building 32-bit variant of the runtime (-m32)... " - @$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m32 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi ../afl-llvm-rt-64.o: afl-llvm-rt.o.c | test_deps @printf "[*] Building 64-bit variant of the runtime (-m64)... " - @$(CLANG_BIN) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi + @$(CLANG_BIN) $(CLANG_CFL) $(CFLAGS_SAFE) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." diff --git a/llvm_mode/afl-llvm-rt.o.c b/llvm_mode/afl-llvm-rt.o.c index 895028b0..a567593e 100644 --- a/llvm_mode/afl-llvm-rt.o.c +++ b/llvm_mode/afl-llvm-rt.o.c @@ -42,6 +42,8 @@ #include #include +#include "llvm/Config/llvm-config.h" + #ifdef __linux__ #include "snapshot-inl.h" #endif @@ -903,7 +905,8 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { } */ -#if LLVM_VERSION_MAJOR < 9 + +#if (LLVM_VERSION_MAJOR < 9) __afl_area_ptr[*guard]++; -- cgit 1.4.1