diff options
Diffstat (limited to 'instrumentation/afl-gcc-pass.so.cc')
-rw-r--r-- | instrumentation/afl-gcc-pass.so.cc | 518 |
1 files changed, 35 insertions, 483 deletions
diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index bb5483fc..795bbd8a 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -124,50 +124,8 @@ entry edge for the entry block. */ -#include "../include/config.h" -#include "../include/debug.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#ifdef likely - #undef likely -#endif -#ifdef unlikely - #undef unlikely -#endif - -#include <list> -#include <string> -#include <fstream> - -#include <algorithm> -#include <fnmatch.h> - -#include <gcc-plugin.h> -#include <plugin-version.h> -#include <toplev.h> -#include <tree-pass.h> -#include <context.h> -#include <tree.h> -#include <gimplify.h> -#include <basic-block.h> -#include <tree-ssa-alias.h> -#include <gimple-expr.h> -#include <gimple.h> -#include <gimple-iterator.h> -#include <stringpool.h> -#include <gimple-ssa.h> -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ - 60200 /* >= version 6.2.0 */ - #include <tree-vrp.h> -#endif -#include <tree-ssanames.h> -#include <tree-phinodes.h> -#include <ssa-iterators.h> - -#include <intl.h> +#include "afl-gcc-common.h" +#include "memmodel.h" /* This plugin, being under the same license as GCC, satisfies the "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY @@ -191,12 +149,10 @@ static constexpr struct pass_data afl_pass_data = { }; -struct afl_pass : gimple_opt_pass { +struct afl_pass : afl_base_pass { afl_pass(bool quiet, unsigned int ratio) - : gimple_opt_pass(afl_pass_data, g), - be_quiet(quiet), - debug(!!getenv("AFL_DEBUG")), + : afl_base_pass(quiet, !!getenv("AFL_DEBUG"), afl_pass_data), inst_ratio(ratio), #ifdef AFL_GCC_OUT_OF_LINE out_of_line(!!(AFL_GCC_OUT_OF_LINE)), @@ -210,13 +166,6 @@ struct afl_pass : gimple_opt_pass { } - /* Are we outputting to a non-terminal, or running with AFL_QUIET - set? */ - const bool be_quiet; - - /* Are we running with AFL_DEBUG set? */ - const bool debug; - /* How likely (%) is a block to be instrumented? */ const unsigned int inst_ratio; @@ -297,21 +246,22 @@ struct afl_pass : gimple_opt_pass { gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); gimple_seq_add_stmt(&seq, idx_map); - /* Increment the counter in idx_map. */ - tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, - build_zero_cst(TREE_TYPE(ntry))); - if (blocks == 0) - cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); - - /* Load the count from the entry. */ - auto load_cntr = gimple_build_assign(cntr, memref); - gimple_seq_add_stmt(&seq, load_cntr); - /* Prepare to add constant 1 to it. */ - tree incrv = build_one_cst(TREE_TYPE(cntr)); + tree incrv = build_one_cst(TREE_TYPE(TREE_TYPE(ntry))); if (neverZero) { + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + /* Load the count from the entry. */ + auto load_cntr = gimple_build_assign(cntr, memref); + gimple_seq_add_stmt(&seq, load_cntr); + /* NeverZero: if count wrapped around to zero, advance to one. */ if (blocks == 0) { @@ -348,15 +298,24 @@ struct afl_pass : gimple_opt_pass { in xincr. */ incrv = xincr; - } + /* Add the increment (1 or the overflow bit) to count. */ + auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); + gimple_seq_add_stmt(&seq, incr_cntr); + + /* Store count in the map entry. */ + auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); + gimple_seq_add_stmt(&seq, store_cntr); - /* Add the increment (1 or the overflow bit) to count. */ - auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); - gimple_seq_add_stmt(&seq, incr_cntr); + } else { - /* Store count in the map entry. */ - auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); - gimple_seq_add_stmt(&seq, store_cntr); + /* Use a serialized memory model. */ + tree memmod = build_int_cst(integer_type_node, MEMMODEL_SEQ_CST); + + tree fadd = builtin_decl_explicit(BUILT_IN_ATOMIC_FETCH_ADD_1); + auto incr_cntr = gimple_build_call(fadd, 3, ntry, incrv, memmod); + gimple_seq_add_stmt(&seq, incr_cntr); + + } /* Store bid >> 1 in __afl_prev_loc. */ auto shift_loc = @@ -456,6 +415,8 @@ struct afl_pass : gimple_opt_pass { thread-local variable. */ static inline tree get_afl_area_ptr_decl() { + /* If type changes, the size N in FETCH_ADD_<N> must be adjusted + in builtin calls above. */ tree type = build_pointer_type(unsigned_char_type_node); tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, get_identifier("__afl_area_ptr"), type); @@ -490,420 +451,11 @@ struct afl_pass : gimple_opt_pass { } -#define report_fatal_error(msg) BADF(msg) - - std::list<std::string> allowListFiles; - std::list<std::string> allowListFunctions; - std::list<std::string> denyListFiles; - std::list<std::string> denyListFunctions; - - /* Note: this ignore check is also called in isInInstrumentList() */ - bool isIgnoreFunction(function *F) { - - // Starting from "LLVMFuzzer" these are functions used in libfuzzer based - // fuzzing campaign installations, e.g. oss-fuzz - - static constexpr const char *ignoreList[] = { - - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzerM", - "LLVMFuzzerC", - "LLVMFuzzerI", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" - - }; - - const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); - - for (auto const &ignoreListFunc : ignoreList) { - - if (strncmp(name, ignoreListFunc, len) == 0) { return true; } - - } - - return false; - - } - - void initInstrumentList() { - - char *allowlist = getenv("AFL_GCC_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); - char *denylist = getenv("AFL_GCC_DENYLIST"); - if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - - if (allowlist && denylist) - FATAL( - "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " - "but not both!"); - - if (allowlist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(allowlist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); - - } - - 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) - DEBUGF("loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); - - } - - if (denylist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(denylist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_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_GCC_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) - DEBUGF("loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); - - } - - } - - /* Returns the source file name attached to the function declaration F. If - there is no source location information, returns an empty string. */ - std::string getSourceName(function *F) { - - return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : ""; - - } - - bool isInInstrumentList(function *F) { - - bool return_default = true; - - // is this a function with code? If it is external we don't instrument it - // anyway and it can't be in the instrument file list. Or if it is it is - // ignored. - if (isIgnoreFunction(F)) return false; - - if (!denyListFiles.empty() || !denyListFunctions.empty()) { - - if (!denyListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the deny function list, not " - "instrumenting ... \n", - instFunction.c_str()); - return false; - - } - - } - - } - - } - - if (!denyListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::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 (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - return false; - - } - - } - - } - - } else { - - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - - } - - } - - } - - // if we do not have a instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { - - return_default = false; - - if (!allowListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::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 */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allow function list, instrumenting " - "... \n", - instFunction.c_str()); - return true; - - } - - } - - } - - } - - if (!allowListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::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 (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allowlist (%s), instrumenting ... " - "\n", - IDENTIFIER_POINTER(DECL_NAME(F->decl)), - source_file.c_str()); - return true; - - } - - } - - } - - } else { - - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - return false; - - } - - } - - } - - return return_default; - - } - }; static struct plugin_info afl_plugin = { - .version = "20220907", + .version = "20220420", .help = G_("AFL gcc plugin\n\ \n\ Set AFL_QUIET in the environment to silence it.\n\ |