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/afl-llvm-common.cc | 485 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 413 insertions(+), 72 deletions(-) (limited to 'llvm_mode/afl-llvm-common.cc') 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 } -- 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/afl-llvm-common.cc') 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