diff options
Diffstat (limited to 'instrumentation/afl-gcc-pass.so.cc')
-rw-r--r-- | instrumentation/afl-gcc-pass.so.cc | 968 |
1 files changed, 968 insertions, 0 deletions
diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc new file mode 100644 index 00000000..04d606cc --- /dev/null +++ b/instrumentation/afl-gcc-pass.so.cc @@ -0,0 +1,968 @@ +/* GCC plugin for instrumentation of code for american fuzzy lop. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AdaCore + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL + LLVM pass by Laszlo Szekeres <lszekeres@google.com> and Michal + Zalewski <lcamtuf@google.com>, and copying a little boilerplate + from GCC's libcc1 plugin and GCC proper. Aside from the + boilerplate, namely includes and the pass data structure, and pass + initialization code and output messages borrowed and adapted from + the LLVM pass into plugin_init and plugin_finalize, the + implementation of the GCC pass proper is written from scratch, + aiming at similar behavior and performance to that of the LLVM + pass, and also at compatibility with the out-of-line + instrumentation and run times of AFL++, as well as of an earlier + GCC plugin implementation by Austin Seipp <aseipp@pobox.com>. The + implementation of Allow/Deny Lists is adapted from that in the LLVM + plugin. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +/* This file implements a GCC plugin that introduces an + instrumentation pass for AFL. What follows is the specification + used to rewrite it, extracted from the functional llvm_mode pass + and from an implementation of the gcc_plugin started by Austin + Seipp <aseipp@pobox.com>. + + Declare itself as GPL-compatible. + + Define a 'plugin_init' function. + + Check version against the global gcc_version. + + Register a PLUGIN_INFO object with .version and .help. + + Initialize the random number generator seed with GCC's + random seed. + + Set quiet mode depending on whether stderr is a terminal and + AFL_QUIET is set. + + Output some identification message if not in quiet mode. + + Parse AFL_INST_RATIO, if set, as a number between 0 and 100. Error + out if it's not in range; set up an instrumentation ratio global + otherwise. + + Introduce a single instrumentation pass after SSA. + + The new pass is to be a GIMPLE_PASS. Given the sort of + instrumentation it's supposed to do, its todo_flags_finish will + certainly need TODO_update_ssa, and TODO_cleanup_cfg. + TODO_verify_il is probably desirable, at least during debugging. + TODO_rebuild_cgraph_edges is required only in the out-of-line + instrumentation mode. + + The instrumentation pass amounts to iterating over all basic blocks + and optionally inserting one of the instrumentation sequences below + after its labels, to indicate execution entered the block. + + A block should be skipped if R(100) (from ../types.h) is >= the + global instrumentation ratio. + + A block may be skipped for other reasons, such as if all of its + predecessors have a single successor. + + For an instrumented block, a R(MAP_SIZE) say <N> should be + generated to be used as its location number. Let <C> be a compiler + constant built out of it. + + Count instrumented blocks and print a message at the end of the + compilation, if not in quiet mode. + + Instrumentation in "dumb" or "out-of-line" mode requires calling a + function, passing it the location number. The function to be + called is __afl_trace, implemented in afl-gcc-rt.o.c. Its + declaration <T> needs only be created once. + + Build the call statement <T> (<C>), then add it to the seq to be + inserted. + + Instrumentation in "fast" or "inline" mode performs the computation + of __afl_trace as part of the function. + + It needs to read and write __afl_prev_loc, a TLS u32 variable. Its + declaration <P> needs only be created once. + + It needs to read and dereference __afl_area_ptr, a pointer to (an + array of) char. Its declaration <M> needs only be created once. + + The instrumentation sequence should then be filled with the + following statements: + + Load from <P> to a temporary (<TP>) of the same type. + + Compute <TP> ^ <C> in sizetype, converting types as needed. + + Pointer-add <B> (to be introduced at a later point) and <I> into + another temporary <A>. + + Increment the <*A> MEM_REF. + + Store <C> >> 1 in <P>. + + Temporaries used above need only be created once per function. + + If any block was instrumented in a function, an initializer for <B> + needs to be introduced, loading it from <M> and inserting it in the + 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> + +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; + +namespace { + +static const struct pass_data afl_pass_data = { + + .type = GIMPLE_PASS, + .name = "afl", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il), + +}; + +struct afl_pass : gimple_opt_pass { + + afl_pass(bool quiet, unsigned int ratio) + : gimple_opt_pass(afl_pass_data, g), + be_quiet(quiet), + debug(!!getenv("AFL_DEBUG")), + inst_ratio(ratio), +#ifdef AFL_GCC_OUT_OF_LINE + out_of_line(!!(AFL_GCC_OUT_OF_LINE)), +#else + out_of_line(getenv("AFL_GCC_OUT_OF_LINE")), +#endif + neverZero(!getenv("AFL_GCC_SKIP_NEVERZERO")), + inst_blocks(0) { + + initInstrumentList(); + + } + + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + + /* How likely (%) is a block to be instrumented? */ + const unsigned int inst_ratio; + + /* Should we use slow, out-of-line call-based instrumentation? */ + const bool out_of_line; + + /* Should we make sure the map edge-crossing counters never wrap + around to zero? */ + const bool neverZero; + + /* Count instrumented blocks. */ + int inst_blocks; + + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; + + int blocks = 0; + + /* These are temporaries used by inline instrumentation only, that + are live throughout the function. */ + tree ploc = NULL, indx = NULL, map = NULL, map_ptr = NULL, ntry = NULL, + cntr = NULL, xaddc = NULL, xincr = NULL; + + basic_block bb; + FOR_EACH_BB_FN(bb, fn) { + + if (!instrument_block_p(bb)) continue; + + /* Generate the block identifier. */ + unsigned bid = R(MAP_SIZE); + tree bidt = build_int_cst(sizetype, bid); + + gimple_seq seq = NULL; + + if (out_of_line) { + + static tree afl_trace = get_afl_trace_decl(); + + /* Call __afl_trace with bid, the new location; */ + gcall *call = gimple_build_call(afl_trace, 1, bidt); + gimple_seq_add_stmt(&seq, call); + + } else { + + static tree afl_prev_loc = get_afl_prev_loc_decl(); + static tree afl_area_ptr = get_afl_area_ptr_decl(); + + /* Load __afl_prev_loc to a temporary ploc. */ + if (blocks == 0) + ploc = create_tmp_var(TREE_TYPE(afl_prev_loc), ".afl_prev_loc"); + auto load_loc = gimple_build_assign(ploc, afl_prev_loc); + gimple_seq_add_stmt(&seq, load_loc); + + /* Compute the index into the map referenced by area_ptr + that we're to update: indx = (sizetype) ploc ^ bid. */ + if (blocks == 0) indx = create_tmp_var(TREE_TYPE(bidt), ".afl_index"); + auto conv_ploc = + gimple_build_assign(indx, fold_convert(TREE_TYPE(indx), ploc)); + gimple_seq_add_stmt(&seq, conv_ploc); + auto xor_loc = gimple_build_assign(indx, BIT_XOR_EXPR, indx, bidt); + gimple_seq_add_stmt(&seq, xor_loc); + + /* Compute the address of that map element. */ + if (blocks == 0) { + + map = afl_area_ptr; + map_ptr = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_ptr"); + ntry = create_tmp_var(TREE_TYPE(afl_area_ptr), ".afl_map_entry"); + + } + + /* .map_ptr is initialized at the function entry point, if we + instrument any blocks, see below. */ + + /* .entry = &map_ptr[.index]; */ + auto idx_map = + gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); + gimple_seq_add_stmt(&seq, idx_map); + + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + /* 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)); + + if (neverZero) { + + /* NeverZero: if count wrapped around to zero, advance to + one. */ + if (blocks == 0) { + + xaddc = create_tmp_var(build_complex_type(TREE_TYPE(memref)), + ".afl_edge_xaddc"); + xincr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_xincr"); + + } + + /* Call the ADD_OVERFLOW builtin, to add 1 (in incrv) to + count. The builtin yields a complex pair: the result of + the add in the real part, and the overflow flag in the + imaginary part, */ + auto_vec<tree> vargs(2); + vargs.quick_push(cntr); + vargs.quick_push(incrv); + gcall *add1_cntr = + gimple_build_call_internal_vec(IFN_ADD_OVERFLOW, vargs); + gimple_call_set_lhs(add1_cntr, xaddc); + gimple_seq_add_stmt(&seq, add1_cntr); + + /* Extract the real part into count. */ + tree cntrb = build1(REALPART_EXPR, TREE_TYPE(cntr), xaddc); + auto xtrct_cntr = gimple_build_assign(cntr, cntrb); + gimple_seq_add_stmt(&seq, xtrct_cntr); + + /* Extract the imaginary part into xincr. */ + tree incrb = build1(IMAGPART_EXPR, TREE_TYPE(xincr), xaddc); + auto xtrct_xincr = gimple_build_assign(xincr, incrb); + gimple_seq_add_stmt(&seq, xtrct_xincr); + + /* Arrange for the add below to use the overflow flag stored + 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); + + /* Store bid >> 1 in __afl_prev_loc. */ + auto shift_loc = + gimple_build_assign(ploc, build_int_cst(TREE_TYPE(ploc), bid >> 1)); + gimple_seq_add_stmt(&seq, shift_loc); + auto store_loc = gimple_build_assign(afl_prev_loc, ploc); + gimple_seq_add_stmt(&seq, store_loc); + + } + + /* Insert the generated sequence. */ + gimple_stmt_iterator insp = gsi_after_labels(bb); + gsi_insert_seq_before(&insp, seq, GSI_SAME_STMT); + + /* Bump this function's instrumented block counter. */ + blocks++; + + } + + /* Aggregate the instrumented block count. */ + inst_blocks += blocks; + + if (blocks) { + + if (out_of_line) return TODO_rebuild_cgraph_edges; + + gimple_seq seq = NULL; + + /* Load afl_area_ptr into map_ptr. We want to do this only + once per function. */ + auto load_ptr = gimple_build_assign(map_ptr, map); + gimple_seq_add_stmt(&seq, load_ptr); + + /* Insert it in the edge to the entry block. We don't want to + insert it in the first block, since there might be a loop + or a goto back to it. Insert in the edge, which may create + another block. */ + edge e = single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(fn)); + gsi_insert_seq_on_edge_immediate(e, seq); + + } + + return 0; + + } + + /* Decide whether to instrument block BB. Skip it due to the random + distribution, or if it's the single successor of all its + predecessors. */ + inline bool instrument_block_p(basic_block bb) { + + if (R(100) >= inst_ratio) return false; + + edge e; + edge_iterator ei; + FOR_EACH_EDGE(e, ei, bb->preds) + if (!single_succ_p(e->src)) return true; + + return false; + + } + + /* Create and return a declaration for the __afl_trace rt function. */ + static inline tree get_afl_trace_decl() { + + tree type = + build_function_type_list(void_type_node, uint16_type_node, NULL_TREE); + tree decl = build_fn_decl("__afl_trace", type); + + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + + return decl; + + } + + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree get_afl_prev_loc_decl() { + + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_prev_loc"), uint32_type_node); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; + set_decl_tls_model( + decl, (flag_pic ? TLS_MODEL_INITIAL_EXEC : TLS_MODEL_LOCAL_EXEC)); + return decl; + + } + + /* Create and return a declaration for the __afl_prev_loc + thread-local variable. */ + static inline tree get_afl_area_ptr_decl() { + + tree type = build_pointer_type(unsigned_char_type_node); + tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, + get_identifier("__afl_area_ptr"), type); + TREE_PUBLIC(decl) = 1; + DECL_EXTERNAL(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; + TREE_STATIC(decl) = 1; + + return decl; + + } + + /* This is registered as a plugin finalize callback, to print an + instrumentation summary unless in quiet mode. */ + static void plugin_finalize(void *, void *p) { + + opt_pass *op = (opt_pass *)p; + afl_pass &self = (afl_pass &)*op; + + if (!self.be_quiet) { + + if (!self.inst_blocks) + WARNF("No instrumentation targets found."); + else + OKF("Instrumented %u locations (%s mode, %s, ratio %u%%).", + self.inst_blocks, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened"), + self.out_of_line ? G_("out of line") : G_("inline"), + self.inst_ratio); + + } + + } + +#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 const char *ignoreList[] = { + + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzer", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" + + }; + + 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) + SAYF(cMGN "[D] " cRST + "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) + SAYF(cMGN "[D] " cRST + "loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } + + } + + std::string getSourceName(function *F) { + + return 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) + SAYF(cMGN "[D] " cRST + "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) + SAYF(cMGN "[D] " cRST + "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) + SAYF(cMGN "[D] " cRST + "Function %s is in the allowlist (%s), " + "instrumenting ... \n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); + return true; + + } + + } + + } + + } 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 = "20200907", + .help = G_("AFL gcc plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +\n\ +Set AFL_INST_RATIO in the environment to a number from 0 to 100\n\ +to control how likely a block will be chosen for instrumentation.\n\ +\n\ +Specify -frandom-seed for reproducible instrumentation.\n\ +"), + +}; + +} // namespace + +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %d.%d"), + GCCPLUGIN_VERSION_MAJOR, GCCPLUGIN_VERSION_MINOR); + + /* Show a banner. */ + bool quiet = false; + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-pass " cBRI VERSION cRST " by <oliva@adacore.com>\n"); + else + quiet = true; + + /* Decide instrumentation ratio. */ + int inst_ratio = 100; + if (char *inst_ratio_str = getenv("AFL_INST_RATIO")) + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || !inst_ratio || + inst_ratio > 100) + FATAL(G_("Bad value of AFL_INST_RATIO (must be between 1 and 100)")); + + /* Initialize the random number generator with GCC's random seed, in + case it was specified in the command line's -frandom-seed for + reproducible instrumentation. */ + srandom(get_random_seed(false)); + + const char *name = info->base_name; + register_callback(name, PLUGIN_INFO, NULL, &afl_plugin); + + afl_pass * aflp = new afl_pass(quiet, inst_ratio); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + register_callback(name, PLUGIN_FINISH, afl_pass::plugin_finalize, + pass_info.pass); + + if (!quiet) + ACTF(G_("%s instrumentation at ratio of %u%% in %s mode."), + aflp->out_of_line ? G_("Call-based") : G_("Inline"), inst_ratio, + getenv("AFL_HARDEN") ? G_("hardened") : G_("non-hardened")); + + return 0; + +} + |