From 491596b6f0d6ea7a4c7f53962842a644da84c9b0 Mon Sep 17 00:00:00 2001 From: ph2ocwf5z <1781919720@qq.com> Date: Tue, 22 Oct 2024 02:06:41 +0800 Subject: [PATCH 1/4] Delete 'src/AFLplusplus-stable/src/afl-as.c' --- src/AFLplusplus-stable/src/afl-as.c | 671 ---------------------------- 1 file changed, 671 deletions(-) delete mode 100644 src/AFLplusplus-stable/src/afl-as.c diff --git a/src/AFLplusplus-stable/src/afl-as.c b/src/AFLplusplus-stable/src/afl-as.c deleted file mode 100644 index d4ddb94..0000000 --- a/src/AFLplusplus-stable/src/afl-as.c +++ /dev/null @@ -1,671 +0,0 @@ -/* - american fuzzy lop++ - wrapper for GNU as - ----------------------------------------- - - Originally written by Michal Zalewski - - Now maintained by Marc Heuse , - Heiko Eissfeldt and - Andrea Fioraldi - - Copyright 2016, 2017 Google Inc. All rights reserved. - Copyright 2019-2024 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - https://www.apache.org/licenses/LICENSE-2.0 - - The sole purpose of this wrapper is to preprocess assembly files generated - by GCC / clang and inject the instrumentation bits included from afl-as.h. It - is automatically invoked by the toolchain when compiling programs using - afl-gcc / afl-clang. - - Note that it's an explicit non-goal to instrument hand-written assembly, - be it in separate .s files or in __asm__ blocks. The only aspiration this - utility has right now is to be able to skip them gracefully and allow the - compilation process to continue. - - That said, see utils/clang_asm_normalize/ for a solution that may - allow clang users to make things work even with hand-crafted assembly. Just - note that there is no equivalent for GCC. - - */ - -#define AFL_MAIN - -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" - -#include "afl-as.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static u8 **as_params; /* Parameters passed to the real 'as' */ - -static u8 *input_file; /* Originally specified input file */ -static u8 *modified_file; /* Instrumented file for the real 'as' */ - -static u8 be_quiet, /* Quiet mode (no stderr output) */ - clang_mode, /* Running in clang mode? */ - pass_thru, /* Just pass data through? */ - just_version, /* Just show version? */ - sanitizer; /* Using ASAN / MSAN */ - -static u32 inst_ratio = 100, /* Instrumentation probability (%) */ - as_par_cnt = 1; /* Number of params to 'as' */ - -/* If we don't find --32 or --64 in the command line, default to - instrumentation for whichever mode we were compiled with. This is not - perfect, but should do the trick for almost all use cases. */ - -#ifdef WORD_SIZE_64 - -static u8 use_64bit = 1; - -#else - -static u8 use_64bit = 0; - - #ifdef __APPLE__ - #error "Sorry, 32-bit Apple platforms are not supported." - #endif /* __APPLE__ */ - -#endif /* ^WORD_SIZE_64 */ - -/* Examine and modify parameters to pass to 'as'. Note that the file name - is always the last parameter passed by GCC, so we exploit this property - to keep the code simple. */ - -static void edit_params(int argc, char **argv) { - - u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); - u32 i, input_index; - -#ifdef __APPLE__ - - u8 use_clang_as = 0; - - /* On MacOS X, the Xcode cctool 'as' driver is a bit stale and does not work - with the code generated by newer versions of clang that are hand-built - by the user. See the thread here: https://goo.gl/HBWDtn. - - To work around this, when using clang and running without AFL_AS - specified, we will actually call 'clang -c' instead of 'as -q' to - compile the assembly file. - - The tools aren't cmdline-compatible, but at least for now, we can - seemingly get away with this by making only very minor tweaks. Thanks - to Nico Weber for the idea. */ - - if (clang_mode && !afl_as) { - - use_clang_as = 1; - - afl_as = getenv("AFL_CC"); - if (!afl_as) afl_as = getenv("AFL_CXX"); - if (!afl_as) afl_as = "clang"; - - } - -#endif /* __APPLE__ */ - - /* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR - is not set. We need to check these non-standard variables to properly - handle the pass_thru logic later on. */ - - if (!tmp_dir) { tmp_dir = getenv("TEMP"); } - if (!tmp_dir) { tmp_dir = getenv("TMP"); } - if (!tmp_dir) { tmp_dir = "/tmp"; } - - as_params = ck_alloc((argc + 32) * sizeof(u8 *)); - if (unlikely((INT_MAX - 32) < argc || !as_params)) { - - FATAL("Too many parameters passed to as"); - - } - - as_params[0] = afl_as ? afl_as : (u8 *)"as"; - - as_params[argc] = 0; - - /* Find the input file. It's usually located near the end. - Assume there won't be any arguments referring to files after the input - file, e.g. as input.s -o output.o */ - for (input_index = argc - 1; input_index > 0; input_index--) { - - input_file = argv[input_index]; - /* Clang may add debug arguments after the input file. */ - if (strncmp(input_file, "-g", 2)) break; - - } - - if (input_index == 0) - FATAL("Could not find input file (not called through afl-gcc?)"); - - for (i = 1; (s32)i < argc; i++) { - - if (i == input_index) continue; - - if (!strcmp(argv[i], "--64")) { - - use_64bit = 1; - - } else if (!strcmp(argv[i], "--32")) { - - use_64bit = 0; - - } - -#ifdef __APPLE__ - - /* The Apple case is a bit different... */ - - if (!strcmp(argv[i], "-arch") && i + 1 < (u32)argc) { - - if (!strcmp(argv[i + 1], "x86_64")) - use_64bit = 1; - else if (!strcmp(argv[i + 1], "i386")) - FATAL("Sorry, 32-bit Apple platforms are not supported."); - - } - - /* Strip options that set the preference for a particular upstream - assembler in Xcode. */ - - if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) - continue; - -#endif /* __APPLE__ */ - - as_params[as_par_cnt++] = argv[i]; - - } - -#ifdef __APPLE__ - - /* When calling clang as the upstream assembler, append -c -x assembler - and hope for the best. */ - - if (use_clang_as) { - - as_params[as_par_cnt++] = "-c"; - as_params[as_par_cnt++] = "-x"; - as_params[as_par_cnt++] = "assembler"; - - } - -#endif /* __APPLE__ */ - - if (input_file[0] == '-') { - - if (!strcmp(input_file + 1, "-version")) { - - just_version = 1; - modified_file = input_file; - goto wrap_things_up; - - } - - if (input_file[1]) { - - FATAL("Incorrect use (not called through afl-gcc?)"); - - } else { - - input_file = NULL; - - } - - } else { - - /* Check if this looks like a standard invocation as a part of an attempt - to compile a program, rather than using gcc on an ad-hoc .s file in - a format we may not understand. This works around an issue compiling - NSS. */ - - if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) && - strncmp(input_file, "/var/tmp/", 9) && - strncmp(input_file, "/tmp/", 5) && - getenv("AFL_AS_FORCE_INSTRUMENT") == NULL) { - - pass_thru = 1; - - } else if (getenv("AFL_AS_FORCE_INSTRUMENT")) { - - unsetenv("AFL_AS_FORCE_INSTRUMENT"); - - } - - } - - modified_file = alloc_printf("%s/.afl-%u-%u-%u.s", tmp_dir, (u32)getpid(), - (u32)time(NULL), (u32)random()); - -wrap_things_up: - - as_params[as_par_cnt++] = modified_file; - as_params[as_par_cnt] = NULL; - -} - -/* Process input file, generate modified_file. Insert instrumentation in all - the appropriate places. */ - -static void add_instrumentation(void) { - - static u8 line[MAX_LINE]; - - FILE *inf; - FILE *outf; - s32 outfd; - u32 ins_lines = 0; - - u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, skip_intel = 0, - skip_app = 0, instrument_next = 0; - -#ifdef __APPLE__ - - u8 *colon_pos; - -#endif /* __APPLE__ */ - - if (input_file) { - - inf = fopen(input_file, "r"); - if (!inf) { PFATAL("Unable to read '%s'", input_file); } - - } else { - - inf = stdin; - - } - - outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, DEFAULT_PERMISSION); - - if (outfd < 0) { PFATAL("Unable to write to '%s'", modified_file); } - - outf = fdopen(outfd, "w"); - - if (!outf) { PFATAL("fdopen() failed"); } - - while (fgets(line, MAX_LINE, inf)) { - - /* In some cases, we want to defer writing the instrumentation trampoline - until after all the labels, macros, comments, etc. If we're in this - mode, and if the line starts with a tab followed by a character, dump - the trampoline now. */ - - if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && - instrument_next && line[0] == '\t' && isalpha(line[1])) { - - fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, - R(MAP_SIZE)); - - instrument_next = 0; - ins_lines++; - - } - - /* Output the actual line, call it a day in pass-thru mode. */ - - fputs(line, outf); - - if (pass_thru) { continue; } - - /* All right, this is where the actual fun begins. For one, we only want to - instrument the .text section. So, let's keep track of that in processed - files - and let's set instr_ok accordingly. */ - - if (line[0] == '\t' && line[1] == '.') { - - /* OpenBSD puts jump tables directly inline with the code, which is - a bit annoying. They use a specific format of p2align directives - around them, so we use that as a signal. */ - - if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) && - isdigit(line[10]) && line[11] == '\n') { - - skip_next_label = 1; - - } - - if (!strncmp(line + 2, "text\n", 5) || - !strncmp(line + 2, "section\t.text", 13) || - !strncmp(line + 2, "section\t__TEXT,__text", 21) || - !strncmp(line + 2, "section __TEXT,__text", 21)) { - - instr_ok = 1; - continue; - - } - - if (!strncmp(line + 2, "section\t", 8) || - !strncmp(line + 2, "section ", 8) || !strncmp(line + 2, "bss\n", 4) || - !strncmp(line + 2, "data\n", 5)) { - - instr_ok = 0; - continue; - - } - - } - - /* Detect off-flavor assembly (rare, happens in gdb). When this is - encountered, we set skip_csect until the opposite directive is - seen, and we do not instrument. */ - - if (strstr(line, ".code")) { - - if (strstr(line, ".code32")) { skip_csect = use_64bit; } - if (strstr(line, ".code64")) { skip_csect = !use_64bit; } - - } - - /* Detect syntax changes, as could happen with hand-written assembly. - Skip Intel blocks, resume instrumentation when back to AT&T. */ - - if (strstr(line, ".intel_syntax")) { skip_intel = 1; } - if (strstr(line, ".att_syntax")) { skip_intel = 0; } - - /* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */ - - if (line[0] == '#' || line[1] == '#') { - - if (strstr(line, "#APP")) { skip_app = 1; } - if (strstr(line, "#NO_APP")) { skip_app = 0; } - - } - - /* If we're in the right mood for instrumenting, check for function - names or conditional labels. This is a bit messy, but in essence, - we want to catch: - - ^main: - function entry point (always instrumented) - ^.L0: - GCC branch label - ^.LBB0_0: - clang branch label (but only in clang mode) - ^\tjnz foo - conditional branches - - ...but not: - - ^# BB#0: - clang comments - ^ # BB#0: - ditto - ^.Ltmp0: - clang non-branch labels - ^.LC0 - GCC non-branch labels - ^.LBB0_0: - ditto (when in GCC mode) - ^\tjmp foo - non-conditional jumps - - Additionally, clang and GCC on MacOS X follow a different convention - with no leading dots on labels, hence the weird maze of #ifdefs - later on. - - */ - - if (skip_intel || skip_app || skip_csect || !instr_ok || line[0] == '#' || - line[0] == ' ') { - - continue; - - } - - /* Conditional branch instruction (jnz, etc). We append the instrumentation - right after the branch (to instrument the not-taken path) and at the - branch destination label (handled later on). */ - - if (line[0] == '\t') { - - if (line[1] == 'j' && line[2] != 'm' && R(100) < (long)inst_ratio) { - - fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, - R(MAP_SIZE)); - - ins_lines++; - - } - - continue; - - } - - /* Label of some sort. This may be a branch destination, but we need to - read carefully and account for several different formatting - conventions. */ - -#ifdef __APPLE__ - - /* Apple: L: */ - - if ((colon_pos = strstr(line, ":"))) { - - if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { - -#else - - /* Everybody else: .L: */ - - if (strstr(line, ":")) { - - if (line[0] == '.') { - -#endif /* __APPLE__ */ - - /* .L0: or LBB0_0: style jump destination */ - -#ifdef __APPLE__ - - /* Apple: L / LBB */ - - if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) && - R(100) < (long)inst_ratio) { - -#else - - /* Apple: .L / .LBB */ - - if ((isdigit(line[2]) || - (clang_mode && !strncmp(line + 1, "LBB", 3))) && - R(100) < (long)inst_ratio) { - -#endif /* __APPLE__ */ - - /* An optimization is possible here by adding the code only if the - label is mentioned in the code in contexts other than call / jmp. - That said, this complicates the code by requiring two-pass - processing (messy with stdin), and results in a speed gain - typically under 10%, because compilers are generally pretty good - about not generating spurious intra-function jumps. - - We use deferred output chiefly to avoid disrupting - .Lfunc_begin0-style exception handling calculations (a problem on - MacOS X). */ - - if (!skip_next_label) { - - instrument_next = 1; - - } else { - - skip_next_label = 0; - - } - - } - - } else { - - /* Function label (always instrumented, deferred mode). */ - - instrument_next = 1; - - } - - } - - } - - if (ins_lines) { fputs(use_64bit ? main_payload_64 : main_payload_32, outf); } - - if (input_file) { fclose(inf); } - fclose(outf); - - if (!be_quiet) { - - if (!ins_lines) { - - WARNF("No instrumentation targets found%s.", - pass_thru ? " (pass-thru mode)" : ""); - - } else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_TSAN") ? ", TSAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : "", - getenv("AFL_USE_LSAN") ? ", LSAN" : ""); - - OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", ins_lines, - use_64bit ? "64" : "32", modeline, inst_ratio); - - } - - } - -} - -/* Main entry point */ - -int main(int argc, char **argv) { - - s32 pid; - u32 rand_seed, i, j; - int status; - u8 *inst_ratio_str = getenv("AFL_INST_RATIO"); - - struct timeval tv; - struct timezone tz; - - clang_mode = !!getenv(CLANG_ENV_VAR); - - if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { - - SAYF(cCYA "afl-as" VERSION cRST " by Michal Zalewski\n"); - - } else { - - be_quiet = 1; - - } - - if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { - - fprintf( - stdout, - "afl-as" VERSION - " by Michal Zalewski\n" - "\n%s [-h]\n\n" - "This is a helper application for afl-fuzz. It is a wrapper around GNU " - "'as',\n" - "executed by the toolchain whenever using afl-gcc or afl-clang. You " - "probably\n" - "don't want to run this program directly.\n\n" - - "Rarely, when dealing with extremely complex projects, it may be " - "advisable\n" - "to set AFL_INST_RATIO to a value less than 100 in order to reduce " - "the\n" - "odds of instrumenting every discovered branch.\n\n" - "Environment variables used:\n" - "AFL_AS: path to assembler to use for instrumented files\n" - "AFL_CC: fall back path to assembler\n" - "AFL_CXX: fall back path to assembler\n" - "TMPDIR: directory to use for temporary files\n" - "TEMP: fall back path to directory for temporary files\n" - "TMP: fall back path to directory for temporary files\n" - "AFL_INST_RATIO: user specified instrumentation ratio\n" - "AFL_QUIET: suppress verbose output\n" - "AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n" - "AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n" - "AFL_HARDEN, AFL_USE_ASAN, AFL_USE_MSAN, AFL_USE_UBSAN, AFL_USE_LSAN:\n" - " used in the instrumentation summary message\n", - argv[0]); - - exit(1); - - } - - gettimeofday(&tv, &tz); - - rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); - // in fast systems where pids can repeat in the same seconds we need this - for (i = 1; (s32)i < argc; i++) - for (j = 0; j < strlen(argv[i]); j++) - rand_seed += argv[i][j]; - - srandom(rand_seed); - - edit_params(argc, argv); - - if (inst_ratio_str) { - - if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) { - - FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)"); - - } - - } - - if (getenv(AS_LOOP_ENV_VAR)) { - - FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); - - } - - setenv(AS_LOOP_ENV_VAR, "1", 1); - - /* When compiling with ASAN, we don't have a particularly elegant way to skip - ASAN-specific branches. But we can probabilistically compensate for - that... */ - - if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { - - sanitizer = 1; - if (!getenv("AFL_INST_RATIO")) { inst_ratio /= 3; } - - } - - if (!just_version) { add_instrumentation(); } - - if (!(pid = fork())) { - - execvp(as_params[0], (char **)as_params); - FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); - - } - - if (pid < 0) { PFATAL("fork() failed"); } - - if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); } - - if (!getenv("AFL_KEEP_ASSEMBLY")) { unlink(modified_file); } - - exit(WEXITSTATUS(status)); - -} - -- 2.34.1 From 412c1ffe17c6ceed6ae77e4f2e724fa690c3635d Mon Sep 17 00:00:00 2001 From: ph2ocwf5z <1781919720@qq.com> Date: Tue, 22 Oct 2024 02:06:56 +0800 Subject: [PATCH 2/4] Delete 'src/AFLplusplus-stable/src/afl-cc.c' --- src/AFLplusplus-stable/src/afl-cc.c | 3545 --------------------------- 1 file changed, 3545 deletions(-) delete mode 100644 src/AFLplusplus-stable/src/afl-cc.c diff --git a/src/AFLplusplus-stable/src/afl-cc.c b/src/AFLplusplus-stable/src/afl-cc.c deleted file mode 100644 index 7afab85..0000000 --- a/src/AFLplusplus-stable/src/afl-cc.c +++ /dev/null @@ -1,3545 +0,0 @@ -/* - american fuzzy lop++ - compiler instrumentation wrapper - ------------------------------------------------------- - - Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2024 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - https://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define AFL_MAIN - -#ifndef _GNU_SOURCE - #define _GNU_SOURCE 1 -#endif - -#include "common.h" -#include "config.h" -#include "types.h" -#include "debug.h" -#include "alloc-inl.h" -#include "llvm-alternative-coverage.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if (LLVM_MAJOR - 0 == 0) - #undef LLVM_MAJOR -#endif -#if !defined(LLVM_MAJOR) - #define LLVM_MAJOR 0 -#endif -#if (LLVM_MINOR - 0 == 0) - #undef LLVM_MINOR -#endif -#if !defined(LLVM_MINOR) - #define LLVM_MINOR 0 -#endif - -#ifndef MAX_PARAMS_NUM - #define MAX_PARAMS_NUM 2048 -#endif - -/** Global declarations -----BEGIN----- **/ - -typedef enum { - - PARAM_MISS, // not matched - PARAM_SCAN, // scan only - PARAM_KEEP, // kept as-is - PARAM_DROP, // ignored - -} param_st; - -typedef enum { - - INSTRUMENT_DEFAULT = 0, - INSTRUMENT_CLASSIC = 1, - INSTRUMENT_AFL = 1, - INSTRUMENT_PCGUARD = 2, - INSTRUMENT_CFG = 3, - INSTRUMENT_LTO = 4, - INSTRUMENT_LLVMNATIVE = 5, - INSTRUMENT_GCC = 6, - INSTRUMENT_CLANG = 7, - INSTRUMENT_OPT_CTX = 8, - INSTRUMENT_OPT_NGRAM = 16, - INSTRUMENT_OPT_CALLER = 32, - INSTRUMENT_OPT_CTX_K = 64, - INSTRUMENT_OPT_CODECOV = 128, - -} instrument_mode_id; - -typedef enum { - - UNSET = 0, - LTO = 1, - LLVM = 2, - GCC_PLUGIN = 3, - GCC = 4, - CLANG = 5 - -} compiler_mode_id; - -static u8 cwd[4096]; - -char instrument_mode_string[18][18] = { - - "DEFAULT", - "CLASSIC", - "PCGUARD", - "CFG", - "LTO", - "PCGUARD-NATIVE", - "GCC", - "CLANG", - "CTX", - "CALLER", - "", - "", - "", - "", - "", - "", - "NGRAM", - "" - -}; - -char compiler_mode_string[7][12] = { - - "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", - "GCC", "CLANG", "" - -}; - -u8 *instrument_mode_2str(instrument_mode_id i) { - - return instrument_mode_string[i]; - -} - -u8 *compiler_mode_2str(compiler_mode_id i) { - - return compiler_mode_string[i]; - -} - -u8 *getthecwd() { - - if (getcwd(cwd, sizeof(cwd)) == NULL) { - - static u8 fail[] = ""; - return fail; - - } - - return cwd; - -} - -typedef struct aflcc_state { - - u8 **cc_params; /* Parameters passed to the real CC */ - u32 cc_par_cnt; /* Param count, including argv0 */ - - u8 *argv0; /* Original argv0 (by strdup) */ - u8 *callname; /* Executable file argv0 indicated */ - - u8 debug; - - u8 compiler_mode, plusplus_mode, lto_mode; - - u8 *lto_flag; - - u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k; - - u8 cmplog_mode; - - u8 have_instr_env, have_gcc, have_clang, have_llvm, have_gcc_plugin, have_lto, - have_optimized_pcguard, have_instr_list; - - u8 fortify_set, x_set, bit_mode, preprocessor_only, have_unroll, have_o, - have_pic, have_c, shared_linking, partial_linking, non_dash, have_fp, - have_flto, have_hidden, have_fortify, have_fcf, have_staticasan, - have_rust_asanrt, have_asan, have_msan, have_ubsan, have_lsan, have_tsan, - have_cfisan; - - // u8 *march_opt; - u8 need_aflpplib; - int passthrough; - - u8 use_stdin; /* dummy */ - u8 *argvnull; /* dummy */ - -} aflcc_state_t; - -void aflcc_state_init(aflcc_state_t *, u8 *argv0); - -u8 *find_object(aflcc_state_t *, u8 *obj); - -void find_built_deps(aflcc_state_t *); - -/* Insert param into the new argv, raise error if MAX_PARAMS_NUM exceeded. */ -static inline void insert_param(aflcc_state_t *aflcc, u8 *param) { - - if (unlikely(aflcc->cc_par_cnt + 1 >= MAX_PARAMS_NUM)) - FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM."); - - aflcc->cc_params[aflcc->cc_par_cnt++] = param; - -} - -/* - Insert a param which contains path to the object file. It uses find_object to - get the path based on the name `obj`, and then uses a sprintf like method to - format it with `fmt`. If `fmt` is NULL, the inserted arg is same as the path. - If `msg` provided, it should be an error msg raised if the path can't be - found. `obj` must not be NULL. -*/ -static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt, - u8 *msg) { - - u8 *_obj_path = find_object(aflcc, obj); - if (!_obj_path) { - - if (msg) - FATAL("%s", msg); - else - FATAL("Unable to find '%s'", obj); - - } else { - - if (fmt) { - - u8 *_obj_path_fmt = alloc_printf(fmt, _obj_path); - ck_free(_obj_path); - aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path_fmt; - - } else { - - aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path; - - } - - } - -} - -/* Insert params into the new argv, make clang load the pass. */ -static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) { - -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - insert_param(aflcc, "-fexperimental-new-pass-manager"); - #endif - insert_object(aflcc, pass, "-fpass-plugin=%s", 0); -#else - insert_param(aflcc, "-Xclang"); - insert_param(aflcc, "-load"); - insert_param(aflcc, "-Xclang"); - insert_object(aflcc, pass, 0, 0); -#endif - -} - -static inline void debugf_args(int argc, char **argv) { - - DEBUGF("cd '%s';", getthecwd()); - for (int i = 0; i < argc; i++) - SAYF(" '%s'", argv[i]); - SAYF("\n"); - fflush(stdout); - fflush(stderr); - -} - -void compiler_mode_by_callname(aflcc_state_t *); -void compiler_mode_by_environ(aflcc_state_t *); -void compiler_mode_by_cmdline(aflcc_state_t *, int argc, char **argv); -void instrument_mode_by_environ(aflcc_state_t *); -void mode_final_checkout(aflcc_state_t *, int argc, char **argv); -void mode_notification(aflcc_state_t *); - -void add_real_argv0(aflcc_state_t *); - -void add_defs_common(aflcc_state_t *); -void add_defs_selective_instr(aflcc_state_t *); -void add_defs_persistent_mode(aflcc_state_t *); -void add_defs_fortify(aflcc_state_t *, u8); -void add_defs_lsan_ctrl(aflcc_state_t *); - -param_st parse_fsanitize(aflcc_state_t *, u8 *, u8); -void add_sanitizers(aflcc_state_t *, char **envp); -void add_optimized_pcguard(aflcc_state_t *); -void add_native_pcguard(aflcc_state_t *); - -void add_assembler(aflcc_state_t *); -void add_gcc_plugin(aflcc_state_t *); - -param_st parse_misc_params(aflcc_state_t *, u8 *, u8); -void add_misc_params(aflcc_state_t *); - -param_st parse_linking_params(aflcc_state_t *, u8 *, u8, u8 *skip_next, - char **argv); - -void add_lto_linker(aflcc_state_t *); -void add_lto_passes(aflcc_state_t *); -void add_runtime(aflcc_state_t *); - -/** Global declarations -----END----- **/ - -/* - Init global state struct. We also extract the callname, - check debug options and if in C++ mode here. -*/ -void aflcc_state_init(aflcc_state_t *aflcc, u8 *argv0) { - - // Default NULL/0 is a good start - memset(aflcc, 0, sizeof(aflcc_state_t)); - - aflcc->cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *)); - aflcc->cc_par_cnt = 1; - - aflcc->lto_flag = AFL_CLANG_FLTO; - - // aflcc->march_opt = CFLAGS_OPT; - - /* callname & if C++ mode */ - - aflcc->argv0 = ck_strdup(argv0); - - char *cname = NULL; - - if ((cname = strrchr(aflcc->argv0, '/')) != NULL) { - - cname++; - - } else { - - cname = aflcc->argv0; - - } - - aflcc->callname = cname; - - if (strlen(cname) > 2 && (strncmp(cname + strlen(cname) - 2, "++", 2) == 0 || - strstr(cname, "-g++") != NULL)) { - - aflcc->plusplus_mode = 1; - - } - - /* debug */ - - if (getenv("AFL_DEBUG")) { - - aflcc->debug = 1; - if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); - - } else if (getenv("AFL_QUIET")) { - - be_quiet = 1; - - } - - if ((getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) && (!aflcc->debug)) { - - be_quiet = 1; - - } - -} - -/* - Try to find a specific runtime we need, in here: - - 1. firstly we check the $AFL_PATH environment variable location if set - 2. next we check argv[0] if it has path information and use it - a) we also check ../lib/afl - 3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and - FreeBSD with procfs) - a) and check here in ../lib/afl too - 4. we look into the AFL_PATH define (usually /usr/local/lib/afl) - 5. we finally try the current directory - - if all these attempts fail - we return NULL and the caller has to decide - what to do. Otherwise the path to obj would be allocated and returned. -*/ -u8 *find_object(aflcc_state_t *aflcc, u8 *obj) { - - u8 *argv0 = aflcc->argv0; - - u8 *afl_path = getenv("AFL_PATH"); - u8 *slash = NULL, *tmp; - - if (afl_path) { - - tmp = alloc_printf("%s/%s", afl_path, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - - } - - if (argv0) { - - slash = strrchr(argv0, '/'); - - if (slash) { - - u8 *dir = ck_strdup(argv0); - - slash = strrchr(dir, '/'); - *slash = 0; - - tmp = alloc_printf("%s/%s", dir, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { - - ck_free(dir); - return tmp; - - } - - ck_free(tmp); - tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { - - ck_free(dir); - return tmp; - - } - - ck_free(tmp); - ck_free(dir); - - } - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) || \ - defined(__ANDROID__) || defined(__NetBSD__) - #define HAS_PROC_FS 1 -#endif -#ifdef HAS_PROC_FS - else { - - char *procname = NULL; - #if defined(__FreeBSD__) || defined(__DragonFly__) - procname = "/proc/curproc/file"; - #elif defined(__linux__) || defined(__ANDROID__) - procname = "/proc/self/exe"; - #elif defined(__NetBSD__) - procname = "/proc/curproc/exe"; - #endif - if (procname) { - - char exepath[PATH_MAX]; - ssize_t exepath_len = readlink(procname, exepath, sizeof(exepath)); - if (exepath_len > 0 && exepath_len < PATH_MAX) { - - exepath[exepath_len] = 0; - slash = strrchr(exepath, '/'); - - if (slash) { - - *slash = 0; - tmp = alloc_printf("%s/%s", exepath, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - - } - - } - - } - - } - -#endif -#undef HAS_PROC_FS - - } - - tmp = alloc_printf("%s/%s", AFL_PATH, obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - tmp = alloc_printf("./%s", obj); - - if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { return tmp; } - - ck_free(tmp); - - if (aflcc->debug) DEBUGF("Trying ... giving up\n"); - - return NULL; - -} - -/* - Deduce some info about compiler toolchains in current system, - from the building results of AFL++ -*/ -void find_built_deps(aflcc_state_t *aflcc) { - - char *ptr = NULL; - -#if defined(__x86_64__) || defined(__i386__) - if ((ptr = find_object(aflcc, "afl-as")) != NULL) { - - #ifndef __APPLE__ - // on OSX clang masquerades as GCC - aflcc->have_gcc = 1; - #endif - aflcc->have_clang = 1; - ck_free(ptr); - - } - -#endif - - if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) { - - aflcc->have_optimized_pcguard = 1; - ck_free(ptr); - - } - -#if (LLVM_MAJOR >= 3) - - if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) { - - aflcc->have_lto = 1; - ck_free(ptr); - - } - - if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) { - - aflcc->have_llvm = 1; - ck_free(ptr); - - } - -#endif - -#ifdef __ANDROID__ - aflcc->have_llvm = 1; -#endif - - if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) { - - aflcc->have_gcc_plugin = 1; - ck_free(ptr); - - } - -#if !defined(__ANDROID__) && !defined(ANDROID) - ptr = find_object(aflcc, "afl-compiler-rt.o"); - - if (!ptr) { - - FATAL( - "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " - "environment variable."); - - } - - if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); } - - ck_free(ptr); -#endif - -} - -/** compiler_mode & instrument_mode selecting -----BEGIN----- **/ - -/* Select compiler_mode by callname, such as "afl-clang-fast", etc. */ -void compiler_mode_by_callname(aflcc_state_t *aflcc) { - - if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) { - - /* afl-clang-fast is always created there by makefile - just like afl-clang, burdened with special purposes: - - If llvm-config is not available (i.e. LLVM_MAJOR is 0), - or too old, it falls back to LLVM-NATIVE mode and let - the actual compiler complain if doesn't work. - - Otherwise try default llvm instruments except LTO. - */ -#if (LLVM_MAJOR >= 3) - aflcc->compiler_mode = LLVM; -#else - aflcc->compiler_mode = CLANG; -#endif - - } else - -#if (LLVM_MAJOR >= 3) - - if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 || - - strncmp(aflcc->callname, "afl-lto", 7) == 0) { - - aflcc->compiler_mode = LTO; - - } else - -#endif - - if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 || - - strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) { - - aflcc->compiler_mode = GCC_PLUGIN; - - } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 || - - strncmp(aflcc->callname, "afl-g++", 7) == 0) { - - aflcc->compiler_mode = GCC; - - } else if (strcmp(aflcc->callname, "afl-clang") == 0 || - - strcmp(aflcc->callname, "afl-clang++") == 0) { - - aflcc->compiler_mode = CLANG; - - } - -} - -/* - Select compiler_mode by env AFL_CC_COMPILER. And passthrough mode can be - regarded as a special compiler_mode, so we check for it here, too. -*/ -void compiler_mode_by_environ(aflcc_state_t *aflcc) { - - if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { - - aflcc->passthrough = 1; - - } - - char *ptr = getenv("AFL_CC_COMPILER"); - - if (!ptr) { return; } - - if (aflcc->compiler_mode) { - - if (!be_quiet) { - - WARNF( - "\"AFL_CC_COMPILER\" is set but a specific compiler was already " - "selected by command line parameter or symlink, ignoring the " - "environment variable!"); - - } - - } else { - - if (strncasecmp(ptr, "LTO", 3) == 0) { - - aflcc->compiler_mode = LTO; - - } else if (strncasecmp(ptr, "LLVM", 4) == 0) { - - aflcc->compiler_mode = LLVM; - - } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || - - strncasecmp(ptr, "GCC-P", 5) == 0 || - strncasecmp(ptr, "GCCP", 4) == 0) { - - aflcc->compiler_mode = GCC_PLUGIN; - - } else if (strcasecmp(ptr, "GCC") == 0) { - - aflcc->compiler_mode = GCC; - - } else if (strcasecmp(ptr, "CLANG") == 0) { - - aflcc->compiler_mode = CLANG; - - } else - - FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); - - } - -} - -/* - Select compiler_mode by command line options --afl-... - If it can be inferred, instrument_mode would also be set. - This can supersedes previous result based on callname - or AFL_CC_COMPILER. And "--afl_noopt"/"--afl-noopt" will - be overwritten by "-g". -*/ -void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) { - - char *ptr = NULL; - - for (int i = 1; i < argc; i++) { - - if (strncmp(argv[i], "--afl", 5) == 0) { - - if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) { - - aflcc->passthrough = 1; - argv[i] = "-g"; // we have to overwrite it, -g is always good - continue; - - } - - if (aflcc->compiler_mode && !be_quiet) { - - WARNF( - "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " - "symlink compiler selection!"); - - } - - ptr = argv[i]; - ptr += 5; - while (*ptr == '-') - ptr++; - - if (strncasecmp(ptr, "LTO", 3) == 0) { - - aflcc->compiler_mode = LTO; - - } else if (strncasecmp(ptr, "LLVM", 4) == 0) { - - aflcc->compiler_mode = LLVM; - - } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || - - strncasecmp(ptr, "PC-GUARD", 8) == 0) { - - aflcc->compiler_mode = LLVM; - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - - } else if (strcasecmp(ptr, "INSTRIM") == 0 || - - strcasecmp(ptr, "CFG") == 0) { - - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and " - "PCGUARD (default in afl-cc).\n"); - - } else if (strcasecmp(ptr, "AFL") == 0 || - - strcasecmp(ptr, "CLASSIC") == 0) { - - aflcc->compiler_mode = LLVM; - aflcc->instrument_mode = INSTRUMENT_CLASSIC; - - } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || - - strcasecmp(ptr, "NATIVE") == 0 || - strcasecmp(ptr, "LLVM-NATIVE") == 0) { - - aflcc->compiler_mode = LLVM; - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || - - strncasecmp(ptr, "GCC-P", 5) == 0 || - strncasecmp(ptr, "GCCP", 4) == 0) { - - aflcc->compiler_mode = GCC_PLUGIN; - - } else if (strcasecmp(ptr, "GCC") == 0) { - - aflcc->compiler_mode = GCC; - - } else if (strncasecmp(ptr, "CLANG", 5) == 0) { - - aflcc->compiler_mode = CLANG; - - } else - - FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); - - } - - } - -} - -/* - Select instrument_mode by those envs in old style: - - USE_TRACE_PC, AFL_USE_TRACE_PC, AFL_LLVM_USE_TRACE_PC, AFL_TRACE_PC - - AFL_LLVM_CALLER, AFL_LLVM_CTX, AFL_LLVM_CTX_K - - AFL_LLVM_NGRAM_SIZE -*/ -static void instrument_mode_old_environ(aflcc_state_t *aflcc) { - - if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || - getenv("INSTRIM_LIB")) { - - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD " - "(default in afl-cc).\n"); - - } - - if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || - getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { - - if (aflcc->instrument_mode == 0) - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - else if (aflcc->instrument_mode != INSTRUMENT_PCGUARD) - FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); - - } - - if (getenv("AFL_LLVM_CTX")) aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; - if (getenv("AFL_LLVM_CALLER") || getenv("AFL_LLVM_LTO_CALLER") || - getenv("AFL_LLVM_LTO_CTX")) - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - - if (getenv("AFL_LLVM_NGRAM_SIZE")) { - - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; - aflcc->ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); - if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) - FATAL( - "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " - "(%u)", - NGRAM_SIZE_MAX); - - } - - if (getenv("AFL_LLVM_CTX_K")) { - - aflcc->ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); - if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) - FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", - CTX_MAX_K); - if (aflcc->ctx_k == 1) { - - setenv("AFL_LLVM_CALLER", "1", 1); - unsetenv("AFL_LLVM_CTX_K"); - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - - } else { - - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; - - } - - } - -} - -/* - Select instrument_mode by env 'AFL_LLVM_INSTRUMENT'. - Previous compiler_mode will be superseded, if required by some - values of instrument_mode. -*/ -static void instrument_mode_new_environ(aflcc_state_t *aflcc) { - - if (!getenv("AFL_LLVM_INSTRUMENT")) { return; } - - u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); - - while (ptr2) { - - if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || - strncasecmp(ptr2, "classic", strlen("classic")) == 0) { - - if (aflcc->instrument_mode == INSTRUMENT_LTO) { - - aflcc->instrument_mode = INSTRUMENT_CLASSIC; - aflcc->lto_mode = 1; - - } else if (!aflcc->instrument_mode || - - aflcc->instrument_mode == INSTRUMENT_AFL) { - - aflcc->instrument_mode = INSTRUMENT_AFL; - - } else { - - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - } - - if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { - - if (!aflcc->instrument_mode || - aflcc->instrument_mode == INSTRUMENT_PCGUARD) - - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || - strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 || - strncasecmp(ptr2, "native", strlen("native")) == 0) { - - if (!aflcc->instrument_mode || - aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) - - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 || - strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) { - - if (!aflcc->instrument_mode || - aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { - - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CODECOV; - - } else { - - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - } - - if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || - strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { - - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and " - "PCGUARD (default in afl-cc).\n"); - - } - - if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { - - aflcc->lto_mode = 1; - if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO) - - aflcc->instrument_mode = INSTRUMENT_LTO; - - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - if (strcasecmp(ptr2, "gcc") == 0) { - - if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC) - - aflcc->instrument_mode = INSTRUMENT_GCC; - - else if (aflcc->instrument_mode != INSTRUMENT_GCC) - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - aflcc->compiler_mode = GCC; - - } - - if (strcasecmp(ptr2, "clang") == 0) { - - if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG) - - aflcc->instrument_mode = INSTRUMENT_CLANG; - - else if (aflcc->instrument_mode != INSTRUMENT_CLANG) - FATAL("main instrumentation mode already set with %s", - instrument_mode_2str(aflcc->instrument_mode)); - - aflcc->compiler_mode = CLANG; - - } - - if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 || - strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 || - strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) { - - u8 *ptr3 = ptr2; - while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) - ptr3++; - - if (!*ptr3) { - - if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) - FATAL( - "you must set the K-CTX K with (e.g. for value 2) " - "AFL_LLVM_INSTRUMENT=ctx-2"); - - } - - aflcc->ctx_k = atoi(ptr3); - if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) - FATAL( - "K-CTX instrumentation option must be between 1 and CTX_MAX_K " - "(%u)", - CTX_MAX_K); - - if (aflcc->ctx_k == 1) { - - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - setenv("AFL_LLVM_CALLER", "1", 1); - unsetenv("AFL_LLVM_CTX_K"); - - } else { - - aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); - u8 *ptr4 = alloc_printf("%u", aflcc->ctx_k); - setenv("AFL_LLVM_CTX_K", ptr4, 1); - - } - - } - - if (strcasecmp(ptr2, "ctx") == 0) { - - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; - setenv("AFL_LLVM_CTX", "1", 1); - - } - - if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { - - aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - setenv("AFL_LLVM_CALLER", "1", 1); - - } - - if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - - u8 *ptr3 = ptr2 + strlen("ngram"); - while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) { - - ptr3++; - - } - - if (!*ptr3) { - - if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) - FATAL( - "you must set the NGRAM size with (e.g. for value 2) " - "AFL_LLVM_INSTRUMENT=ngram-2"); - - } - - aflcc->ngram_size = atoi(ptr3); - - if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) { - - FATAL( - "NGRAM instrumentation option must be between 2 and " - "NGRAM_SIZE_MAX (%u)", - NGRAM_SIZE_MAX); - - } - - aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - u8 *ptr4 = alloc_printf("%u", aflcc->ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); - - } - - ptr2 = strtok(NULL, ":,;"); - - } - -} - -/* - Select instrument_mode by envs, the top wrapper. We check - have_instr_env firstly, then call instrument_mode_old_environ - and instrument_mode_new_environ sequentially. -*/ -void instrument_mode_by_environ(aflcc_state_t *aflcc) { - - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || - getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || - getenv("AFL_LLVM_BLOCKLIST")) { - - aflcc->have_instr_env = 1; - - } - - if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) { - - WARNF( - "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " - "for file matching, only function matching!"); - - } - - instrument_mode_old_environ(aflcc); - instrument_mode_new_environ(aflcc); - -} - -/* - Workaround to ensure CALLER, CTX, K-CTX and NGRAM - instrumentation were used correctly. -*/ -static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) { - - if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { - - FATAL("you cannot set CTX and CALLER together"); - - } - - if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { - - FATAL("you cannot set CTX and K-CTX together"); - - } - - if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { - - FATAL("you cannot set CALLER and K-CTX together"); - - } - - if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM && - !((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && - aflcc->compiler_mode == LTO)) - FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); - - if (aflcc->instrument_opt_mode && - aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV && - aflcc->instrument_mode != INSTRUMENT_CLASSIC && - !(aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER && - aflcc->compiler_mode == LTO)) - FATAL( - "CALLER, CTX and NGRAM instrumentation options can only be used with " - "the LLVM CLASSIC instrumentation mode."); - -} - -/* - Last step of compiler_mode & instrument_mode selecting. - We have a few of workarounds here, to check any corner cases, - prepare for a series of fallbacks, and raise warnings or errors. -*/ -void mode_final_checkout(aflcc_state_t *aflcc, int argc, char **argv) { - - if (aflcc->instrument_opt_mode && - aflcc->instrument_mode == INSTRUMENT_DEFAULT && - (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == UNSET)) { - - aflcc->instrument_mode = INSTRUMENT_CLASSIC; - aflcc->compiler_mode = LLVM; - - } - - if (!aflcc->compiler_mode) { - - // lto is not a default because outside of afl-cc RANLIB and AR have to - // be set to LLVM versions so this would work - if (aflcc->have_llvm) - aflcc->compiler_mode = LLVM; - else if (aflcc->have_gcc_plugin) - aflcc->compiler_mode = GCC_PLUGIN; - else if (aflcc->have_gcc) - aflcc->compiler_mode = GCC; - else if (aflcc->have_clang) - aflcc->compiler_mode = CLANG; - else if (aflcc->have_lto) - aflcc->compiler_mode = LTO; - else - FATAL("no compiler mode available"); - - } - - switch (aflcc->compiler_mode) { - - case GCC: - if (!aflcc->have_gcc) FATAL("afl-gcc is not available on your platform!"); - break; - case CLANG: - if (!aflcc->have_clang) - FATAL("afl-clang is not available on your platform!"); - break; - case LLVM: - if (!aflcc->have_llvm) - FATAL( - "LLVM mode is not available, please install LLVM 13+ and recompile " - "AFL++"); - break; - case GCC_PLUGIN: - if (!aflcc->have_gcc_plugin) - FATAL( - "GCC_PLUGIN mode is not available, install gcc plugin support and " - "recompile AFL++"); - break; - case LTO: - if (!aflcc->have_lto) - FATAL( - "LTO mode is not available, please install LLVM 13+ and lld of the " - "same version and recompile AFL++"); - break; - default: - FATAL("no compiler mode available"); - - } - - if (aflcc->compiler_mode == GCC) { aflcc->instrument_mode = INSTRUMENT_GCC; } - - if (aflcc->compiler_mode == CLANG) { - - /* if our PCGUARD implementation is not available then silently switch to - native LLVM PCGUARD. Or classic asm instrument is explicitly preferred. */ - if (!aflcc->have_optimized_pcguard && - (aflcc->instrument_mode == INSTRUMENT_DEFAULT || - aflcc->instrument_mode == INSTRUMENT_PCGUARD)) { - - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - } else { - - aflcc->instrument_mode = INSTRUMENT_CLANG; - setenv(CLANG_ENV_VAR, "1", 1); // used by afl-as - - } - - } - - if (aflcc->compiler_mode == LTO) { - - if (aflcc->instrument_mode == 0 || - aflcc->instrument_mode == INSTRUMENT_LTO || - aflcc->instrument_mode == INSTRUMENT_CFG || - aflcc->instrument_mode == INSTRUMENT_PCGUARD) { - - aflcc->lto_mode = 1; - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - - } else if (aflcc->instrument_mode == INSTRUMENT_CLASSIC) { - - aflcc->lto_mode = 1; - - } else { - - if (!be_quiet) { - - WARNF("afl-clang-lto called with mode %s, using that mode instead", - instrument_mode_2str(aflcc->instrument_mode)); - - } - - } - - } - - if (aflcc->instrument_mode == 0 && aflcc->compiler_mode < GCC_PLUGIN) { - -#if LLVM_MAJOR >= 7 - #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) - if (aflcc->have_instr_env) { - - aflcc->instrument_mode = INSTRUMENT_AFL; - if (!be_quiet) { - - WARNF( - "Switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); - - } - - } else - - #endif - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - -#else - aflcc->instrument_mode = INSTRUMENT_AFL; -#endif - - } - - if (!aflcc->instrument_opt_mode && aflcc->lto_mode && - aflcc->instrument_mode == INSTRUMENT_CFG) { - - aflcc->instrument_mode = INSTRUMENT_PCGUARD; - - } - -#ifndef AFL_CLANG_FLTO - if (aflcc->lto_mode) - FATAL( - "instrumentation mode LTO specified but LLVM support not available " - "(requires LLVM 11 or higher)"); -#endif - - if (aflcc->lto_mode) { - - if (aflcc->lto_flag[0] != '-') - FATAL( - "Using afl-clang-lto is not possible because Makefile magic did not " - "identify the correct -flto flag"); - else - aflcc->compiler_mode = LTO; - - } - - if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) - FATAL( - "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " - "together"); - -#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) - - if (aflcc->instrument_mode == INSTRUMENT_PCGUARD && aflcc->have_instr_env) { - - FATAL( - "Instrumentation type PCGUARD does not support " - "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); - - } - -#endif - - instrument_opt_mode_exclude(aflcc); - - u8 *ptr2; - - if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') - FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); - - if (getenv("AFL_LLVM_LAF_ALL")) { - - setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); - setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); - setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); - setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); - - } - - if (getenv("AFL_LLVM_DICT2FILE") && - (getenv("AFL_LLVM_LAF_SPLIT_SWITCHES") || - getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || - getenv("AFL_LLVM_LAF_SPLIT_FLOATS") || - getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES"))) - FATAL("AFL_LLVM_DICT2FILE is incompatible with AFL_LLVM_LAF_*"); - - aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || - getenv("AFL_GCC_CMPLOG"); - -} - -/* - Print welcome message on screen, giving brief notes about - compiler_mode and instrument_mode. -*/ -void mode_notification(aflcc_state_t *aflcc) { - - char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size); - char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k); - - char *ptr1 = alloc_printf( - "%s%s%s%s%s", instrument_mode_2str(aflcc->instrument_mode), - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", - (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); - - ck_free(ptr2); - ck_free(ptr3); - - if ((isatty(2) && !be_quiet) || aflcc->debug) { - - SAYF(cCYA - "afl-cc" VERSION cRST - " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", - compiler_mode_2str(aflcc->compiler_mode), ptr1); - - } - - ck_free(ptr1); - - if (!be_quiet && - (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)) { - - WARNF( - "You are using outdated instrumentation, install LLVM and/or " - "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast " - "instead!"); - - } - -} - -/* - Set argv[0] required by execvp. It can be - - specified by env AFL_CXX - - g++ or clang++ - - CLANGPP_BIN or LLVM_BINDIR/clang++ - when in C++ mode, or - - specified by env AFL_CC - - gcc or clang - - CLANG_BIN or LLVM_BINDIR/clang - otherwise. -*/ -void add_real_argv0(aflcc_state_t *aflcc) { - - static u8 llvm_fullpath[PATH_MAX]; - - if (aflcc->plusplus_mode) { - - u8 *alt_cxx = getenv("AFL_CXX"); - - if (!alt_cxx) { - - if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { - - alt_cxx = "g++"; - - } else if (aflcc->compiler_mode == CLANG) { - - alt_cxx = "clang++"; - - } else { - - if (USE_BINDIR) - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", - LLVM_BINDIR); - else - snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); - alt_cxx = llvm_fullpath; - - } - - } - - aflcc->cc_params[0] = alt_cxx; - - } else { - - u8 *alt_cc = getenv("AFL_CC"); - - if (!alt_cc) { - - if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { - - alt_cc = "gcc"; - - } else if (aflcc->compiler_mode == CLANG) { - - alt_cc = "clang"; - - } else { - - if (USE_BINDIR) - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", - LLVM_BINDIR); - else - snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANG_BIN); - alt_cc = llvm_fullpath; - - } - - } - - aflcc->cc_params[0] = alt_cc; - - } - -} - -/** compiler_mode & instrument_mode selecting -----END----- **/ - -/** Macro defs for the preprocessor -----BEGIN----- **/ - -void add_defs_common(aflcc_state_t *aflcc) { - - insert_param(aflcc, "-D__AFL_COMPILER=1"); - insert_param(aflcc, "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"); - -} - -/* - __afl_coverage macro defs. See - instrumentation/README.instrument_list.md# - 2-selective-instrumentation-with-_afl_coverage-directives -*/ -void add_defs_selective_instr(aflcc_state_t *aflcc) { - - if (aflcc->plusplus_mode) { - - insert_param(aflcc, - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "extern \"C\" void __afl_coverage_discard();" - "extern \"C\" void __afl_coverage_skip();" - "extern \"C\" void __afl_coverage_on();" - "extern \"C\" void __afl_coverage_off();"); - - } else { - - insert_param(aflcc, - "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" - "void __afl_coverage_discard();" - "void __afl_coverage_skip();" - "void __afl_coverage_on();" - "void __afl_coverage_off();"); - - } - - insert_param( - aflcc, - "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " - "1;"); - insert_param(aflcc, "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"); - insert_param(aflcc, "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"); - insert_param(aflcc, "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"); - insert_param(aflcc, "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"); - -} - -/* - Macro defs for persistent mode. As documented in - instrumentation/README.persistent_mode.md, deferred forkserver initialization - and persistent mode are not available in afl-gcc and afl-clang. -*/ -void add_defs_persistent_mode(aflcc_state_t *aflcc) { - - if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) return; - - insert_param(aflcc, "-D__AFL_HAVE_MANUAL_CONTROL=1"); - - /* When the user tries to use persistent or deferred forkserver modes by - appending a single line to the program, we want to reliably inject a - signature into the binary (to be picked up by afl-fuzz) and we want - to call a function from the runtime .o file. This is unnecessarily - painful for three reasons: - - 1) We need to convince the compiler not to optimize out the signature. - This is done with __attribute__((used)). - - 2) We need to convince the linker, when called with -Wl,--gc-sections, - not to do the same. This is done by forcing an assignment to a - 'volatile' pointer. - - 3) We need to declare __afl_persistent_loop() in the global namespace, - but doing this within a method in a class is hard - :: and extern "C" - are forbidden and __attribute__((alias(...))) doesn't work. Hence the - __asm__ aliasing trick. - - */ - - insert_param(aflcc, - "-D__AFL_FUZZ_INIT()=" - "int __afl_sharedmem_fuzzing = 1;" - "extern __attribute__((visibility(\"default\"))) " - "unsigned int *__afl_fuzz_len;" - "extern __attribute__((visibility(\"default\"))) " - "unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1048576];" - "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"); - - insert_param(aflcc, - "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " - "__afl_fuzz_alt_ptr)"); - - insert_param( - aflcc, - "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " - "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " - "? 0 : *__afl_fuzz_len)"); - - insert_param( - aflcc, - "-D__AFL_LOOP(_A)=" - "({ static volatile const char *_B __attribute__((used,unused)); " - " _B = (const char*)\"" PERSIST_SIG - "\"; " - "extern __attribute__((visibility(\"default\"))) int __afl_connected;" -#ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " -#else - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " -#endif /* ^__APPLE__ */ - // if afl is connected, we run _A times, else once. - "_L(__afl_connected ? _A : 1); })"); - - insert_param( - aflcc, - "-D__AFL_INIT()=" - "do { static volatile const char *_A __attribute__((used,unused)); " - " _A = (const char*)\"" DEFER_SIG - "\"; " -#ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"___afl_manual_init\"); " -#else - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"__afl_manual_init\"); " -#endif /* ^__APPLE__ */ - "_I(); } while (0)"); - -} - -/* - Control macro def of _FORTIFY_SOURCE. It will do nothing - if we detect this routine has been called previously, or - the macro already here in these existing args. -*/ -void add_defs_fortify(aflcc_state_t *aflcc, u8 action) { - - if (aflcc->have_fortify) { return; } - - switch (action) { - - case 1: - insert_param(aflcc, "-D_FORTIFY_SOURCE=1"); - break; - - case 2: - insert_param(aflcc, "-D_FORTIFY_SOURCE=2"); - break; - - default: // OFF - insert_param(aflcc, "-U_FORTIFY_SOURCE"); - break; - - } - - aflcc->have_fortify = 1; - -} - -/* Macro defs of __AFL_LEAK_CHECK, __AFL_LSAN_ON and __AFL_LSAN_OFF */ -void add_defs_lsan_ctrl(aflcc_state_t *aflcc) { - - insert_param(aflcc, "-includesanitizer/lsan_interface.h"); - insert_param( - aflcc, - "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) " - "_exit(23); }"); - insert_param(aflcc, "-D__AFL_LSAN_OFF()=__lsan_disable();"); - insert_param(aflcc, "-D__AFL_LSAN_ON()=__lsan_enable();"); - -} - -/** Macro defs for the preprocessor -----END----- **/ - -/** About -fsanitize -----BEGIN----- **/ - -/* For input "-fsanitize=...", it: - - 1. may have various OOB traps :) if ... doesn't contain ',' or - the input has bad syntax such as "-fsantiz=," - 2. strips any fuzzer* in ... and writes back (may result in "-fsanitize=") - 3. rets 1 if exactly "fuzzer" found, otherwise rets 0 -*/ -static u8 fsanitize_fuzzer_comma(char *string) { - - u8 detect_single_fuzzer = 0; - - char *p, *ptr = string + strlen("-fsanitize="); - // ck_alloc will check alloc failure - char *new = ck_alloc(strlen(string) + 1); - char *tmp = ck_alloc(strlen(ptr) + 1); - u32 count = 0, len, ende = 0; - - strcpy(new, "-fsanitize="); - - do { - - p = strchr(ptr, ','); - if (!p) { - - p = ptr + strlen(ptr) + 1; - ende = 1; - - } - - len = p - ptr; - if (len) { - - strncpy(tmp, ptr, len); - tmp[len] = 0; - // fprintf(stderr, "Found: %s\n", tmp); - ptr += len + 1; - if (*tmp) { - - u32 copy = 1; - if (!strcmp(tmp, "fuzzer")) { - - detect_single_fuzzer = 1; - copy = 0; - - } else if (!strncmp(tmp, "fuzzer", 6)) { - - copy = 0; - - } - - if (copy) { - - if (count) { strcat(new, ","); } - strcat(new, tmp); - ++count; - - } - - } - - } else { - - ptr++; - - } - - } while (!ende); - - strcpy(string, new); - - ck_free(tmp); - ck_free(new); - - return detect_single_fuzzer; - -} - -/* - Parse and process possible -fsanitize related args, return PARAM_MISS - if nothing matched. We have 3 main tasks here for these args: - - Check which one of those sanitizers present here. - - Check if libfuzzer present. We need to block the request of enable - libfuzzer, and link harness with our libAFLDriver.a later. - - Check if SanCov allow/denylist options present. We need to try switching - to LLVMNATIVE instead of using our optimized PCGUARD anyway. If we - can't make it finally for various reasons, just drop these options. -*/ -param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { - - param_st final_ = PARAM_MISS; - -// MACRO START -#define HAVE_SANITIZER_SCAN_KEEP(v, k) \ - do { \ - \ - if (strstr(cur_argv, "=" STRINGIFY(k)) || \ - strstr(cur_argv, "," STRINGIFY(k))) { \ - \ - if (scan) { \ - \ - aflcc->have_##v = 1; \ - final_ = PARAM_SCAN; \ - \ - } else { \ - \ - final_ = PARAM_KEEP; \ - \ - } \ - \ - } \ - \ - } while (0) - - // MACRO END - - if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize="))) { - - HAVE_SANITIZER_SCAN_KEEP(asan, address); - HAVE_SANITIZER_SCAN_KEEP(msan, memory); - HAVE_SANITIZER_SCAN_KEEP(ubsan, undefined); - HAVE_SANITIZER_SCAN_KEEP(tsan, thread); - HAVE_SANITIZER_SCAN_KEEP(lsan, leak); - HAVE_SANITIZER_SCAN_KEEP(cfisan, cfi); - - } - -#undef HAVE_SANITIZER_SCAN_KEEP - - // We can't use a "else if" there, because some of the following - // matching rules overlap with those in the if-statement above. - if (!strcmp(cur_argv, "-fsanitize=fuzzer")) { - - if (scan) { - - aflcc->need_aflpplib = 1; - final_ = PARAM_SCAN; - - } else { - - final_ = PARAM_DROP; - - } - - } else if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize=")) && - - strchr(cur_argv, ',') && - !strstr(cur_argv, "=,")) { // avoid OOB errors - - if (scan) { - - u8 *cur_argv_ = ck_strdup(cur_argv); - - if (fsanitize_fuzzer_comma(cur_argv_)) { - - aflcc->need_aflpplib = 1; - final_ = PARAM_SCAN; - - } - - ck_free(cur_argv_); - - } else { - - fsanitize_fuzzer_comma(cur_argv); - if (!cur_argv || strlen(cur_argv) <= strlen("-fsanitize=")) - final_ = PARAM_DROP; // this means it only has "fuzzer" previously. - - } - - } else if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) && - - strstr(cur_argv, "list=")) { - - if (scan) { - - aflcc->have_instr_list = 1; - final_ = PARAM_SCAN; - - } else { - - if (aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE) { - - if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); } - final_ = PARAM_DROP; - - } else { - - final_ = PARAM_KEEP; - - } - - } - - } - - if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); - - return final_; - -} - -/* - Add params for sanitizers. Here we need to consider: - - Use static runtime for asan, as much as possible. - - ASAN, MSAN, AFL_HARDEN are mutually exclusive. - - Add options if not found there, on request of AFL_USE_ASAN, AFL_USE_MSAN, - etc. - - Update have_* so that functions called after this can have correct context. - However this also means any functions called before should NOT depend on - these have_*, otherwise they may not work as expected. -*/ -void add_sanitizers(aflcc_state_t *aflcc, char **envp) { - - if (getenv("AFL_USE_ASAN") || aflcc->have_asan) { - - if (getenv("AFL_USE_MSAN") || aflcc->have_msan) - FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - - if (aflcc->compiler_mode == GCC_PLUGIN && !aflcc->have_staticasan) { - - insert_param(aflcc, "-static-libasan"); - - } - - add_defs_fortify(aflcc, 0); - if (!aflcc->have_asan) { - - insert_param(aflcc, "-fsanitize=address"); - insert_param(aflcc, "-fno-common"); - - } - - aflcc->have_asan = 1; - - } else if (getenv("AFL_USE_MSAN") || aflcc->have_msan) { - - if (getenv("AFL_USE_ASAN") || aflcc->have_asan) - FATAL("ASAN and MSAN are mutually exclusive"); - - if (getenv("AFL_HARDEN")) - FATAL("MSAN and AFL_HARDEN are mutually exclusive"); - - add_defs_fortify(aflcc, 0); - if (!aflcc->have_msan) { insert_param(aflcc, "-fsanitize=memory"); } - aflcc->have_msan = 1; - - } - - if (getenv("AFL_USE_UBSAN") || aflcc->have_ubsan) { - - if (!aflcc->have_ubsan) { - - insert_param(aflcc, "-fsanitize=undefined"); - insert_param(aflcc, "-fsanitize-undefined-trap-on-error"); - insert_param(aflcc, "-fno-sanitize-recover=all"); - - } - - if (!aflcc->have_fp) { - - insert_param(aflcc, "-fno-omit-frame-pointer"); - aflcc->have_fp = 1; - - } - - aflcc->have_ubsan = 1; - - } - - if (getenv("AFL_USE_TSAN") || aflcc->have_tsan) { - - if (!aflcc->have_fp) { - - insert_param(aflcc, "-fno-omit-frame-pointer"); - aflcc->have_fp = 1; - - } - - if (!aflcc->have_tsan) { insert_param(aflcc, "-fsanitize=thread"); } - aflcc->have_tsan = 1; - - } - - if (getenv("AFL_USE_LSAN") && !aflcc->have_lsan) { - - insert_param(aflcc, "-fsanitize=leak"); - add_defs_lsan_ctrl(aflcc); - aflcc->have_lsan = 1; - - } - - if (getenv("AFL_USE_CFISAN") || aflcc->have_cfisan) { - - if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) { - - if (!aflcc->have_fcf) { insert_param(aflcc, "-fcf-protection=full"); } - - } else { - - if (!aflcc->lto_mode && !aflcc->have_flto) { - - uint32_t i = 0, found = 0; - while (envp[i] != NULL && !found) { - - if (strncmp("-flto", envp[i++], 5) == 0) found = 1; - - } - - if (!found) { insert_param(aflcc, "-flto"); } - aflcc->have_flto = 1; - - } - - if (!aflcc->have_cfisan) { insert_param(aflcc, "-fsanitize=cfi"); } - - if (!aflcc->have_hidden) { - - insert_param(aflcc, "-fvisibility=hidden"); - aflcc->have_hidden = 1; - - } - - aflcc->have_cfisan = 1; - - } - - } - -} - -/* Add params to enable LLVM SanCov, the native PCGUARD */ -void add_native_pcguard(aflcc_state_t *aflcc) { - - /* If there is a rust ASan runtime on the command line, it is likely we're - * linking from rust and adding native flags requiring the sanitizer runtime - * will trigger native clang to add yet another runtime, causing linker - * errors. For now we shouldn't add instrumentation here, we're linking - * anyway. - */ - if (aflcc->have_rust_asanrt) { return; } - - /* If llvm-config doesn't figure out LLVM_MAJOR, just - go on anyway and let compiler complain if doesn't work. */ - -#if LLVM_MAJOR > 0 && LLVM_MAJOR < 6 - FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+"); -#else - #if LLVM_MAJOR == 0 - WARNF( - "pcguard instrumentation with pc-table requires LLVM 6.0.1+" - " otherwise the compiler will fail"); - #endif - if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { - - insert_param(aflcc, - "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); - - } else { - - insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard,pc-table"); - - } - -#endif - -} - -/* - Add params to launch our optimized PCGUARD on request. - It will fallback to use the native PCGUARD in some cases. If so, plz - bear in mind that instrument_mode will be set to INSTRUMENT_LLVMNATIVE. -*/ -void add_optimized_pcguard(aflcc_state_t *aflcc) { - -#if LLVM_MAJOR >= 13 - #if defined __ANDROID__ || ANDROID - - insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - #else - - if (aflcc->have_instr_list) { - - if (!be_quiet) - SAYF( - "Using unoptimized trace-pc-guard, due usage of " - "-fsanitize-coverage-allow/denylist, you can use " - "AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST instead.\n"); - - insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - } else { - - /* Since LLVM_MAJOR >= 13 we use new pass manager */ - #if LLVM_MAJOR < 16 - insert_param(aflcc, "-fexperimental-new-pass-manager"); - #endif - insert_object(aflcc, "SanitizerCoveragePCGUARD.so", "-fpass-plugin=%s", 0); - - } - - #endif // defined __ANDROID__ || ANDROID -#else // LLVM_MAJOR < 13 - #if LLVM_MAJOR >= 4 - - if (!be_quiet) - SAYF( - "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for " - "enhanced version.\n"); - insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); - aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - - #else - - FATAL("pcguard instrumentation requires LLVM 4.0.1+"); - - #endif -#endif - -} - -/** About -fsanitize -----END----- **/ - -/** Linking behaviors -----BEGIN----- **/ - -/* - Parse and process possible linking stage related args, - return PARAM_MISS if nothing matched. -*/ -param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan, - u8 *skip_next, char **argv) { - - if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) { - - FATAL( - "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or " - "use afl-clang-fast!"); - - } - - param_st final_ = PARAM_MISS; - - if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) { - - if (scan) { - - aflcc->shared_linking = 1; - final_ = PARAM_SCAN; - - } else { - - final_ = PARAM_KEEP; - - } - - } else if (!strcmp(cur_argv, "-Wl,-r") || !strcmp(cur_argv, "-Wl,-i") || - - !strcmp(cur_argv, "-Wl,--relocatable") || - !strcmp(cur_argv, "-r") || !strcmp(cur_argv, "--relocatable")) { - - if (scan) { - - aflcc->partial_linking = 1; - final_ = PARAM_SCAN; - - } else { - - final_ = PARAM_KEEP; - - } - - } else if (!strncmp(cur_argv, "-fuse-ld=", 9) || - - !strncmp(cur_argv, "--ld-path=", 10)) { - - if (scan) { - - final_ = PARAM_SCAN; - - } else { - - if (aflcc->lto_mode) - final_ = PARAM_DROP; - else - final_ = PARAM_KEEP; - - } - - } else if (!strcmp(cur_argv, "-Wl,-z,defs") || - - !strcmp(cur_argv, "-Wl,--no-undefined") || - !strcmp(cur_argv, "-Wl,-no-undefined") || - !strcmp(cur_argv, "--no-undefined") || - strstr(cur_argv, "afl-compiler-rt") || - strstr(cur_argv, "afl-llvm-rt")) { - - if (scan) { - - final_ = PARAM_SCAN; - - } else { - - final_ = PARAM_DROP; - - } - - } else if (!strcmp(cur_argv, "-z") || !strcmp(cur_argv, "-Wl,-z")) { - - u8 *param = *(argv + 1); - if (param && (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs"))) { - - *skip_next = 1; - - if (scan) { - - final_ = PARAM_SCAN; - - } else { - - final_ = PARAM_DROP; - - } - - } - - } - - // Try to warn user for some unsupported cases - if (scan && final_ == PARAM_MISS) { - - u8 *ptr_ = NULL; - - if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) { - - if (!strcmp(ptr_, "defs")) { - - WARNF("'-Xlinker' 'defs' detected. This may result in a bad link."); - - } else if (strstr(ptr_, "-no-undefined")) { - - WARNF( - "'-Xlinker' '%s' detected. The latter option may be dropped and " - "result in a bad link.", - ptr_); - - } - - } else if (!strncmp(cur_argv, "-Wl,", 4) && - - (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) { - - ptr_ = cur_argv + 4; - - if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) { - - WARNF( - "'%s': multiple link options after '-Wl,' may break shared " - "linking.", - ptr_); - - } - - if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") || - strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) { - - WARNF( - "'%s': multiple link options after '-Wl,' may break partial " - "linking.", - ptr_); - - } - - if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) { - - WARNF( - "'%s': multiple link options after '-Wl,' may enable report " - "unresolved symbol references and result in a bad link.", - ptr_); - - } - - } - - } - - if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); - - return final_; - -} - -/* Add params to specify the linker used in LTO */ -void add_lto_linker(aflcc_state_t *aflcc) { - - unsetenv("AFL_LD"); - unsetenv("AFL_LD_CALLER"); - - u8 *ld_path = NULL; - if (getenv("AFL_REAL_LD")) { - - ld_path = strdup(getenv("AFL_REAL_LD")); - - } else { - - ld_path = strdup(AFL_REAL_LD); - - } - - if (!ld_path || !*ld_path) { - - if (ld_path) { - - // Freeing empty string - free(ld_path); - - } - - ld_path = strdup("ld.lld"); - - } - - if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); } -#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 - insert_param(aflcc, alloc_printf("--ld-path=%s", ld_path)); -#else - insert_param(aflcc, alloc_printf("-fuse-ld=%s", ld_path)); -#endif - free(ld_path); - -} - -/* Add params to launch SanitizerCoverageLTO.so when linking */ -void add_lto_passes(aflcc_state_t *aflcc) { - -#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15 - // The NewPM implementation only works fully since LLVM 15. - insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,--load-pass-plugin=%s", - 0); -#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 - insert_param(aflcc, "-Wl,--lto-legacy-pass-manager"); - insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); -#else - insert_param(aflcc, "-fno-experimental-new-pass-manager"); - insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); -#endif - - insert_param(aflcc, "-Wl,--allow-multiple-definition"); - -} - -/* Add params to link with libAFLDriver.a on request */ -static void add_aflpplib(aflcc_state_t *aflcc) { - - if (!aflcc->need_aflpplib) return; - - u8 *afllib = find_object(aflcc, "libAFLDriver.a"); - - if (!be_quiet) { - - OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a"); - - } - - if (!afllib) { - - if (!be_quiet) { - - WARNF( - "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in " - "the flags - this will fail!"); - - } - - } else { - - insert_param(aflcc, afllib); - -#ifdef __APPLE__ - insert_param(aflcc, "-Wl,-undefined,dynamic_lookup"); -#endif - - } - -} - -/* Add params to link with runtimes depended by our instrumentation */ -void add_runtime(aflcc_state_t *aflcc) { - - if (aflcc->preprocessor_only || aflcc->have_c || !aflcc->non_dash) { - - /* 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. */ - return; - - } - - if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC && - !getenv("AFL_LLVM_NO_RPATH")) { - - // in case LLVM is installed not via a package manager or "make install" - // e.g. compiled download or compiled from github then its ./lib directory - // might not be in the search path. Add it if so. - const char *libdir = LLVM_LIBDIR; - if (aflcc->plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && - strncmp(libdir, "/lib", 4)) { - -#ifdef __APPLE__ - u8 *libdir_opt = strdup("-Wl,-rpath," LLVM_LIBDIR); -#else - u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR); -#endif - insert_param(aflcc, libdir_opt); - - } - - } - -#ifndef __ANDROID__ - - #define M32_ERR_MSG "-m32 is not supported by your compiler" - #define M64_ERR_MSG "-m64 is not supported by your compiler" - - if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) { - - switch (aflcc->bit_mode) { - - case 0: - if (!aflcc->shared_linking && !aflcc->partial_linking) - insert_object(aflcc, "afl-compiler-rt.o", 0, 0); - if (aflcc->lto_mode) insert_object(aflcc, "afl-llvm-rt-lto.o", 0, 0); - break; - - case 32: - if (!aflcc->shared_linking && !aflcc->partial_linking) - insert_object(aflcc, "afl-compiler-rt-32.o", 0, M32_ERR_MSG); - if (aflcc->lto_mode) - insert_object(aflcc, "afl-llvm-rt-lto-32.o", 0, M32_ERR_MSG); - break; - - case 64: - if (!aflcc->shared_linking && !aflcc->partial_linking) - insert_object(aflcc, "afl-compiler-rt-64.o", 0, M64_ERR_MSG); - if (aflcc->lto_mode) - insert_object(aflcc, "afl-llvm-rt-lto-64.o", 0, M64_ERR_MSG); - break; - - } - - #if __AFL_CODE_COVERAGE - // Required for dladdr used in afl-compiler-rt.o - insert_param(aflcc, "-ldl"); - #endif - - #if !defined(__APPLE__) && !defined(__sun) - if (!aflcc->shared_linking && !aflcc->partial_linking) - insert_object(aflcc, "dynamic_list.txt", "-Wl,--dynamic-list=%s", 0); - #endif - - #if defined(__APPLE__) - if (aflcc->shared_linking || aflcc->partial_linking) { - - insert_param(aflcc, "-Wl,-U"); - insert_param(aflcc, "-Wl,___afl_area_ptr"); - insert_param(aflcc, "-Wl,-U"); - insert_param(aflcc, "-Wl,___sanitizer_cov_trace_pc_guard_init"); - - } - - #endif - - } - -#endif - - add_aflpplib(aflcc); - -#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ - insert_param(aflcc, "-Wl,-lrt"); -#endif - -} - -/** Linking behaviors -----END----- **/ - -/** Miscellaneous routines -----BEGIN----- **/ - -/* - Add params to make compiler driver use our afl-as - as assembler, required by the vanilla instrumentation. -*/ -void add_assembler(aflcc_state_t *aflcc) { - - u8 *afl_as = find_object(aflcc, "afl-as"); - - if (!afl_as) FATAL("Cannot find 'afl-as'."); - - u8 *slash = strrchr(afl_as, '/'); - if (slash) *slash = 0; - - // Search for 'as' may be unreliable in some cases (see #2058) - // so use 'afl-as' instead, because 'as' is usually a symbolic link, - // or can be a renamed copy of 'afl-as' created in the same dir. - // Now we should verify if the compiler can find the 'as' we need. - -#define AFL_AS_ERR "(should be a symlink or copy of 'afl-as')" - - u8 *afl_as_dup = alloc_printf("%s/as", afl_as); - - int fd = open(afl_as_dup, O_RDONLY); - if (fd < 0) { PFATAL("Unable to open '%s' " AFL_AS_ERR, afl_as_dup); } - - struct stat st; - if (fstat(fd, &st) < 0) { - - PFATAL("Unable to fstat '%s' " AFL_AS_ERR, afl_as_dup); - - } - - u32 f_len = st.st_size; - - u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); - if (f_data == MAP_FAILED) { - - PFATAL("Unable to mmap file '%s' " AFL_AS_ERR, afl_as_dup); - - } - - close(fd); - - // "AFL_AS" is a const str passed to getenv in afl-as.c - if (!memmem(f_data, f_len, "AFL_AS", strlen("AFL_AS") + 1)) { - - FATAL( - "Looks like '%s' is not a valid symlink or copy of '%s/afl-as'. " - "It is a prerequisite to override system-wide 'as' for " - "instrumentation.", - afl_as_dup, afl_as); - - } - - if (munmap(f_data, f_len)) { PFATAL("unmap() failed"); } - - ck_free(afl_as_dup); - -#undef AFL_AS_ERR - - insert_param(aflcc, "-B"); - insert_param(aflcc, afl_as); - - if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as"); - -} - -/* Add params to launch the gcc plugins for instrumentation. */ -void add_gcc_plugin(aflcc_state_t *aflcc) { - - if (aflcc->cmplog_mode) { - - insert_object(aflcc, "afl-gcc-cmplog-pass.so", "-fplugin=%s", 0); - insert_object(aflcc, "afl-gcc-cmptrs-pass.so", "-fplugin=%s", 0); - - } - - insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0); - - insert_param(aflcc, "-fno-if-conversion"); - insert_param(aflcc, "-fno-if-conversion2"); - -} - -/* Add some miscellaneous params required by our instrumentation. */ -void add_misc_params(aflcc_state_t *aflcc) { - - if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") || - aflcc->lto_mode) { - - insert_param(aflcc, "-fno-builtin-strcmp"); - insert_param(aflcc, "-fno-builtin-strncmp"); - insert_param(aflcc, "-fno-builtin-strcasecmp"); - insert_param(aflcc, "-fno-builtin-strncasecmp"); - insert_param(aflcc, "-fno-builtin-memcmp"); - insert_param(aflcc, "-fno-builtin-bcmp"); - insert_param(aflcc, "-fno-builtin-strstr"); - insert_param(aflcc, "-fno-builtin-strcasestr"); - - } - - if (!aflcc->have_pic) { insert_param(aflcc, "-fPIC"); } - - if (getenv("AFL_HARDEN")) { - - insert_param(aflcc, "-fstack-protector-all"); - - if (!aflcc->fortify_set) add_defs_fortify(aflcc, 2); - - } - - if (!getenv("AFL_DONT_OPTIMIZE")) { - - insert_param(aflcc, "-g"); - if (!aflcc->have_o) insert_param(aflcc, "-O3"); - if (!aflcc->have_unroll) insert_param(aflcc, "-funroll-loops"); - // if (strlen(aflcc->march_opt) > 1 && aflcc->march_opt[0] == '-') - // insert_param(aflcc, aflcc->march_opt); - - } - - if (aflcc->x_set) { - - insert_param(aflcc, "-x"); - insert_param(aflcc, "none"); - - } - -} - -/* - Parse and process a variety of args under our matching rules, - return PARAM_MISS if nothing matched. -*/ -param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { - - param_st final_ = PARAM_MISS; - -// MACRO START -#define SCAN_KEEP(dst, src) \ - do { \ - \ - if (scan) { \ - \ - dst = src; \ - final_ = PARAM_SCAN; \ - \ - } else { \ - \ - final_ = PARAM_KEEP; \ - \ - } \ - \ - } while (0) - - // MACRO END - - if (!strncasecmp(cur_argv, "-fpic", 5)) { - - SCAN_KEEP(aflcc->have_pic, 1); - - } else if (!strcmp(cur_argv, "-m32") || - - !strcmp(cur_argv, "armv7a-linux-androideabi")) { - - SCAN_KEEP(aflcc->bit_mode, 32); - - } else if (!strcmp(cur_argv, "-m64")) { - - SCAN_KEEP(aflcc->bit_mode, 64); - - } else if (strstr(cur_argv, "FORTIFY_SOURCE")) { - - SCAN_KEEP(aflcc->fortify_set, 1); - - } else if (!strcmp(cur_argv, "-x")) { - - SCAN_KEEP(aflcc->x_set, 1); - - } else if (!strcmp(cur_argv, "-E")) { - - SCAN_KEEP(aflcc->preprocessor_only, 1); - - } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) { - - SCAN_KEEP(aflcc->passthrough, 1); - - } else if (!strcmp(cur_argv, "-c")) { - - SCAN_KEEP(aflcc->have_c, 1); - - } else if (!strcmp(cur_argv, "-static-libasan")) { - - SCAN_KEEP(aflcc->have_staticasan, 1); - - } else if (strstr(cur_argv, "librustc") && strstr(cur_argv, "_rt.asan.a")) { - - SCAN_KEEP(aflcc->have_rust_asanrt, 1); - - } else if (!strcmp(cur_argv, "-fno-omit-frame-pointer")) { - - SCAN_KEEP(aflcc->have_fp, 1); - - } else if (!strcmp(cur_argv, "-fvisibility=hidden")) { - - SCAN_KEEP(aflcc->have_hidden, 1); - - } else if (!strcmp(cur_argv, "-flto") || !strcmp(cur_argv, "-flto=full")) { - - SCAN_KEEP(aflcc->have_flto, 1); - - } else if (!strncmp(cur_argv, "-D_FORTIFY_SOURCE", - - strlen("-D_FORTIFY_SOURCE"))) { - - SCAN_KEEP(aflcc->have_fortify, 1); - - } else if (!strncmp(cur_argv, "-fcf-protection", strlen("-fcf-protection"))) { - - SCAN_KEEP(aflcc->have_cfisan, 1); - - } else if (!strncmp(cur_argv, "-O", 2)) { - - SCAN_KEEP(aflcc->have_o, 1); - - } else if (!strncmp(cur_argv, "-funroll-loop", 13)) { - - SCAN_KEEP(aflcc->have_unroll, 1); - - } else if (!strncmp(cur_argv, "--afl", 5)) { - - if (scan) - final_ = PARAM_SCAN; - else - final_ = PARAM_DROP; - - } else if (!strncmp(cur_argv, "-fno-unroll", 11)) { - - if (scan) - final_ = PARAM_SCAN; - else - final_ = PARAM_DROP; - - } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) { - - if (scan) - final_ = PARAM_SCAN; - else - final_ = PARAM_DROP; - - } else if (!strncmp(cur_argv, "-stdlib=", 8) && - - (aflcc->compiler_mode == GCC || - aflcc->compiler_mode == GCC_PLUGIN)) { - - if (scan) { - - final_ = PARAM_SCAN; - - } else { - - if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv); - final_ = PARAM_DROP; - - } - - } else if (cur_argv[0] != '-') { - - /* It's a weak, loose pattern, with very different purpose - than others. We handle it at last, cautiously and robustly. */ - - if (scan && cur_argv[0] != '@') // response file support - aflcc->non_dash = 1; - - } - -#undef SCAN_KEEP - - if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); - - return final_; - -} - -/** Miscellaneous routines -----END----- **/ - -/* Print help message on request */ -static void maybe_usage(aflcc_state_t *aflcc, int argc, char **argv) { - - if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { - - printf("afl-cc" VERSION - " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); - - SAYF( - "\n" - "afl-cc/afl-c++ [options]\n" - "\n" - "This is a helper application for afl-fuzz. It serves as a drop-in " - "replacement\n" - "for gcc and clang, letting you recompile third-party code with the " - "required\n" - "runtime instrumentation. A common use pattern would be one of the " - "following:\n\n" - - " CC=afl-cc CXX=afl-c++ ./configure --disable-shared\n" - " cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ .\n" - " CC=afl-cc CXX=afl-c++ meson\n\n"); - - SAYF( - " |------------- FEATURES " - "-------------|\n" - "MODES: NCC PERSIST DICT LAF " - "CMPLOG SELECT\n" - " [LLVM] LLVM: %s%s\n" - " PCGUARD %s yes yes module yes yes " - "yes\n" - " NATIVE AVAILABLE no yes no no " - "part. yes\n" - " CLASSIC %s no yes module yes yes " - "yes\n" - " - NORMAL\n" - " - CALLER\n" - " - CTX\n" - " - NGRAM-{2-16}\n" - " [LTO] LLVM LTO: %s%s\n" - " PCGUARD DEFAULT yes yes yes yes yes " - " yes\n" - " CLASSIC yes yes yes yes yes " - " yes\n" - " [GCC_PLUGIN] gcc plugin: %s%s\n" - " CLASSIC DEFAULT no yes no no no " - "yes\n" - " [GCC/CLANG] simple gcc/clang: %s%s\n" - " CLASSIC DEFAULT no no no no no " - "no\n\n", - aflcc->have_llvm ? "AVAILABLE " : "unavailable!", - aflcc->compiler_mode == LLVM ? " [SELECTED]" : "", - aflcc->have_llvm ? "AVAILABLE " : "unavailable!", - aflcc->have_llvm ? "AVAILABLE " : "unavailable!", - aflcc->have_lto ? "AVAILABLE" : "unavailable!", - aflcc->compiler_mode == LTO ? " [SELECTED]" : "", - aflcc->have_gcc_plugin ? "AVAILABLE" : "unavailable!", - aflcc->compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", - aflcc->have_gcc && aflcc->have_clang - ? "AVAILABLE" - : (aflcc->have_gcc - ? "GCC ONLY " - : (aflcc->have_clang ? "CLANG ONLY" : "unavailable!")), - (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) - ? " [SELECTED]" - : ""); - - SAYF( - "Modes:\n" - " To select the compiler mode use a symlink version (e.g. " - "afl-clang-fast), set\n" - " the environment variable AFL_CC_COMPILER to a mode (e.g. LLVM) or " - "use the\n" - " command line parameter --afl-MODE (e.g. --afl-llvm). If none is " - "selected,\n" - " afl-cc will select the best available (LLVM -> GCC_PLUGIN -> GCC).\n" - " The best is LTO but it often needs RANLIB and AR settings outside " - "of afl-cc.\n\n"); - -#if LLVM_MAJOR >= 11 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) - #define NATIVE_MSG \ - " LLVM-NATIVE: use llvm's native PCGUARD instrumentation (less " \ - "performant)\n" -#else - #define NATIVE_MSG "" -#endif - - SAYF( - "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " - "available)\n" - " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" - - NATIVE_MSG - - " CLASSIC: decision target instrumentation (README.llvm.md)\n" - " CALLER: CLASSIC + single callee context " - "(instrumentation/README.ctx.md)\n" - " CTX: CLASSIC + full callee context " - "(instrumentation/README.ctx.md)\n" - " NGRAM-x: CLASSIC + previous path " - "((instrumentation/README.ngram.md)\n\n"); - -#undef NATIVE_MSG - - SAYF( - "Features: (see documentation links)\n" - " NCC: non-colliding coverage [automatic] (that is an amazing " - "thing!)\n" - " (instrumentation/README.lto.md)\n" - " PERSIST: persistent mode support [code] (huge speed increase!)\n" - " (instrumentation/README.persistent_mode.md)\n" - " DICT: dictionary in the target [yes=automatic or LLVM module " - "pass]\n" - " (instrumentation/README.lto.md + " - "instrumentation/README.llvm.md)\n" - " LAF: comparison splitting [env] " - "(instrumentation/README.laf-intel.md)\n" - " CMPLOG: input2state exploration [env] " - "(instrumentation/README.cmplog.md)\n" - " SELECT: selective instrumentation (allow/deny) on filename or " - "function [env]\n" - " (instrumentation/README.instrument_list.md)\n\n"); - - if (argc < 2 || strncmp(argv[1], "-hh", 3)) { - - SAYF( - "To see all environment variables for the configuration of afl-cc " - "use \"-hh\".\n"); - - } else { - - SAYF( - "Environment variables used:\n" - " AFL_CC: path to the C compiler to use\n" - " AFL_CXX: path to the C++ compiler to use\n" - " AFL_DEBUG: enable developer debugging output\n" - " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" - " AFL_NO_BUILTIN: no builtins for string compare functions (for " - "libtokencap.so)\n" - " AFL_NOOPT: behave like a normal compiler (to pass configure " - "tests)\n" - " AFL_PATH: path to instrumenting pass and runtime " - "(afl-compiler-rt.*o)\n" - " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" - " AFL_INST_RATIO: percentage of branches to instrument\n" - " AFL_QUIET: suppress verbose output\n" - " AFL_HARDEN: adds code hardening to catch memory bugs\n" - " AFL_USE_ASAN: activate address sanitizer\n" - " AFL_USE_CFISAN: activate control flow sanitizer\n" - " AFL_USE_MSAN: activate memory sanitizer\n" - " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" - " AFL_USE_TSAN: activate thread sanitizer\n" - " AFL_USE_LSAN: activate leak-checker sanitizer\n"); - - if (aflcc->have_gcc_plugin) - SAYF( - "\nGCC Plugin-specific environment variables:\n" - " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" - " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" - " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" - " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " - "filename\n"); - -#if LLVM_MAJOR >= 9 - #define COUNTER_BEHAVIOUR \ - " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" -#else - #define COUNTER_BEHAVIOUR \ - " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" -#endif - if (aflcc->have_llvm) - SAYF( - "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " - "variables:\n" - " AFL_LLVM_THREADSAFE_INST: instrument with thread safe counters, " - "disables neverzero\n" - - COUNTER_BEHAVIOUR - - " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " - "comparisons\n" - " AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the " - "dictionary\n" - " AFL_LLVM_INJECTIONS_ALL: enables all injections hooking\n" - " AFL_LLVM_INJECTIONS_SQL: enables SQL injections hooking\n" - " AFL_LLVM_INJECTIONS_LDAP: enables LDAP injections hooking\n" - " AFL_LLVM_INJECTIONS_XSS: enables XSS injections hooking\n" - " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" - " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" - " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" - " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" - " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" - " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " - "functions\n" - " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " - "instrument allow/\n" - " deny listing (selective instrumentation)\n"); - - if (aflcc->have_llvm) - SAYF( - " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " - "mutator)\n" - " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" - " CLASSIC, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, NGRAM-2 " - "..-16\n" - " You can also use the old environment variables instead:\n" - " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" - " AFL_LLVM_CALLER: use single context sensitive coverage (for " - "CLASSIC)\n" - " AFL_LLVM_CTX: use full context sensitive coverage (for " - "CLASSIC)\n" - " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " - "CLASSIC)\n" - " AFL_LLVM_NO_RPATH: disable rpath setting for custom LLVM " - "locations\n"); - -#ifdef AFL_CLANG_FLTO - if (aflcc->have_lto) - SAYF( - "\nLTO/afl-clang-lto specific environment variables:\n" - " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " - "e.g. " - "0x10000\n" - " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " - "functions\n" - " into this file (LTO mode)\n" - " AFL_LLVM_LTO_CALLER: activate CALLER/CTX instrumentation\n" - " AFL_LLVM_LTO_CALLER_DEPTH: skip how many empty functions\n" - " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " - "global var\n" - " AFL_LLVM_LTO_STARTID: from which ID to start counting from for " - "a bb\n" - " AFL_REAL_LD: use this lld linker instead of the compiled in " - "path\n" - " AFL_LLVM_LTO_SKIPINIT: don't inject initialization code " - "(used in WAFL mode)\n" - "If anything fails - be sure to read README.lto.md!\n"); -#endif - - SAYF( - "\nYou can supply --afl-noopt to not instrument, like AFL_NOOPT. " - "(this is helpful\n" - "in some build systems if you do not want to instrument " - "everything.\n"); - - } - - SAYF( - "\nFor any information on the available instrumentations and options " - "please \n" - "consult the README.md, especially section 3.1 about instrumenting " - "targets.\n\n"); - -#if (LLVM_MAJOR >= 3) - if (aflcc->have_lto) - SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); - if (aflcc->have_llvm) - SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, - LLVM_BINDIR); -#endif - -#ifdef USEMMAP - #if !defined(__HAIKU__) - SAYF("Compiled with shm_open support.\n"); - #else - SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); - #endif -#else - SAYF("Compiled with shmat support.\n"); -#endif - SAYF("\n"); - - SAYF( - "Do not be overwhelmed :) afl-cc uses good defaults if no options are " - "selected.\n" - "Read the documentation for FEATURES though, all are good but few are " - "defaults.\n" - "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " - "with\n" - "AFL_LLVM_CMPLOG and " - "AFL_LLVM_DICT2FILE+AFL_LLVM_DICT2FILE_NO_MAIN.\n\n"); - - if (LLVM_MAJOR < 13) { - - SAYF( - "Warning: It is highly recommended to use at least LLVM version 13 " - "(or better, higher) rather than %d!\n\n", - LLVM_MAJOR); - - } - - exit(1); - - } - -} - -/* - Process params passed to afl-cc. - - We have two working modes, *scan* and *non-scan*. In scan mode, - the main task is to set some variables in aflcc according to current argv[i], - while in non-scan mode, is to choose keep or drop current argv[i]. - - We have several matching routines being called sequentially in the while-loop, - and each of them try to parse and match current argv[i] according to their own - rules. If one miss match, the next will then take over. In non-scan mode, each - argv[i] mis-matched by all the routines will be kept. - - These routines are: - 1. parse_misc_params - 2. parse_fsanitize - 3. parse_linking_params - 4. `if (*cur == '@') {...}`, i.e., parse response files -*/ -static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc, - char **argv) { - - // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]); - - /* Process the argument list. */ - - u8 skip_next = 0; - while (--argc) { - - u8 *cur = *(++argv); - - if (skip_next > 0) { - - skip_next--; - continue; - - } - - if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; - - if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; - - if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) - continue; - - /* Response file support -----BEGIN----- - We have two choices - move everything to the command line or - rewrite the response files to temporary files and delete them - afterwards. We choose the first for easiness. - For clang, llvm::cl::ExpandResponseFiles does this, however it - only has C++ interface. And for gcc there is expandargv in libiberty, - written in C, but we can't simply copy-paste since its LGPL licensed. - So here we use an equivalent FSM as alternative, and try to be compatible - with the two above. See: - - https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html - - driver::expand_at_files in gcc.git/gcc/gcc.c - - expandargv in gcc.git/libiberty/argv.c - - llvm-project.git/clang/tools/driver/driver.cpp - - ExpandResponseFiles in - llvm-project.git/llvm/lib/Support/CommandLine.cpp - */ - if (*cur == '@') { - - u8 *filename = cur + 1; - if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } - - // Check not found or empty? let the compiler complain if so. - FILE *f = fopen(filename, "r"); - if (!f) { - - if (!scan) insert_param(aflcc, cur); - continue; - - } - - struct stat st; - if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) { - - fclose(f); - if (!scan) insert_param(aflcc, cur); - continue; - - } - - // Limit the number of response files, the max value - // just keep consistent with expandargv. Only do this in - // scan mode, and not touch rsp_count anymore in the next. - static u32 rsp_count = 2000; - if (scan) { - - if (rsp_count == 0) FATAL("Too many response files provided!"); - - --rsp_count; - - } - - // argc, argv acquired from this rsp file. Note that - // process_params ignores argv[0], we need to put a const "" here. - u32 argc_read = 1; - char **argv_read = ck_alloc(sizeof(char *)); - argv_read[0] = ""; - - char *arg_buf = NULL; - u64 arg_len = 0; - - enum fsm_state { - - fsm_whitespace, // whitespace seen so far - fsm_double_quote, // have unpaired double quote - fsm_single_quote, // have unpaired single quote - fsm_backslash, // a backslash is seen with no unpaired quote - fsm_normal // a normal char is seen - - }; - - // Workaround to append c to arg buffer, and append the buffer to argv -#define ARG_ALLOC(c) \ - do { \ - \ - ++arg_len; \ - arg_buf = ck_realloc(arg_buf, (arg_len + 1) * sizeof(char)); \ - arg_buf[arg_len] = '\0'; \ - arg_buf[arg_len - 1] = (char)c; \ - \ - } while (0) - -#define ARG_STORE() \ - do { \ - \ - ++argc_read; \ - argv_read = ck_realloc(argv_read, argc_read * sizeof(char *)); \ - argv_read[argc_read - 1] = arg_buf; \ - arg_buf = NULL; \ - arg_len = 0; \ - \ - } while (0) - - int cur_chr = (int)' '; // init as whitespace, as a good start :) - enum fsm_state state_ = fsm_whitespace; - - while (cur_chr != EOF) { - - switch (state_) { - - case fsm_whitespace: - - if (arg_buf) { - - ARG_STORE(); - break; - - } - - if (isspace(cur_chr)) { - - cur_chr = fgetc(f); - - } else if (cur_chr == (int)'\'') { - - state_ = fsm_single_quote; - cur_chr = fgetc(f); - - } else if (cur_chr == (int)'"') { - - state_ = fsm_double_quote; - cur_chr = fgetc(f); - - } else if (cur_chr == (int)'\\') { - - state_ = fsm_backslash; - cur_chr = fgetc(f); - - } else { - - state_ = fsm_normal; - - } - - break; - - case fsm_normal: - - if (isspace(cur_chr)) { - - state_ = fsm_whitespace; - - } else if (cur_chr == (int)'\'') { - - state_ = fsm_single_quote; - cur_chr = fgetc(f); - - } else if (cur_chr == (int)'\"') { - - state_ = fsm_double_quote; - cur_chr = fgetc(f); - - } else if (cur_chr == (int)'\\') { - - state_ = fsm_backslash; - cur_chr = fgetc(f); - - } else { - - ARG_ALLOC(cur_chr); - cur_chr = fgetc(f); - - } - - break; - - case fsm_backslash: - - ARG_ALLOC(cur_chr); - cur_chr = fgetc(f); - state_ = fsm_normal; - - break; - - case fsm_single_quote: - - if (cur_chr == (int)'\\') { - - cur_chr = fgetc(f); - if (cur_chr == EOF) break; - ARG_ALLOC(cur_chr); - - } else if (cur_chr == (int)'\'') { - - state_ = fsm_normal; - - } else { - - ARG_ALLOC(cur_chr); - - } - - cur_chr = fgetc(f); - break; - - case fsm_double_quote: - - if (cur_chr == (int)'\\') { - - cur_chr = fgetc(f); - if (cur_chr == EOF) break; - ARG_ALLOC(cur_chr); - - } else if (cur_chr == (int)'"') { - - state_ = fsm_normal; - - } else { - - ARG_ALLOC(cur_chr); - - } - - cur_chr = fgetc(f); - break; - - default: - break; - - } - - } - - if (arg_buf) { ARG_STORE(); } // save the pending arg after EOF - -#undef ARG_ALLOC -#undef ARG_STORE - - if (argc_read > 1) { process_params(aflcc, scan, argc_read, argv_read); } - - // We cannot free argv_read[] unless we don't need to keep any - // reference in cc_params. Never free argv[0], the const "". - if (scan) { - - while (argc_read > 1) - ck_free(argv_read[--argc_read]); - - ck_free(argv_read); - - } - - continue; - - } /* Response file support -----END----- */ - - if (!scan) insert_param(aflcc, cur); - - } - -} - -/* Process each of the existing argv, also add a few new args. */ -static void edit_params(aflcc_state_t *aflcc, u32 argc, char **argv, - char **envp) { - - add_real_argv0(aflcc); - - // prevent unnecessary build errors - if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) { - - insert_param(aflcc, "-Wno-unused-command-line-argument"); - - } - - if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { - - add_assembler(aflcc); - - } - - if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } - - if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { - - if (aflcc->lto_mode && aflcc->have_instr_env) { - - load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); - - } - - if (getenv("AFL_LLVM_DICT2FILE")) { - - load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); - - } - - // laf - if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - - load_llvm_pass(aflcc, "split-switches-pass.so"); - - } - - if (getenv("LAF_TRANSFORM_COMPARES") || - getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { - - load_llvm_pass(aflcc, "compare-transform-pass.so"); - - } - - if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || - getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { - - load_llvm_pass(aflcc, "split-compares-pass.so"); - - } - - // /laf - - if (aflcc->cmplog_mode) { - - insert_param(aflcc, "-fno-inline"); - - load_llvm_pass(aflcc, "cmplog-switches-pass.so"); - // reuse split switches from laf - load_llvm_pass(aflcc, "split-switches-pass.so"); - - } - - // #if LLVM_MAJOR >= 13 - // // Use the old pass manager in LLVM 14 which the AFL++ passes still - // use. insert_param(aflcc, "-flegacy-pass-manager"); - // #endif - - if (aflcc->lto_mode) { - - insert_param(aflcc, aflcc->lto_flag); - - if (!aflcc->have_c) { - - add_lto_linker(aflcc); - add_lto_passes(aflcc); - - } - - } else { - - if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) { - - add_optimized_pcguard(aflcc); - - } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { - - add_native_pcguard(aflcc); - - } else { - - load_llvm_pass(aflcc, "afl-llvm-pass.so"); - - } - - } - - if (aflcc->cmplog_mode) { - - load_llvm_pass(aflcc, "cmplog-instructions-pass.so"); - load_llvm_pass(aflcc, "cmplog-routines-pass.so"); - - } - - if (getenv("AFL_LLVM_INJECTIONS_ALL") || - getenv("AFL_LLVM_INJECTIONS_SQL") || - getenv("AFL_LLVM_INJECTIONS_LDAP") || - getenv("AFL_LLVM_INJECTIONS_XSS")) { - - load_llvm_pass(aflcc, "injection-pass.so"); - - } - - // insert_param(aflcc, "-Qunused-arguments"); - - } - - /* Inspect the command line parameters. */ - - process_params(aflcc, 0, argc, argv); - - add_sanitizers(aflcc, envp); - - add_misc_params(aflcc); - - add_defs_common(aflcc); - add_defs_selective_instr(aflcc); - add_defs_persistent_mode(aflcc); - - add_runtime(aflcc); - - insert_param(aflcc, NULL); - -} - -/* Main entry point */ -int main(int argc, char **argv, char **envp) { - - aflcc_state_t *aflcc = malloc(sizeof(aflcc_state_t)); - aflcc_state_init(aflcc, (u8 *)argv[0]); - - check_environment_vars(envp); - - find_built_deps(aflcc); - - compiler_mode_by_callname(aflcc); - compiler_mode_by_environ(aflcc); - compiler_mode_by_cmdline(aflcc, argc, argv); - - instrument_mode_by_environ(aflcc); - - mode_final_checkout(aflcc, argc, argv); - - process_params(aflcc, 1, argc, argv); - - maybe_usage(aflcc, argc, argv); - - mode_notification(aflcc); - - if (aflcc->debug) debugf_args(argc, argv); - - edit_params(aflcc, argc, argv, envp); - - if (aflcc->debug) - debugf_args((s32)aflcc->cc_par_cnt, (char **)aflcc->cc_params); - - if (aflcc->passthrough) { - - argv[0] = aflcc->cc_params[0]; - execvp(aflcc->cc_params[0], (char **)argv); - - } else { - - execvp(aflcc->cc_params[0], (char **)aflcc->cc_params); - - } - - FATAL("Oops, failed to execute '%s' - check your PATH", aflcc->cc_params[0]); - - return 0; - -} - -- 2.34.1 From 93c870de50642ac002c8ede1d173eb26c0406dcc Mon Sep 17 00:00:00 2001 From: ph2ocwf5z <1781919720@qq.com> Date: Sun, 12 Jan 2025 15:49:02 +0800 Subject: [PATCH 3/4] ADD file via upload --- src%2FAFLplusplus-stable%2Fsrc/afl-cc.c | 3698 +++++++++++++++++++++++ 1 file changed, 3698 insertions(+) create mode 100644 src%2FAFLplusplus-stable%2Fsrc/afl-cc.c diff --git a/src%2FAFLplusplus-stable%2Fsrc/afl-cc.c b/src%2FAFLplusplus-stable%2Fsrc/afl-cc.c new file mode 100644 index 0000000..eca6788 --- /dev/null +++ b/src%2FAFLplusplus-stable%2Fsrc/afl-cc.c @@ -0,0 +1,3698 @@ +/* + american fuzzy lop++ - compiler instrumentation wrapper + ------------------------------------------------------- + + Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2024 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + https://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE 1 +#endif + +#include "common.h" +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "llvm-alternative-coverage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (LLVM_MAJOR - 0 == 0) + #undef LLVM_MAJOR +#endif +#if !defined(LLVM_MAJOR) + #define LLVM_MAJOR 0 +#endif +#if (LLVM_MINOR - 0 == 0) + #undef LLVM_MINOR +#endif +#if !defined(LLVM_MINOR) + #define LLVM_MINOR 0 +#endif + +#ifndef MAX_PARAMS_NUM + #define MAX_PARAMS_NUM 2048 +#endif + +/** Global declarations -----BEGIN----- **/ + +typedef enum { + + PARAM_MISS, // not matched + PARAM_SCAN, // scan only + PARAM_KEEP, // kept as-is + PARAM_DROP, // ignored + +} param_st; + +typedef enum { + + INSTRUMENT_DEFAULT = 0, + INSTRUMENT_CLASSIC = 1, + INSTRUMENT_AFL = 1, + INSTRUMENT_PCGUARD = 2, + INSTRUMENT_CFG = 3, + INSTRUMENT_LTO = 4, + INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, + INSTRUMENT_OPT_CTX = 8, + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, + INSTRUMENT_OPT_CODECOV = 128, + +} instrument_mode_id; + +typedef enum { + + UNSET = 0, + LTO = 1, + LLVM = 2, + GCC_PLUGIN = 3, + GCC = 4, + CLANG = 5 + +} compiler_mode_id; + +static u8 cwd[4096]; + +char instrument_mode_string[18][18] = { + + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "CALLER", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" + +}; + +char compiler_mode_string[7][12] = { + + "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", + "GCC", "CLANG", "" + +}; + +u8 *instrument_mode_2str(instrument_mode_id i) { + + return instrument_mode_string[i]; + +} + +u8 *compiler_mode_2str(compiler_mode_id i) { + + return compiler_mode_string[i]; + +} + +u8 *getthecwd() { + + if (getcwd(cwd, sizeof(cwd)) == NULL) { + + static u8 fail[] = ""; + return fail; + + } + + return cwd; + +} + +typedef struct aflcc_state { + + u8 **cc_params; /* Parameters passed to the real CC */ + u32 cc_par_cnt; /* Param count, including argv0 */ + + u8 *argv0; /* Original argv0 (by strdup) */ + u8 *callname; /* Executable file argv0 indicated */ + + u8 debug; + + u8 compiler_mode, plusplus_mode, lto_mode; + + u8 *lto_flag; + + u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k; + + u8 cmplog_mode; + + u8 have_instr_env, have_gcc, have_clang, have_llvm, have_gcc_plugin, have_lto, + have_optimized_pcguard, have_instr_list; + + u8 fortify_set, x_set, bit_mode, preprocessor_only, have_unroll, have_o, + have_pic, have_c, shared_linking, partial_linking, non_dash, have_fp, + have_flto, have_hidden, have_fortify, have_fcf, have_staticasan, + have_rust_asanrt, have_asan, have_msan, have_ubsan, have_lsan, have_tsan, + have_cfisan; + + // u8 *march_opt; + u8 need_aflpplib; + int passthrough; + + u8 use_stdin; /* dummy */ + u8 *argvnull; /* dummy */ + +} aflcc_state_t; + +void aflcc_state_init(aflcc_state_t *, u8 *argv0); + +u8 *find_object(aflcc_state_t *, u8 *obj); + +void find_built_deps(aflcc_state_t *); + +/* Insert param into the new argv, raise error if MAX_PARAMS_NUM exceeded. */ +static inline void insert_param(aflcc_state_t *aflcc, u8 *param) { + + if (unlikely(aflcc->cc_par_cnt + 1 >= MAX_PARAMS_NUM)) + FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM."); + + aflcc->cc_params[aflcc->cc_par_cnt++] = param; + +} + +/* + Insert a param which contains path to the object file. It uses find_object to + get the path based on the name `obj`, and then uses a sprintf like method to + format it with `fmt`. If `fmt` is NULL, the inserted arg is same as the path. + If `msg` provided, it should be an error msg raised if the path can't be + found. `obj` must not be NULL. +*/ +static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt, + u8 *msg) { + + u8 *_obj_path = find_object(aflcc, obj); + if (!_obj_path) { + + if (msg) + FATAL("%s", msg); + else + FATAL("Unable to find '%s'", obj); + + } else { + + if (fmt) { + + u8 *_obj_path_fmt = alloc_printf(fmt, _obj_path); + ck_free(_obj_path); + aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path_fmt; + + } else { + + aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path; + + } + + } + +} + +/* Insert params into the new argv, make clang load the pass. */ +static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) { + +#if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 + insert_param(aflcc, "-fexperimental-new-pass-manager"); + #endif + insert_object(aflcc, pass, "-fpass-plugin=%s", 0); +#else + insert_param(aflcc, "-Xclang"); + insert_param(aflcc, "-load"); + insert_param(aflcc, "-Xclang"); + insert_object(aflcc, pass, 0, 0); +#endif + +} + +static inline void debugf_args(int argc, char **argv) { + + DEBUGF("cd '%s';", getthecwd()); + for (int i = 0; i < argc; i++) + SAYF(" '%s'", argv[i]); + SAYF("\n"); + fflush(stdout); + fflush(stderr); + +} + +void compiler_mode_by_callname(aflcc_state_t *); +void compiler_mode_by_environ(aflcc_state_t *); +void compiler_mode_by_cmdline(aflcc_state_t *, int argc, char **argv); +void instrument_mode_by_environ(aflcc_state_t *); +void mode_final_checkout(aflcc_state_t *, int argc, char **argv); +void mode_notification(aflcc_state_t *); + +void add_real_argv0(aflcc_state_t *); + +void add_defs_common(aflcc_state_t *); +void add_defs_selective_instr(aflcc_state_t *); +void add_defs_persistent_mode(aflcc_state_t *); +void add_defs_fortify(aflcc_state_t *, u8); +void add_defs_lsan_ctrl(aflcc_state_t *); + +param_st parse_fsanitize(aflcc_state_t *, u8 *, u8); +void add_sanitizers(aflcc_state_t *, char **envp); +void add_optimized_pcguard(aflcc_state_t *); +void add_native_pcguard(aflcc_state_t *); + +void add_assembler(aflcc_state_t *); +void add_gcc_plugin(aflcc_state_t *); + +param_st parse_misc_params(aflcc_state_t *, u8 *, u8); +void add_misc_params(aflcc_state_t *); + +param_st parse_linking_params(aflcc_state_t *, u8 *, u8, u8 *skip_next, + char **argv); + +void add_lto_linker(aflcc_state_t *); +void add_lto_passes(aflcc_state_t *); +void add_runtime(aflcc_state_t *); + +/** Global declarations -----END----- **/ + +/* + Init global state struct. We also extract the callname, + check debug options and if in C++ mode here. +*/ +void aflcc_state_init(aflcc_state_t *aflcc, u8 *argv0) { + + // Default NULL/0 is a good start + memset(aflcc, 0, sizeof(aflcc_state_t)); + + aflcc->cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *)); + aflcc->cc_par_cnt = 1; + + aflcc->lto_flag = AFL_CLANG_FLTO; + + // aflcc->march_opt = CFLAGS_OPT; + + /* callname & if C++ mode */ + + aflcc->argv0 = ck_strdup(argv0); + + char *cname = NULL; + + if ((cname = strrchr(aflcc->argv0, '/')) != NULL) { + + cname++; + + } else { + + cname = aflcc->argv0; + + } + + aflcc->callname = cname; + + if (strlen(cname) > 2 && (strncmp(cname + strlen(cname) - 2, "++", 2) == 0 || + strstr(cname, "-g++") != NULL)) { + + aflcc->plusplus_mode = 1; + + } + + /* debug */ + + if (getenv("AFL_DEBUG")) { + + aflcc->debug = 1; + if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + + } else if (getenv("AFL_QUIET")) { + + be_quiet = 1; + + } + + if ((getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) && (!aflcc->debug)) { + + be_quiet = 1; + + } + +} + +/* + Try to find a specific runtime we need, in here: + + 1. firstly we check the $AFL_PATH environment variable location if set + 2. next we check argv[0] if it has path information and use it + a) we also check ../lib/afl + 3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + FreeBSD with procfs) + a) and check here in ../lib/afl too + 4. we look into the AFL_PATH define (usually /usr/local/lib/afl) + 5. we finally try the current directory + + if all these attempts fail - we return NULL and the caller has to decide + what to do. Otherwise the path to obj would be allocated and returned. +*/ +u8 *find_object(aflcc_state_t *aflcc, u8 *obj) { + + u8 *argv0 = aflcc->argv0; + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash = NULL, *tmp; + + if (afl_path) { + + tmp = alloc_printf("%s/%s", afl_path, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + + if (argv0) { + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir = ck_strdup(argv0); + + slash = strrchr(dir, '/'); + *slash = 0; + + tmp = alloc_printf("%s/%s", dir, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + ck_free(dir); + return tmp; + + } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + ck_free(dir); + return tmp; + + } + + ck_free(tmp); + ck_free(dir); + + } + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) || \ + defined(__ANDROID__) || defined(__NetBSD__) + #define HAS_PROC_FS 1 +#endif +#ifdef HAS_PROC_FS + else { + + char *procname = NULL; + #if defined(__FreeBSD__) || defined(__DragonFly__) + procname = "/proc/curproc/file"; + #elif defined(__linux__) || defined(__ANDROID__) + procname = "/proc/self/exe"; + #elif defined(__NetBSD__) + procname = "/proc/curproc/exe"; + #endif + if (procname) { + + char exepath[PATH_MAX]; + ssize_t exepath_len = readlink(procname, exepath, sizeof(exepath)); + if (exepath_len > 0 && exepath_len < PATH_MAX) { + + exepath[exepath_len] = 0; + slash = strrchr(exepath, '/'); + + if (slash) { + + *slash = 0; + tmp = alloc_printf("%s/%s", exepath, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + + } + + } + + } + +#endif +#undef HAS_PROC_FS + + } + + tmp = alloc_printf("%s/%s", AFL_PATH, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + tmp = alloc_printf("./%s", obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + if (aflcc->debug) DEBUGF("Trying ... giving up\n"); + + return NULL; + +} + +/* + Deduce some info about compiler toolchains in current system, + from the building results of AFL++ +*/ +void find_built_deps(aflcc_state_t *aflcc) { + + char *ptr = NULL; + +#if defined(__x86_64__) || defined(__i386__) + if ((ptr = find_object(aflcc, "afl-as")) != NULL) { + + #ifndef __APPLE__ + // on OSX clang masquerades as GCC + aflcc->have_gcc = 1; + #endif + aflcc->have_clang = 1; + ck_free(ptr); + + } + +#endif + + if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) { + + aflcc->have_optimized_pcguard = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR >= 3) + + if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) { + + aflcc->have_lto = 1; + ck_free(ptr); + + } + + if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) { + + aflcc->have_llvm = 1; + ck_free(ptr); + + } + +#endif + +#ifdef __ANDROID__ + aflcc->have_llvm = 1; +#endif + + if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) { + + aflcc->have_gcc_plugin = 1; + ck_free(ptr); + + } + +#if !defined(__ANDROID__) && !defined(ANDROID) + ptr = find_object(aflcc, "afl-compiler-rt.o"); + + if (!ptr) { + + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); + + } + + if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); } + + ck_free(ptr); +#endif + +} + +/** compiler_mode & instrument_mode selecting -----BEGIN----- **/ + +/* Select compiler_mode by callname, such as "afl-clang-fast", etc. */ +void compiler_mode_by_callname(aflcc_state_t *aflcc) { + + if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) { + + /* afl-clang-fast is always created there by makefile + just like afl-clang, burdened with special purposes: + - If llvm-config is not available (i.e. LLVM_MAJOR is 0), + or too old, it falls back to LLVM-NATIVE mode and let + the actual compiler complain if doesn't work. + - Otherwise try default llvm instruments except LTO. + */ +#if (LLVM_MAJOR >= 3) + aflcc->compiler_mode = LLVM; +#else + aflcc->compiler_mode = CLANG; +#endif + + } else + +#if (LLVM_MAJOR >= 3) + + if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 || + + strncmp(aflcc->callname, "afl-lto", 7) == 0) { + + aflcc->compiler_mode = LTO; + + } else + +#endif + + if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 || + + strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 || + + strncmp(aflcc->callname, "afl-g++", 7) == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strcmp(aflcc->callname, "afl-clang") == 0 || + + strcmp(aflcc->callname, "afl-clang++") == 0) { + + aflcc->compiler_mode = CLANG; + + } + +} + +/* + Select compiler_mode by env AFL_CC_COMPILER. And passthrough mode can be + regarded as a special compiler_mode, so we check for it here, too. +*/ +void compiler_mode_by_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + aflcc->passthrough = 1; + + } + + char *ptr = getenv("AFL_CC_COMPILER"); + + if (!ptr) { return; } + + if (aflcc->compiler_mode) { + + if (!be_quiet) { + + WARNF( + "\"AFL_CC_COMPILER\" is set but a specific compiler was already " + "selected by command line parameter or symlink, ignoring the " + "environment variable!"); + + } + + } else { + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + aflcc->compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + aflcc->compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strcasecmp(ptr, "CLANG") == 0) { + + aflcc->compiler_mode = CLANG; + + } else + + FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); + + } + +} + +/* + Select compiler_mode by command line options --afl-... + If it can be inferred, instrument_mode would also be set. + This can supersedes previous result based on callname + or AFL_CC_COMPILER. And "--afl_noopt"/"--afl-noopt" will + be overwritten by "-g". +*/ +void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) { + + char *ptr = NULL; + + for (int i = 1; i < argc; i++) { + + if (strncmp(argv[i], "--afl", 5) == 0) { + + if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) { + + aflcc->passthrough = 1; + argv[i] = "-g"; // we have to overwrite it, -g is always good + continue; + + } + + if (aflcc->compiler_mode && !be_quiet) { + + WARNF( + "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " + "symlink compiler selection!"); + + } + + ptr = argv[i]; + ptr += 5; + while (*ptr == '-') + ptr++; + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + aflcc->compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + aflcc->compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + + strncasecmp(ptr, "PC-GUARD", 8) == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } else if (strcasecmp(ptr, "INSTRIM") == 0 || + + strcasecmp(ptr, "CFG") == 0) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and " + "PCGUARD (default in afl-cc).\n"); + + } else if (strcasecmp(ptr, "AFL") == 0 || + + strcasecmp(ptr, "CLASSIC") == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + + strcasecmp(ptr, "NATIVE") == 0 || + strcasecmp(ptr, "LLVM-NATIVE") == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { + + aflcc->compiler_mode = CLANG; + + } else + + FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); + + } + + } + +} + +/* + Select instrument_mode by those envs in old style: + - USE_TRACE_PC, AFL_USE_TRACE_PC, AFL_LLVM_USE_TRACE_PC, AFL_TRACE_PC + - AFL_LLVM_CALLER, AFL_LLVM_CTX, AFL_LLVM_CTX_K + - AFL_LLVM_NGRAM_SIZE +*/ +static void instrument_mode_old_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || + getenv("INSTRIM_LIB")) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD " + "(default in afl-cc).\n"); + + } + + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || + getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { + + if (aflcc->instrument_mode == 0) + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + else if (aflcc->instrument_mode != INSTRUMENT_PCGUARD) + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + + } + + if (getenv("AFL_LLVM_CTX")) aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; + if (getenv("AFL_LLVM_CALLER") || getenv("AFL_LLVM_LTO_CALLER") || + getenv("AFL_LLVM_LTO_CTX")) + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + if (getenv("AFL_LLVM_NGRAM_SIZE")) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; + aflcc->ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); + if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "(%u)", + NGRAM_SIZE_MAX); + + } + + if (getenv("AFL_LLVM_CTX_K")) { + + aflcc->ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); + if (aflcc->ctx_k == 1) { + + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + } else { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + + } + + } + +} + +/* + Select instrument_mode by env 'AFL_LLVM_INSTRUMENT'. + Previous compiler_mode will be superseded, if required by some + values of instrument_mode. +*/ +static void instrument_mode_new_environ(aflcc_state_t *aflcc) { + + if (!getenv("AFL_LLVM_INSTRUMENT")) { return; } + + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + + while (ptr2) { + + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { + + if (aflcc->instrument_mode == INSTRUMENT_LTO) { + + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->lto_mode = 1; + + } else if (!aflcc->instrument_mode || + + aflcc->instrument_mode == INSTRUMENT_AFL) { + + aflcc->instrument_mode = INSTRUMENT_AFL; + + } else { + + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_PCGUARD) + + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 || + strncasecmp(ptr2, "native", strlen("native")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 || + strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CODECOV; + + } else { + + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and " + "PCGUARD (default in afl-cc).\n"); + + } + + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { + + aflcc->lto_mode = 1; + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO) + + aflcc->instrument_mode = INSTRUMENT_LTO; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strcasecmp(ptr2, "gcc") == 0) { + + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC) + + aflcc->instrument_mode = INSTRUMENT_GCC; + + else if (aflcc->instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + aflcc->compiler_mode = GCC; + + } + + if (strcasecmp(ptr2, "clang") == 0) { + + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG) + + aflcc->instrument_mode = INSTRUMENT_CLANG; + + else if (aflcc->instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + aflcc->compiler_mode = CLANG; + + } + + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 || + strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 || + strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) { + + u8 *ptr3 = ptr2; + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) + FATAL( + "you must set the K-CTX K with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ctx-2"); + + } + + aflcc->ctx_k = atoi(ptr3); + if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) + FATAL( + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", + CTX_MAX_K); + + if (aflcc->ctx_k == 1) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + + } else { + + aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", aflcc->ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } + + } + + if (strcasecmp(ptr2, "ctx") == 0) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); + + } + + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + + } + + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) { + + ptr3++; + + } + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + FATAL( + "you must set the NGRAM size with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ngram-2"); + + } + + aflcc->ngram_size = atoi(ptr3); + + if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) { + + FATAL( + "NGRAM instrumentation option must be between 2 and " + "NGRAM_SIZE_MAX (%u)", + NGRAM_SIZE_MAX); + + } + + aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); + u8 *ptr4 = alloc_printf("%u", aflcc->ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); + + } + + ptr2 = strtok(NULL, ":,;"); + + } + +} + +/* + Select instrument_mode by envs, the top wrapper. We check + have_instr_env firstly, then call instrument_mode_old_environ + and instrument_mode_new_environ sequentially. +*/ +void instrument_mode_by_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { + + aflcc->have_instr_env = 1; + + } + + if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) { + + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); + + } + + instrument_mode_old_environ(aflcc); + instrument_mode_new_environ(aflcc); + +} + +/* + Workaround to ensure CALLER, CTX, K-CTX and NGRAM + instrumentation were used correctly. +*/ +static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) { + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + + FATAL("you cannot set CTX and CALLER together"); + + } + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CTX and K-CTX together"); + + } + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CALLER and K-CTX together"); + + } + + if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM && + !((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + aflcc->compiler_mode == LTO)) + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); + + if (aflcc->instrument_opt_mode && + aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV && + aflcc->instrument_mode != INSTRUMENT_CLASSIC && + !(aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER && + aflcc->compiler_mode == LTO)) + FATAL( + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); + +} + +/* + Last step of compiler_mode & instrument_mode selecting. + We have a few of workarounds here, to check any corner cases, + prepare for a series of fallbacks, and raise warnings or errors. +*/ +void mode_final_checkout(aflcc_state_t *aflcc, int argc, char **argv) { + + if (aflcc->instrument_opt_mode && + aflcc->instrument_mode == INSTRUMENT_DEFAULT && + (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == UNSET)) { + + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->compiler_mode = LLVM; + + } + + if (!aflcc->compiler_mode) { + + // lto is not a default because outside of afl-cc RANLIB and AR have to + // be set to LLVM versions so this would work + if (aflcc->have_llvm) + aflcc->compiler_mode = LLVM; + else if (aflcc->have_gcc_plugin) + aflcc->compiler_mode = GCC_PLUGIN; + else if (aflcc->have_gcc) + aflcc->compiler_mode = GCC; + else if (aflcc->have_clang) + aflcc->compiler_mode = CLANG; + else if (aflcc->have_lto) + aflcc->compiler_mode = LTO; + else + FATAL("no compiler mode available"); + + } + + switch (aflcc->compiler_mode) { + + case GCC: + if (!aflcc->have_gcc) FATAL("afl-gcc is not available on your platform!"); + break; + case CLANG: + if (!aflcc->have_clang) + FATAL("afl-clang is not available on your platform!"); + break; + case LLVM: + if (!aflcc->have_llvm) + FATAL( + "LLVM mode is not available, please install LLVM 13+ and recompile " + "AFL++"); + break; + case GCC_PLUGIN: + if (!aflcc->have_gcc_plugin) + FATAL( + "GCC_PLUGIN mode is not available, install gcc plugin support and " + "recompile AFL++"); + break; + case LTO: + if (!aflcc->have_lto) + FATAL( + "LTO mode is not available, please install LLVM 13+ and lld of the " + "same version and recompile AFL++"); + break; + default: + FATAL("no compiler mode available"); + + } + + if (aflcc->compiler_mode == GCC) { aflcc->instrument_mode = INSTRUMENT_GCC; } + + if (aflcc->compiler_mode == CLANG) { + + /* if our PCGUARD implementation is not available then silently switch to + native LLVM PCGUARD. Or classic asm instrument is explicitly preferred. */ + if (!aflcc->have_optimized_pcguard && + (aflcc->instrument_mode == INSTRUMENT_DEFAULT || + aflcc->instrument_mode == INSTRUMENT_PCGUARD)) { + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else { + + aflcc->instrument_mode = INSTRUMENT_CLANG; + setenv(CLANG_ENV_VAR, "1", 1); // used by afl-as + + } + + } + + if (aflcc->compiler_mode == LTO) { + + if (aflcc->instrument_mode == 0 || + aflcc->instrument_mode == INSTRUMENT_LTO || + aflcc->instrument_mode == INSTRUMENT_CFG || + aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + + aflcc->lto_mode = 1; + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } else if (aflcc->instrument_mode == INSTRUMENT_CLASSIC) { + + aflcc->lto_mode = 1; + + } else { + + if (!be_quiet) { + + WARNF("afl-clang-lto called with mode %s, using that mode instead", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + } + + if (aflcc->instrument_mode == 0 && aflcc->compiler_mode < GCC_PLUGIN) { + +#if LLVM_MAJOR >= 7 + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (aflcc->have_instr_env) { + + aflcc->instrument_mode = INSTRUMENT_AFL; + if (!be_quiet) { + + WARNF( + "Switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); + + } + + } else + + #endif + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + +#else + aflcc->instrument_mode = INSTRUMENT_AFL; +#endif + + } + + if (!aflcc->instrument_opt_mode && aflcc->lto_mode && + aflcc->instrument_mode == INSTRUMENT_CFG) { + + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } + +#ifndef AFL_CLANG_FLTO + if (aflcc->lto_mode) + FATAL( + "instrumentation mode LTO specified but LLVM support not available " + "(requires LLVM 11 or higher)"); +#endif + + if (aflcc->lto_mode) { + + if (aflcc->lto_flag[0] != '-') + FATAL( + "Using afl-clang-lto is not possible because Makefile magic did not " + "identify the correct -flto flag"); + else + aflcc->compiler_mode = LTO; + + } + + if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) + FATAL( + "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " + "together"); + +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD && aflcc->have_instr_env) { + + FATAL( + "Instrumentation type PCGUARD does not support " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif + + instrument_opt_mode_exclude(aflcc); + + u8 *ptr2; + + if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') + FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); + + if (getenv("AFL_LLVM_LAF_ALL")) { + + setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); + setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); + + } + + if (getenv("AFL_LLVM_DICT2FILE") && + (getenv("AFL_LLVM_LAF_SPLIT_SWITCHES") || + getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES"))) + FATAL("AFL_LLVM_DICT2FILE is incompatible with AFL_LLVM_LAF_*"); + + aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || + getenv("AFL_GCC_CMPLOG"); + +} + +/* + Print welcome message on screen, giving brief notes about + compiler_mode and instrument_mode. +*/ +void mode_notification(aflcc_state_t *aflcc) { + + char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k); + + char *ptr1 = alloc_printf( + "%s%s%s%s%s", instrument_mode_2str(aflcc->instrument_mode), + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); + + ck_free(ptr2); + ck_free(ptr3); + + if ((isatty(2) && !be_quiet) || aflcc->debug) { + + SAYF(cCYA + "afl-cc" VERSION cRST + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", + compiler_mode_2str(aflcc->compiler_mode), ptr1); + + } + + ck_free(ptr1); + + if (!be_quiet && + (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)) { + + WARNF( + "You are using outdated instrumentation, install LLVM and/or " + "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast " + "instead!"); + + } + +} + +/* + Set argv[0] required by execvp. It can be + - specified by env AFL_CXX + - g++ or clang++ + - CLANGPP_BIN or LLVM_BINDIR/clang++ + when in C++ mode, or + - specified by env AFL_CC + - gcc or clang + - CLANG_BIN or LLVM_BINDIR/clang + otherwise. +*/ +void add_real_argv0(aflcc_state_t *aflcc) { + + static u8 llvm_fullpath[PATH_MAX]; + + if (aflcc->plusplus_mode) { + + u8 *alt_cxx = getenv("AFL_CXX"); + + if (!alt_cxx) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { + + alt_cxx = "g++"; + + } else if (aflcc->compiler_mode == CLANG) { + + alt_cxx = "clang++"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cxx = llvm_fullpath; + + } + + } + + aflcc->cc_params[0] = alt_cxx; + + } else { + + u8 *alt_cc = getenv("AFL_CC"); + + if (!alt_cc) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { + + alt_cc = "gcc"; + + } else if (aflcc->compiler_mode == CLANG) { + + alt_cc = "clang"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANG_BIN); + alt_cc = llvm_fullpath; + + } + + } + + aflcc->cc_params[0] = alt_cc; + + } + +} + +/** compiler_mode & instrument_mode selecting -----END----- **/ + +/** Macro defs for the preprocessor -----BEGIN----- **/ + +void add_defs_common(aflcc_state_t *aflcc) { + + insert_param(aflcc, "-D__AFL_COMPILER=1"); + insert_param(aflcc, "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"); + +} + +/* + __afl_coverage macro defs. See + instrumentation/README.instrument_list.md# + 2-selective-instrumentation-with-_afl_coverage-directives +*/ +void add_defs_selective_instr(aflcc_state_t *aflcc) { + + if (aflcc->plusplus_mode) { + + insert_param(aflcc, + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_skip();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"); + + } else { + + insert_param(aflcc, + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_skip();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"); + + } + + insert_param( + aflcc, + "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " + "1;"); + insert_param(aflcc, "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"); + insert_param(aflcc, "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"); + insert_param(aflcc, "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"); + insert_param(aflcc, "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"); + +} + +/* + Macro defs for persistent mode. As documented in + instrumentation/README.persistent_mode.md, deferred forkserver initialization + and persistent mode are not available in afl-gcc and afl-clang. +*/ +void add_defs_persistent_mode(aflcc_state_t *aflcc) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) return; + + insert_param(aflcc, "-D__AFL_HAVE_MANUAL_CONTROL=1"); + + /* When the user tries to use persistent or deferred forkserver modes by + appending a single line to the program, we want to reliably inject a + signature into the binary (to be picked up by afl-fuzz) and we want + to call a function from the runtime .o file. This is unnecessarily + painful for three reasons: + + 1) We need to convince the compiler not to optimize out the signature. + This is done with __attribute__((used)). + + 2) We need to convince the linker, when called with -Wl,--gc-sections, + not to do the same. This is done by forcing an assignment to a + 'volatile' pointer. + + 3) We need to declare __afl_persistent_loop() in the global namespace, + but doing this within a method in a class is hard - :: and extern "C" + are forbidden and __attribute__((alias(...))) doesn't work. Hence the + __asm__ aliasing trick. + + */ + + insert_param(aflcc, + "-D__AFL_FUZZ_INIT()=" + "int __afl_sharedmem_fuzzing = 1;" + "extern __attribute__((visibility(\"default\"))) " + "unsigned int *__afl_fuzz_len;" + "extern __attribute__((visibility(\"default\"))) " + "unsigned char *__afl_fuzz_ptr;" + "unsigned char __afl_fuzz_alt[1048576];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"); + + insert_param(aflcc, + "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " + "__afl_fuzz_alt_ptr)"); + + insert_param( + aflcc, + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"); + + insert_param( + aflcc, + "-D__AFL_LOOP(_A)=" + "({ static volatile const char *_B __attribute__((used,unused)); " + " _B = (const char*)\"" PERSIST_SIG + "\"; " + "extern __attribute__((visibility(\"default\"))) int __afl_connected;" +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " +#else + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " +#endif /* ^__APPLE__ */ + // if afl is connected, we run _A times, else once. + "_L(__afl_connected ? _A : 1); })"); + + insert_param( + aflcc, + "-D__AFL_INIT()=" + "do { static volatile const char *_A __attribute__((used,unused)); " + " _A = (const char*)\"" DEFER_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " +#else + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " +#endif /* ^__APPLE__ */ + "_I(); } while (0)"); + +} + +/* + Control macro def of _FORTIFY_SOURCE. It will do nothing + if we detect this routine has been called previously, or + the macro already here in these existing args. +*/ +void add_defs_fortify(aflcc_state_t *aflcc, u8 action) { + + if (aflcc->have_fortify) { return; } + + switch (action) { + + case 1: + insert_param(aflcc, "-D_FORTIFY_SOURCE=1"); + break; + + case 2: + insert_param(aflcc, "-D_FORTIFY_SOURCE=2"); + break; + + default: // OFF + insert_param(aflcc, "-U_FORTIFY_SOURCE"); + break; + + } + + aflcc->have_fortify = 1; + +} + +/* Macro defs of __AFL_LEAK_CHECK, __AFL_LSAN_ON and __AFL_LSAN_OFF */ +void add_defs_lsan_ctrl(aflcc_state_t *aflcc) { + + insert_param(aflcc, "-includesanitizer/lsan_interface.h"); + insert_param( + aflcc, + "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) " + "_exit(23); }"); + insert_param(aflcc, "-D__AFL_LSAN_OFF()=__lsan_disable();"); + insert_param(aflcc, "-D__AFL_LSAN_ON()=__lsan_enable();"); + +} + +/** Macro defs for the preprocessor -----END----- **/ + +/** About -fsanitize -----BEGIN----- **/ + +/* For input "-fsanitize=...", it: + + 1. may have various OOB traps :) if ... doesn't contain ',' or + the input has bad syntax such as "-fsantiz=," + 2. strips any fuzzer* in ... and writes back (may result in "-fsanitize=") + 3. rets 1 if exactly "fuzzer" found, otherwise rets 0 +*/ +static u8 fsanitize_fuzzer_comma(char *string) { + + u8 detect_single_fuzzer = 0; + + char *p, *ptr = string + strlen("-fsanitize="); + // ck_alloc will check alloc failure + char *new = ck_alloc(strlen(string) + 1); + char *tmp = ck_alloc(strlen(ptr) + 1); + u32 count = 0, len, ende = 0; + + strcpy(new, "-fsanitize="); + + do { + + p = strchr(ptr, ','); + if (!p) { + + p = ptr + strlen(ptr) + 1; + ende = 1; + + } + + len = p - ptr; + if (len) { + + strncpy(tmp, ptr, len); + tmp[len] = 0; + // fprintf(stderr, "Found: %s\n", tmp); + ptr += len + 1; + if (*tmp) { + + u32 copy = 1; + if (!strcmp(tmp, "fuzzer")) { + + detect_single_fuzzer = 1; + copy = 0; + + } else if (!strncmp(tmp, "fuzzer", 6)) { + + copy = 0; + + } + + if (copy) { + + if (count) { strcat(new, ","); } + strcat(new, tmp); + ++count; + + } + + } + + } else { + + ptr++; + + } + + } while (!ende); + + strcpy(string, new); + + ck_free(tmp); + ck_free(new); + + return detect_single_fuzzer; + +} + +/* + Parse and process possible -fsanitize related args, return PARAM_MISS + if nothing matched. We have 3 main tasks here for these args: + - Check which one of those sanitizers present here. + - Check if libfuzzer present. We need to block the request of enable + libfuzzer, and link harness with our libAFLDriver.a later. + - Check if SanCov allow/denylist options present. We need to try switching + to LLVMNATIVE instead of using our optimized PCGUARD anyway. If we + can't make it finally for various reasons, just drop these options. +*/ +param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { + + param_st final_ = PARAM_MISS; + +// MACRO START +#define HAVE_SANITIZER_SCAN_KEEP(v, k) \ + do { \ + \ + if (strstr(cur_argv, "=" STRINGIFY(k)) || \ + strstr(cur_argv, "," STRINGIFY(k))) { \ + \ + if (scan) { \ + \ + aflcc->have_##v = 1; \ + final_ = PARAM_SCAN; \ + \ + } else { \ + \ + final_ = PARAM_KEEP; \ + \ + } \ + \ + } \ + \ + } while (0) + + // MACRO END + + if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize="))) { + + HAVE_SANITIZER_SCAN_KEEP(asan, address); + HAVE_SANITIZER_SCAN_KEEP(msan, memory); + HAVE_SANITIZER_SCAN_KEEP(ubsan, undefined); + HAVE_SANITIZER_SCAN_KEEP(tsan, thread); + HAVE_SANITIZER_SCAN_KEEP(lsan, leak); + HAVE_SANITIZER_SCAN_KEEP(cfisan, cfi); + + } + +#undef HAVE_SANITIZER_SCAN_KEEP + + // We can't use a "else if" there, because some of the following + // matching rules overlap with those in the if-statement above. + if (!strcmp(cur_argv, "-fsanitize=fuzzer")) { + + if (scan) { + + aflcc->need_aflpplib = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } else if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize=")) && + + strchr(cur_argv, ',') && + !strstr(cur_argv, "=,")) { // avoid OOB errors + + if (scan) { + + u8 *cur_argv_ = ck_strdup(cur_argv); + + if (fsanitize_fuzzer_comma(cur_argv_)) { + + aflcc->need_aflpplib = 1; + final_ = PARAM_SCAN; + + } + + ck_free(cur_argv_); + + } else { + + fsanitize_fuzzer_comma(cur_argv); + if (!cur_argv || strlen(cur_argv) <= strlen("-fsanitize=")) + final_ = PARAM_DROP; // this means it only has "fuzzer" previously. + + } + + } else if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) && + + strstr(cur_argv, "list=")) { + + if (scan) { + + aflcc->have_instr_list = 1; + final_ = PARAM_SCAN; + + } else { + + if (aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE) { + + if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); } + final_ = PARAM_DROP; + + } else { + + final_ = PARAM_KEEP; + + } + + } + + } + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/* + Add params for sanitizers. Here we need to consider: + - Use static runtime for asan, as much as possible. + - ASAN, MSAN, AFL_HARDEN are mutually exclusive. + - Add options if not found there, on request of AFL_USE_ASAN, AFL_USE_MSAN, + etc. + - Update have_* so that functions called after this can have correct context. + However this also means any functions called before should NOT depend on + these have_*, otherwise they may not work as expected. +*/ +void add_sanitizers(aflcc_state_t *aflcc, char **envp) { + // 如果启用了 ASAN (地址消毒器),则进行相关配置 + if (getenv("AFL_USE_ASAN") || aflcc->have_asan) { + + // 如果同时启用了 MSAN (内存消毒器),则报错,因为 ASAN 和 MSAN 不能同时使用 + if (getenv("AFL_USE_MSAN") || aflcc->have_msan) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 如果启用了 AFL_HARDEN,则报错,因为 ASAN 和 AFL_HARDEN 不能同时使用 + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + + // 如果是 GCC 插件模式,并且没有启用静态 ASAN 库,则添加静态 ASAN 库的选项 + if (aflcc->compiler_mode == GCC_PLUGIN && !aflcc->have_staticasan) { + insert_param(aflcc, "-static-libasan"); + } + + // 添加 fortify 配置,0表示没有额外的强化 + add_defs_fortify(aflcc, 0); + + // 如果没有启用 ASAN,则添加相应的编译选项来启用地址消毒 + if (!aflcc->have_asan) { + insert_param(aflcc, "-fsanitize=address"); + insert_param(aflcc, "-fno-common"); + } + + aflcc->have_asan = 1; // 标记已经启用了 ASAN + } + // 如果启用了 MSAN (内存消毒器),则进行相关配置 + else if (getenv("AFL_USE_MSAN") || aflcc->have_msan) { + + // 如果同时启用了 ASAN,则报错,因为 ASAN 和 MSAN 不能同时使用 + if (getenv("AFL_USE_ASAN") || aflcc->have_asan) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 如果启用了 AFL_HARDEN,则报错,因为 MSAN 和 AFL_HARDEN 不能同时使用 + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + // 添加 fortify 配置,0表示没有额外的强化 + add_defs_fortify(aflcc, 0); + + // 如果没有启用 MSAN,则添加相应的编译选项来启用内存消毒 + if (!aflcc->have_msan) { insert_param(aflcc, "-fsanitize=memory"); } + + aflcc->have_msan = 1; // 标记已经启用了 MSAN + } + + // 如果启用了 UBSAN (未定义行为消毒器),则进行相关配置 + if (getenv("AFL_USE_UBSAN") || aflcc->have_ubsan) { + + // 如果没有启用 UBSAN,则添加相应的编译选项来启用未定义行为消毒 + if (!aflcc->have_ubsan) { + insert_param(aflcc, "-fsanitize=undefined"); + insert_param(aflcc, "-fsanitize-undefined-trap-on-error"); + insert_param(aflcc, "-fno-sanitize-recover=all"); + } + + // 如果没有启用帧指针,则添加相应的选项来启用帧指针 + if (!aflcc->have_fp) { + insert_param(aflcc, "-fno-omit-frame-pointer"); + aflcc->have_fp = 1; + } + + aflcc->have_ubsan = 1; // 标记已经启用了 UBSAN + } + + // 如果启用了 TSAN (线程消毒器),则进行相关配置 + if (getenv("AFL_USE_TSAN") || aflcc->have_tsan) { + + // 如果没有启用帧指针,则添加相应的选项来启用帧指针 + if (!aflcc->have_fp) { + insert_param(aflcc, "-fno-omit-frame-pointer"); + aflcc->have_fp = 1; + } + + // 如果没有启用 TSAN,则添加相应的编译选项来启用线程消毒 + if (!aflcc->have_tsan) { insert_param(aflcc, "-fsanitize=thread"); } + + aflcc->have_tsan = 1; // 标记已经启用了 TSAN + } + + // 如果启用了 LSAN (泄漏消毒器),则进行相关配置 + if (getenv("AFL_USE_LSAN") && !aflcc->have_lsan) { + + // 添加编译选项来启用泄漏消毒 + insert_param(aflcc, "-fsanitize=leak"); + + // 添加 LSAN 控制的定义 + add_defs_lsan_ctrl(aflcc); + + aflcc->have_lsan = 1; // 标记已经启用了 LSAN + } + + // 如果启用了 CFISAN (控制流完整性消毒器),则进行相关配置 + if (getenv("AFL_USE_CFISAN") || aflcc->have_cfisan) { + + // 如果是 GCC 插件模式或 GCC 模式,则启用完整的控制流保护 + if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) { + + // 如果没有启用控制流保护,则添加相应选项 + if (!aflcc->have_fcf) { insert_param(aflcc, "-fcf-protection=full"); } + + } else { + + // 如果没有启用 LTO (链接时优化),则添加 LTO 选项 + if (!aflcc->lto_mode && !aflcc->have_flto) { + uint32_t i = 0, found = 0; + while (envp[i] != NULL && !found) { + if (strncmp("-flto", envp[i++], 5) == 0) found = 1; + } + if (!found) { insert_param(aflcc, "-flto"); } + aflcc->have_flto = 1; // 标记已经启用了 LTO + } + + // 如果没有启用 CFISAN,则添加相应选项来启用控制流完整性消毒 + if (!aflcc->have_cfisan) { insert_param(aflcc, "-fsanitize=cfi"); } + + // 如果没有启用隐藏符号,则启用隐藏符号选项 + if (!aflcc->have_hidden) { + insert_param(aflcc, "-fvisibility=hidden"); + aflcc->have_hidden = 1; + } + + aflcc->have_cfisan = 1; // 标记已经启用了 CFISAN + } + } +} + +/* Add params to enable LLVM SanCov, the native PCGUARD */ +void add_native_pcguard(aflcc_state_t *aflcc) { + + /* If there is a rust ASan runtime on the command line, it is likely we're + * linking from rust and adding native flags requiring the sanitizer runtime + * will trigger native clang to add yet another runtime, causing linker + * errors. For now we shouldn't add instrumentation here, we're linking + * anyway. + */ + if (aflcc->have_rust_asanrt) { return; } + + /* If llvm-config doesn't figure out LLVM_MAJOR, just + go on anyway and let compiler complain if doesn't work. */ + +#if LLVM_MAJOR > 0 && LLVM_MAJOR < 6 + FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+"); +#else + #if LLVM_MAJOR == 0 + WARNF( + "pcguard instrumentation with pc-table requires LLVM 6.0.1+" + " otherwise the compiler will fail"); + #endif + if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { + + insert_param(aflcc, + "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); + + } else { + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard,pc-table"); + + } + +#endif + +} + +/* + Add params to launch our optimized PCGUARD on request. + It will fallback to use the native PCGUARD in some cases. If so, plz + bear in mind that instrument_mode will be set to INSTRUMENT_LLVMNATIVE. +*/ +void add_optimized_pcguard(aflcc_state_t *aflcc) { + +#if LLVM_MAJOR >= 13 + #if defined __ANDROID__ || ANDROID + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + #else + + if (aflcc->have_instr_list) { + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST instead.\n"); + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else { + + /* Since LLVM_MAJOR >= 13 we use new pass manager */ + #if LLVM_MAJOR < 16 + insert_param(aflcc, "-fexperimental-new-pass-manager"); + #endif + insert_object(aflcc, "SanitizerCoveragePCGUARD.so", "-fpass-plugin=%s", 0); + + } + + #endif // defined __ANDROID__ || ANDROID +#else // LLVM_MAJOR < 13 + #if LLVM_MAJOR >= 4 + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for " + "enhanced version.\n"); + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + #else + + FATAL("pcguard instrumentation requires LLVM 4.0.1+"); + + #endif +#endif + +} + +/** About -fsanitize -----END----- **/ + +/** Linking behaviors -----BEGIN----- **/ + +/* + Parse and process possible linking stage related args, + return PARAM_MISS if nothing matched. +*/ +param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan, + u8 *skip_next, char **argv) { + + if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) { + + FATAL( + "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or " + "use afl-clang-fast!"); + + } + + param_st final_ = PARAM_MISS; + + if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) { + + if (scan) { + + aflcc->shared_linking = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_KEEP; + + } + + } else if (!strcmp(cur_argv, "-Wl,-r") || !strcmp(cur_argv, "-Wl,-i") || + + !strcmp(cur_argv, "-Wl,--relocatable") || + !strcmp(cur_argv, "-r") || !strcmp(cur_argv, "--relocatable")) { + + if (scan) { + + aflcc->partial_linking = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_KEEP; + + } + + } else if (!strncmp(cur_argv, "-fuse-ld=", 9) || + + !strncmp(cur_argv, "--ld-path=", 10)) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + if (aflcc->lto_mode) + final_ = PARAM_DROP; + else + final_ = PARAM_KEEP; + + } + + } else if (!strcmp(cur_argv, "-Wl,-z,defs") || + + !strcmp(cur_argv, "-Wl,--no-undefined") || + !strcmp(cur_argv, "-Wl,-no-undefined") || + !strcmp(cur_argv, "--no-undefined") || + strstr(cur_argv, "afl-compiler-rt") || + strstr(cur_argv, "afl-llvm-rt")) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } else if (!strcmp(cur_argv, "-z") || !strcmp(cur_argv, "-Wl,-z")) { + + u8 *param = *(argv + 1); + if (param && (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs"))) { + + *skip_next = 1; + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } + + } + + // Try to warn user for some unsupported cases + if (scan && final_ == PARAM_MISS) { + + u8 *ptr_ = NULL; + + if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) { + + if (!strcmp(ptr_, "defs")) { + + WARNF("'-Xlinker' 'defs' detected. This may result in a bad link."); + + } else if (strstr(ptr_, "-no-undefined")) { + + WARNF( + "'-Xlinker' '%s' detected. The latter option may be dropped and " + "result in a bad link.", + ptr_); + + } + + } else if (!strncmp(cur_argv, "-Wl,", 4) && + + (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) { + + ptr_ = cur_argv + 4; + + if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may break shared " + "linking.", + ptr_); + + } + + if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") || + strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may break partial " + "linking.", + ptr_); + + } + + if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may enable report " + "unresolved symbol references and result in a bad link.", + ptr_); + + } + + } + + } + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/* Add params to specify the linker used in LTO */ +void add_lto_linker(aflcc_state_t *aflcc) { + + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); + + u8 *ld_path = NULL; + if (getenv("AFL_REAL_LD")) { + + ld_path = strdup(getenv("AFL_REAL_LD")); + + } else { + + ld_path = strdup(AFL_REAL_LD); + + } + + if (!ld_path || !*ld_path) { + + if (ld_path) { + + // Freeing empty string + free(ld_path); + + } + + ld_path = strdup("ld.lld"); + + } + + if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); } +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 + insert_param(aflcc, alloc_printf("--ld-path=%s", ld_path)); +#else + insert_param(aflcc, alloc_printf("-fuse-ld=%s", ld_path)); +#endif + free(ld_path); + +} + +/* Add params to launch SanitizerCoverageLTO.so when linking */ +void add_lto_passes(aflcc_state_t *aflcc) { + +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15 + // The NewPM implementation only works fully since LLVM 15. + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,--load-pass-plugin=%s", + 0); +#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 + insert_param(aflcc, "-Wl,--lto-legacy-pass-manager"); + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); +#else + insert_param(aflcc, "-fno-experimental-new-pass-manager"); + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); +#endif + + insert_param(aflcc, "-Wl,--allow-multiple-definition"); + +} + +/* Add params to link with libAFLDriver.a on request */ +static void add_aflpplib(aflcc_state_t *aflcc) { + + if (!aflcc->need_aflpplib) return; + + u8 *afllib = find_object(aflcc, "libAFLDriver.a"); + + if (!be_quiet) { + + OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a"); + + } + + if (!afllib) { + + if (!be_quiet) { + + WARNF( + "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in " + "the flags - this will fail!"); + + } + + } else { + + insert_param(aflcc, afllib); + +#ifdef __APPLE__ + insert_param(aflcc, "-Wl,-undefined,dynamic_lookup"); +#endif + + } + +} + +/* Add params to link with runtimes depended by our instrumentation */ +void add_runtime(aflcc_state_t *aflcc) { + + if (aflcc->preprocessor_only || aflcc->have_c || !aflcc->non_dash) { + + /* 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. */ + return; + + } + + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC && + !getenv("AFL_LLVM_NO_RPATH")) { + + // in case LLVM is installed not via a package manager or "make install" + // e.g. compiled download or compiled from github then its ./lib directory + // might not be in the search path. Add it if so. + const char *libdir = LLVM_LIBDIR; + if (aflcc->plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && + strncmp(libdir, "/lib", 4)) { + +#ifdef __APPLE__ + u8 *libdir_opt = strdup("-Wl,-rpath," LLVM_LIBDIR); +#else + u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR); +#endif + insert_param(aflcc, libdir_opt); + + } + + } + +#ifndef __ANDROID__ + + #define M32_ERR_MSG "-m32 is not supported by your compiler" + #define M64_ERR_MSG "-m64 is not supported by your compiler" + + if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) { + + switch (aflcc->bit_mode) { + + case 0: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt.o", 0, 0); + if (aflcc->lto_mode) insert_object(aflcc, "afl-llvm-rt-lto.o", 0, 0); + break; + + case 32: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt-32.o", 0, M32_ERR_MSG); + if (aflcc->lto_mode) + insert_object(aflcc, "afl-llvm-rt-lto-32.o", 0, M32_ERR_MSG); + break; + + case 64: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt-64.o", 0, M64_ERR_MSG); + if (aflcc->lto_mode) + insert_object(aflcc, "afl-llvm-rt-lto-64.o", 0, M64_ERR_MSG); + break; + + } + + #if __AFL_CODE_COVERAGE + // Required for dladdr used in afl-compiler-rt.o + insert_param(aflcc, "-ldl"); + #endif + + #if !defined(__APPLE__) && !defined(__sun) + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "dynamic_list.txt", "-Wl,--dynamic-list=%s", 0); + #endif + + #if defined(__APPLE__) + if (aflcc->shared_linking || aflcc->partial_linking) { + + insert_param(aflcc, "-Wl,-U"); + insert_param(aflcc, "-Wl,___afl_area_ptr"); + insert_param(aflcc, "-Wl,-U"); + insert_param(aflcc, "-Wl,___sanitizer_cov_trace_pc_guard_init"); + + } + + #endif + + } + +#endif + + add_aflpplib(aflcc); + +#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ + insert_param(aflcc, "-Wl,-lrt"); +#endif + +} + +/** Linking behaviors -----END----- **/ + +/** Miscellaneous routines -----BEGIN----- **/ + +/* + Add params to make compiler driver use our afl-as + as assembler, required by the vanilla instrumentation. +*/ +void add_assembler(aflcc_state_t *aflcc) { + + u8 *afl_as = find_object(aflcc, "afl-as"); + + if (!afl_as) FATAL("Cannot find 'afl-as'."); + + u8 *slash = strrchr(afl_as, '/'); + if (slash) *slash = 0; + + // Search for 'as' may be unreliable in some cases (see #2058) + // so use 'afl-as' instead, because 'as' is usually a symbolic link, + // or can be a renamed copy of 'afl-as' created in the same dir. + // Now we should verify if the compiler can find the 'as' we need. + +#define AFL_AS_ERR "(should be a symlink or copy of 'afl-as')" + + u8 *afl_as_dup = alloc_printf("%s/as", afl_as); + + int fd = open(afl_as_dup, O_RDONLY); + if (fd < 0) { PFATAL("Unable to open '%s' " AFL_AS_ERR, afl_as_dup); } + + struct stat st; + if (fstat(fd, &st) < 0) { + + PFATAL("Unable to fstat '%s' " AFL_AS_ERR, afl_as_dup); + + } + + u32 f_len = st.st_size; + + u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); + if (f_data == MAP_FAILED) { + + PFATAL("Unable to mmap file '%s' " AFL_AS_ERR, afl_as_dup); + + } + + close(fd); + + // "AFL_AS" is a const str passed to getenv in afl-as.c + if (!memmem(f_data, f_len, "AFL_AS", strlen("AFL_AS") + 1)) { + + FATAL( + "Looks like '%s' is not a valid symlink or copy of '%s/afl-as'. " + "It is a prerequisite to override system-wide 'as' for " + "instrumentation.", + afl_as_dup, afl_as); + + } + + if (munmap(f_data, f_len)) { PFATAL("unmap() failed"); } + + ck_free(afl_as_dup); + +#undef AFL_AS_ERR + + insert_param(aflcc, "-B"); + insert_param(aflcc, afl_as); + + if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as"); + +} + +/* Add params to launch the gcc plugins for instrumentation. */ +void add_gcc_plugin(aflcc_state_t *aflcc) { + + if (aflcc->cmplog_mode) { + + insert_object(aflcc, "afl-gcc-cmplog-pass.so", "-fplugin=%s", 0); + insert_object(aflcc, "afl-gcc-cmptrs-pass.so", "-fplugin=%s", 0); + + } + + insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0); + + insert_param(aflcc, "-fno-if-conversion"); + insert_param(aflcc, "-fno-if-conversion2"); + +} + +/* Add some miscellaneous params required by our instrumentation. */ +void add_misc_params(aflcc_state_t *aflcc) { + + // 如果环境变量 AFl_NO_BUILTIN 或其他相关环境变量被设置,或者启用了 LTO 模式 + // 则禁用内置的字符串和内存比较函数 + if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") || + aflcc->lto_mode) { + + // 禁用常见的字符串和内存比较函数的内置实现,防止与模糊测试产生冲突 + insert_param(aflcc, "-fno-builtin-strcmp"); + insert_param(aflcc, "-fno-builtin-strncmp"); + insert_param(aflcc, "-fno-builtin-strcasecmp"); + insert_param(aflcc, "-fno-builtin-strncasecmp"); + insert_param(aflcc, "-fno-builtin-memcmp"); + insert_param(aflcc, "-fno-builtin-bcmp"); + insert_param(aflcc, "-fno-builtin-strstr"); + insert_param(aflcc, "-fno-builtin-strcasestr"); + + } + + // 如果没有启用位置无关代码(PIC),则添加 -fPIC 参数 + if (!aflcc->have_pic) { + insert_param(aflcc, "-fPIC"); + } + + // 如果环境变量 AFL_HARDEN 被设置,启用栈保护等安全选项 + if (getenv("AFL_HARDEN")) { + + // 启用所有函数的栈保护 + insert_param(aflcc, "-fstack-protector-all"); + + // 如果未设置 Fortify,设置 Fortify 防护等级 + if (!aflcc->fortify_set) + add_defs_fortify(aflcc, 2); + } + + // 如果环境变量 AFL_DONT_OPTIMIZE 未设置,启用优化选项 + if (!getenv("AFL_DONT_OPTIMIZE")) { + + // 启用调试符号生成 + insert_param(aflcc, "-g"); + + // 如果没有设置 -O 优化级别,设置为 -O3(最高优化) + if (!aflcc->have_o) + insert_param(aflcc, "-O3"); + + // 如果没有设置循环展开,启用循环展开优化 + if (!aflcc->have_unroll) + insert_param(aflcc, "-funroll-loops"); + + // 以下代码被注释掉了,但如果有指定架构优化选项(如 -march),也可以启用 + // if (strlen(aflcc->march_opt) > 1 && aflcc->march_opt[0] == '-') + // insert_param(aflcc, aflcc->march_opt); + } + + // 如果设置了 x_set 标志,插入 -x none 参数 + if (aflcc->x_set) { + + insert_param(aflcc, "-x"); + insert_param(aflcc, "none"); + } + +} + +/* + Parse and process a variety of args under our matching rules, + return PARAM_MISS if nothing matched. +*/ +param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { + + param_st final_ = PARAM_MISS; + +// MACRO START +#define SCAN_KEEP(dst, src) \ + do { \ + \ + if (scan) { \ + \ + dst = src; \ + final_ = PARAM_SCAN; \ + \ + } else { \ + \ + final_ = PARAM_KEEP; \ + \ + } \ + \ + } while (0) + + // MACRO END + + if (!strncasecmp(cur_argv, "-fpic", 5)) { + + SCAN_KEEP(aflcc->have_pic, 1); + + } else if (!strcmp(cur_argv, "-m32") || + + !strcmp(cur_argv, "armv7a-linux-androideabi")) { + + SCAN_KEEP(aflcc->bit_mode, 32); + + } else if (!strcmp(cur_argv, "-m64")) { + + SCAN_KEEP(aflcc->bit_mode, 64); + + } else if (strstr(cur_argv, "FORTIFY_SOURCE")) { + + SCAN_KEEP(aflcc->fortify_set, 1); + + } else if (!strcmp(cur_argv, "-x")) { + + SCAN_KEEP(aflcc->x_set, 1); + + } else if (!strcmp(cur_argv, "-E")) { + + SCAN_KEEP(aflcc->preprocessor_only, 1); + + } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) { + + SCAN_KEEP(aflcc->passthrough, 1); + + } else if (!strcmp(cur_argv, "-c")) { + + SCAN_KEEP(aflcc->have_c, 1); + + } else if (!strcmp(cur_argv, "-static-libasan")) { + + SCAN_KEEP(aflcc->have_staticasan, 1); + + } else if (strstr(cur_argv, "librustc") && strstr(cur_argv, "_rt.asan.a")) { + + SCAN_KEEP(aflcc->have_rust_asanrt, 1); + + } else if (!strcmp(cur_argv, "-fno-omit-frame-pointer")) { + + SCAN_KEEP(aflcc->have_fp, 1); + + } else if (!strcmp(cur_argv, "-fvisibility=hidden")) { + + SCAN_KEEP(aflcc->have_hidden, 1); + + } else if (!strcmp(cur_argv, "-flto") || !strcmp(cur_argv, "-flto=full")) { + + SCAN_KEEP(aflcc->have_flto, 1); + + } else if (!strncmp(cur_argv, "-D_FORTIFY_SOURCE", + + strlen("-D_FORTIFY_SOURCE"))) { + + SCAN_KEEP(aflcc->have_fortify, 1); + + } else if (!strncmp(cur_argv, "-fcf-protection", strlen("-fcf-protection"))) { + + SCAN_KEEP(aflcc->have_cfisan, 1); + + } else if (!strncmp(cur_argv, "-O", 2)) { + + SCAN_KEEP(aflcc->have_o, 1); + + } else if (!strncmp(cur_argv, "-funroll-loop", 13)) { + + SCAN_KEEP(aflcc->have_unroll, 1); + + } else if (!strncmp(cur_argv, "--afl", 5)) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strncmp(cur_argv, "-fno-unroll", 11)) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strncmp(cur_argv, "-stdlib=", 8) && + + (aflcc->compiler_mode == GCC || + aflcc->compiler_mode == GCC_PLUGIN)) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv); + final_ = PARAM_DROP; + + } + + } else if (cur_argv[0] != '-') { + + /* It's a weak, loose pattern, with very different purpose + than others. We handle it at last, cautiously and robustly. */ + + if (scan && cur_argv[0] != '@') // response file support + aflcc->non_dash = 1; + + } + +#undef SCAN_KEEP + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/** Miscellaneous routines -----END----- **/ + +/* Print help message on request */ +static void maybe_usage(aflcc_state_t *aflcc, int argc, char **argv) { + + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { + + printf("afl-cc" VERSION + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); + + SAYF( + "\n" + "afl-cc/afl-c++ [options]\n" + "\n" + "This is a helper application for afl-fuzz. It serves as a drop-in " + "replacement\n" + "for gcc and clang, letting you recompile third-party code with the " + "required\n" + "runtime instrumentation. A common use pattern would be one of the " + "following:\n\n" + + " CC=afl-cc CXX=afl-c++ ./configure --disable-shared\n" + " cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ .\n" + " CC=afl-cc CXX=afl-c++ meson\n\n"); + + SAYF( + " |------------- FEATURES " + "-------------|\n" + "MODES: NCC PERSIST DICT LAF " + "CMPLOG SELECT\n" + " [LLVM] LLVM: %s%s\n" + " PCGUARD %s yes yes module yes yes " + "yes\n" + " NATIVE AVAILABLE no yes no no " + "part. yes\n" + " CLASSIC %s no yes module yes yes " + "yes\n" + " - NORMAL\n" + " - CALLER\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " [LTO] LLVM LTO: %s%s\n" + " PCGUARD DEFAULT yes yes yes yes yes " + " yes\n" + " CLASSIC yes yes yes yes yes " + " yes\n" + " [GCC_PLUGIN] gcc plugin: %s%s\n" + " CLASSIC DEFAULT no yes no no no " + "yes\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" + " CLASSIC DEFAULT no no no no no " + "no\n\n", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->compiler_mode == LLVM ? " [SELECTED]" : "", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->have_lto ? "AVAILABLE" : "unavailable!", + aflcc->compiler_mode == LTO ? " [SELECTED]" : "", + aflcc->have_gcc_plugin ? "AVAILABLE" : "unavailable!", + aflcc->compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", + aflcc->have_gcc && aflcc->have_clang + ? "AVAILABLE" + : (aflcc->have_gcc + ? "GCC ONLY " + : (aflcc->have_clang ? "CLANG ONLY" : "unavailable!")), + (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) + ? " [SELECTED]" + : ""); + + SAYF( + "Modes:\n" + " To select the compiler mode use a symlink version (e.g. " + "afl-clang-fast), set\n" + " the environment variable AFL_CC_COMPILER to a mode (e.g. LLVM) or " + "use the\n" + " command line parameter --afl-MODE (e.g. --afl-llvm). If none is " + "selected,\n" + " afl-cc will select the best available (LLVM -> GCC_PLUGIN -> GCC).\n" + " The best is LTO but it often needs RANLIB and AR settings outside " + "of afl-cc.\n\n"); + +#if LLVM_MAJOR >= 11 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + #define NATIVE_MSG \ + " LLVM-NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" +#else + #define NATIVE_MSG "" +#endif + + SAYF( + "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " + "available)\n" + " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" + + NATIVE_MSG + + " CLASSIC: decision target instrumentation (README.llvm.md)\n" + " CALLER: CLASSIC + single callee context " + "(instrumentation/README.ctx.md)\n" + " CTX: CLASSIC + full callee context " + "(instrumentation/README.ctx.md)\n" + " NGRAM-x: CLASSIC + previous path " + "((instrumentation/README.ngram.md)\n\n"); + +#undef NATIVE_MSG + + SAYF( + "Features: (see documentation links)\n" + " NCC: non-colliding coverage [automatic] (that is an amazing " + "thing!)\n" + " (instrumentation/README.lto.md)\n" + " PERSIST: persistent mode support [code] (huge speed increase!)\n" + " (instrumentation/README.persistent_mode.md)\n" + " DICT: dictionary in the target [yes=automatic or LLVM module " + "pass]\n" + " (instrumentation/README.lto.md + " + "instrumentation/README.llvm.md)\n" + " LAF: comparison splitting [env] " + "(instrumentation/README.laf-intel.md)\n" + " CMPLOG: input2state exploration [env] " + "(instrumentation/README.cmplog.md)\n" + " SELECT: selective instrumentation (allow/deny) on filename or " + "function [env]\n" + " (instrumentation/README.instrument_list.md)\n\n"); + + if (argc < 2 || strncmp(argv[1], "-hh", 3)) { + + SAYF( + "To see all environment variables for the configuration of afl-cc " + "use \"-hh\".\n"); + + } else { + + SAYF( + "Environment variables used:\n" + " AFL_CC: path to the C compiler to use\n" + " AFL_CXX: path to the C++ compiler to use\n" + " AFL_DEBUG: enable developer debugging output\n" + " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_NOOPT: behave like a normal compiler (to pass configure " + "tests)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" + " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" + " AFL_INST_RATIO: percentage of branches to instrument\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" + " AFL_USE_TSAN: activate thread sanitizer\n" + " AFL_USE_LSAN: activate leak-checker sanitizer\n"); + + if (aflcc->have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); + +#if LLVM_MAJOR >= 9 + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#else + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#endif + if (aflcc->have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" + " AFL_LLVM_THREADSAFE_INST: instrument with thread safe counters, " + "disables neverzero\n" + + COUNTER_BEHAVIOUR + + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " + "comparisons\n" + " AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the " + "dictionary\n" + " AFL_LLVM_INJECTIONS_ALL: enables all injections hooking\n" + " AFL_LLVM_INJECTIONS_SQL: enables SQL injections hooking\n" + " AFL_LLVM_INJECTIONS_LDAP: enables LDAP injections hooking\n" + " AFL_LLVM_INJECTIONS_XSS: enables XSS injections hooking\n" + " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" + " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" + " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" + " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" + " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" + " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " + "functions\n" + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n"); + + if (aflcc->have_llvm) + SAYF( + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " + "mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, NGRAM-2 " + "..-16\n" + " You can also use the old environment variables instead:\n" + " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" + " AFL_LLVM_CALLER: use single context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_CTX: use full context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC)\n" + " AFL_LLVM_NO_RPATH: disable rpath setting for custom LLVM " + "locations\n"); + +#ifdef AFL_CLANG_FLTO + if (aflcc->have_lto) + SAYF( + "\nLTO/afl-clang-lto specific environment variables:\n" + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " + "e.g. " + "0x10000\n" + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions\n" + " into this file (LTO mode)\n" + " AFL_LLVM_LTO_CALLER: activate CALLER/CTX instrumentation\n" + " AFL_LLVM_LTO_CALLER_DEPTH: skip how many empty functions\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + "global var\n" + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for " + "a bb\n" + " AFL_REAL_LD: use this lld linker instead of the compiled in " + "path\n" + " AFL_LLVM_LTO_SKIPINIT: don't inject initialization code " + "(used in WAFL mode)\n" + "If anything fails - be sure to read README.lto.md!\n"); +#endif + + SAYF( + "\nYou can supply --afl-noopt to not instrument, like AFL_NOOPT. " + "(this is helpful\n" + "in some build systems if you do not want to instrument " + "everything.\n"); + + } + + SAYF( + "\nFor any information on the available instrumentations and options " + "please \n" + "consult the README.md, especially section 3.1 about instrumenting " + "targets.\n\n"); + +#if (LLVM_MAJOR >= 3) + if (aflcc->have_lto) + SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); + if (aflcc->have_llvm) + SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, + LLVM_BINDIR); +#endif + +#ifdef USEMMAP + #if !defined(__HAIKU__) + SAYF("Compiled with shm_open support.\n"); + #else + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); + #endif +#else + SAYF("Compiled with shmat support.\n"); +#endif + SAYF("\n"); + + SAYF( + "Do not be overwhelmed :) afl-cc uses good defaults if no options are " + "selected.\n" + "Read the documentation for FEATURES though, all are good but few are " + "defaults.\n" + "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " + "with\n" + "AFL_LLVM_CMPLOG and " + "AFL_LLVM_DICT2FILE+AFL_LLVM_DICT2FILE_NO_MAIN.\n\n"); + + if (LLVM_MAJOR < 13) { + + SAYF( + "Warning: It is highly recommended to use at least LLVM version 13 " + "(or better, higher) rather than %d!\n\n", + LLVM_MAJOR); + + } + + exit(1); + + } + +} + +/* + Process params passed to afl-cc. + + We have two working modes, *scan* and *non-scan*. In scan mode, + the main task is to set some variables in aflcc according to current argv[i], + while in non-scan mode, is to choose keep or drop current argv[i]. + + We have several matching routines being called sequentially in the while-loop, + and each of them try to parse and match current argv[i] according to their own + rules. If one miss match, the next will then take over. In non-scan mode, each + argv[i] mis-matched by all the routines will be kept. + + These routines are: + 1. parse_misc_params + 2. parse_fsanitize + 3. parse_linking_params + 4. `if (*cur == '@') {...}`, i.e., parse response files +*/ +static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc, + char **argv) { + +<<<<<<< HEAD + u8 skip_next = 0; + while (--argc) { + u8 *cur = *(++argv); + if (skip_next > 0) { + skip_next--; + continue; + } + if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; + if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; + if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) + continue; + if (*cur == '@') { + u8 *filename = cur + 1; + if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } + FILE *f = fopen(filename, "r"); + if (!f) { + if (!scan) insert_param(aflcc, cur); + continue; + } + struct stat st; + if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) { + fclose(f); + if (!scan) insert_param(aflcc, cur); + continue; + } + static u32 rsp_count = 2000; + if (scan) { + if (rsp_count == 0) FATAL("Too many response files provided!"); + --rsp_count; + } + u32 argc_read = 1; + char **argv_read = ck_alloc(sizeof(char *)); + argv_read[0] = ""; + char *arg_buf = NULL; + u64 arg_len = 0; + enum fsm_state { +======= + // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]); + + /* Process the argument list. */ + + u8 skip_next = 0; + while (--argc) { + + u8 *cur = *(++argv); + + if (skip_next > 0) { + + skip_next--; + continue; + + } + + if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; + + if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; + + if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) + continue; + + /* Response file support -----BEGIN----- + We have two choices - move everything to the command line or + rewrite the response files to temporary files and delete them + afterwards. We choose the first for easiness. + For clang, llvm::cl::ExpandResponseFiles does this, however it + only has C++ interface. And for gcc there is expandargv in libiberty, + written in C, but we can't simply copy-paste since its LGPL licensed. + So here we use an equivalent FSM as alternative, and try to be compatible + with the two above. See: + - https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html + - driver::expand_at_files in gcc.git/gcc/gcc.c + - expandargv in gcc.git/libiberty/argv.c + - llvm-project.git/clang/tools/driver/driver.cpp + - ExpandResponseFiles in + llvm-project.git/llvm/lib/Support/CommandLine.cpp + */ + if (*cur == '@') { + + u8 *filename = cur + 1; + if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } + + // Check not found or empty? let the compiler complain if so. + FILE *f = fopen(filename, "r"); + if (!f) { + + if (!scan) insert_param(aflcc, cur); + continue; + + } + + struct stat st; + if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) { + + fclose(f); + if (!scan) insert_param(aflcc, cur); + continue; + + } + + // Limit the number of response files, the max value + // just keep consistent with expandargv. Only do this in + // scan mode, and not touch rsp_count anymore in the next. + static u32 rsp_count = 2000; + if (scan) { + + if (rsp_count == 0) FATAL("Too many response files provided!"); + + --rsp_count; + + } + + // argc, argv acquired from this rsp file. Note that + // process_params ignores argv[0], we need to put a const "" here. + u32 argc_read = 1; + char **argv_read = ck_alloc(sizeof(char *)); + argv_read[0] = ""; + + char *arg_buf = NULL; + u64 arg_len = 0; + + enum fsm_state { + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 + fsm_whitespace, // whitespace seen so far + fsm_double_quote, // have unpaired double quote + fsm_single_quote, // have unpaired single quote + fsm_backslash, // a backslash is seen with no unpaired quote + fsm_normal // a normal char is seen +<<<<<<< HEAD + }; +======= + + }; + + // Workaround to append c to arg buffer, and append the buffer to argv +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 +#define ARG_ALLOC(c) \ + do { \ + \ + ++arg_len; \ + arg_buf = ck_realloc(arg_buf, (arg_len + 1) * sizeof(char)); \ + arg_buf[arg_len] = '\0'; \ + arg_buf[arg_len - 1] = (char)c; \ + \ + } while (0) + +#define ARG_STORE() \ + do { \ + \ + ++argc_read; \ + argv_read = ck_realloc(argv_read, argc_read * sizeof(char *)); \ + argv_read[argc_read - 1] = arg_buf; \ + arg_buf = NULL; \ + arg_len = 0; \ + \ + } while (0) + + int cur_chr = (int)' '; // init as whitespace, as a good start :) + enum fsm_state state_ = fsm_whitespace; + + while (cur_chr != EOF) { + + switch (state_) { + + case fsm_whitespace: + + if (arg_buf) { + + ARG_STORE(); + break; + + } + + if (isspace(cur_chr)) { + + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_single_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'"') { + + state_ = fsm_double_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\\') { + + state_ = fsm_backslash; + cur_chr = fgetc(f); + + } else { + + state_ = fsm_normal; + + } + + break; + + case fsm_normal: + + if (isspace(cur_chr)) { + + state_ = fsm_whitespace; + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_single_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\"') { + + state_ = fsm_double_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\\') { + + state_ = fsm_backslash; + cur_chr = fgetc(f); + + } else { + + ARG_ALLOC(cur_chr); + cur_chr = fgetc(f); + + } + + break; + + case fsm_backslash: + + ARG_ALLOC(cur_chr); + cur_chr = fgetc(f); + state_ = fsm_normal; + + break; + + case fsm_single_quote: + + if (cur_chr == (int)'\\') { + + cur_chr = fgetc(f); + if (cur_chr == EOF) break; + ARG_ALLOC(cur_chr); + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_normal; + + } else { + + ARG_ALLOC(cur_chr); + + } + + cur_chr = fgetc(f); + break; + + case fsm_double_quote: + + if (cur_chr == (int)'\\') { + + cur_chr = fgetc(f); + if (cur_chr == EOF) break; + ARG_ALLOC(cur_chr); + + } else if (cur_chr == (int)'"') { + + state_ = fsm_normal; + + } else { + + ARG_ALLOC(cur_chr); + + } + + cur_chr = fgetc(f); + break; + + default: + break; + + } + + } + + if (arg_buf) { ARG_STORE(); } // save the pending arg after EOF + +#undef ARG_ALLOC +#undef ARG_STORE + + if (argc_read > 1) { process_params(aflcc, scan, argc_read, argv_read); } + + // We cannot free argv_read[] unless we don't need to keep any + // reference in cc_params. Never free argv[0], the const "". + if (scan) { + + while (argc_read > 1) + ck_free(argv_read[--argc_read]); + + ck_free(argv_read); + + } + + continue; + + } /* Response file support -----END----- */ + + if (!scan) insert_param(aflcc, cur); + + } + +} + +/* Process each of the existing argv, also add a few new args. */ +static void edit_params(aflcc_state_t *aflcc, u32 argc, char **argv, + char **envp) { +<<<<<<< HEAD + add_real_argv0(aflcc); + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) { + insert_param(aflcc, "-Wno-unused-command-line-argument"); + } + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { + add_assembler(aflcc); + } + if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } + if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { + if (aflcc->lto_mode && aflcc->have_instr_env) { + load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); + } + if (getenv("AFL_LLVM_DICT2FILE")) { + load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); + } + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + load_llvm_pass(aflcc, "split-switches-pass.so"); + } + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + load_llvm_pass(aflcc, "compare-transform-pass.so"); + } + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + load_llvm_pass(aflcc, "split-compares-pass.so"); + } + if (aflcc->cmplog_mode) { + insert_param(aflcc, "-fno-inline"); + load_llvm_pass(aflcc, "cmplog-switches-pass.so"); + load_llvm_pass(aflcc, "split-switches-pass.so"); + } + if (aflcc->lto_mode) { + insert_param(aflcc, aflcc->lto_flag); + if (!aflcc->have_c) { + add_lto_linker(aflcc); + add_lto_passes(aflcc); + } + } else { + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + add_optimized_pcguard(aflcc); + } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + add_native_pcguard(aflcc); + } else { + load_llvm_pass(aflcc, "afl-llvm-pass.so"); + } + } +======= + + add_real_argv0(aflcc); + + // prevent unnecessary build errors + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) { + + insert_param(aflcc, "-Wno-unused-command-line-argument"); + + } + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { + + add_assembler(aflcc); + + } + + if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } + + if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { + + if (aflcc->lto_mode && aflcc->have_instr_env) { + + load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); + + } + + if (getenv("AFL_LLVM_DICT2FILE")) { + + load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); + + } + + // laf + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + + load_llvm_pass(aflcc, "split-switches-pass.so"); + + } + + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + + load_llvm_pass(aflcc, "compare-transform-pass.so"); + + } + + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + + load_llvm_pass(aflcc, "split-compares-pass.so"); + + } + + // /laf + + if (aflcc->cmplog_mode) { + + insert_param(aflcc, "-fno-inline"); + + load_llvm_pass(aflcc, "cmplog-switches-pass.so"); + // reuse split switches from laf + load_llvm_pass(aflcc, "split-switches-pass.so"); + + } + + // #if LLVM_MAJOR >= 13 + // // Use the old pass manager in LLVM 14 which the AFL++ passes still + // use. insert_param(aflcc, "-flegacy-pass-manager"); + // #endif + + if (aflcc->lto_mode) { + + insert_param(aflcc, aflcc->lto_flag); + + if (!aflcc->have_c) { + + add_lto_linker(aflcc); + add_lto_passes(aflcc); + + } + + } else { + + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + + add_optimized_pcguard(aflcc); + + } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + + add_native_pcguard(aflcc); + + } else { + + load_llvm_pass(aflcc, "afl-llvm-pass.so"); + + } + + } + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 + if (aflcc->cmplog_mode) { + + load_llvm_pass(aflcc, "cmplog-instructions-pass.so"); + load_llvm_pass(aflcc, "cmplog-routines-pass.so"); + + } + + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_SQL") || + getenv("AFL_LLVM_INJECTIONS_LDAP") || + getenv("AFL_LLVM_INJECTIONS_XSS")) { + + load_llvm_pass(aflcc, "injection-pass.so"); + + } + + // insert_param(aflcc, "-Qunused-arguments"); + + } + + /* Inspect the command line parameters. */ + + process_params(aflcc, 0, argc, argv); + + add_sanitizers(aflcc, envp); + + add_misc_params(aflcc); + + add_defs_common(aflcc); + add_defs_selective_instr(aflcc); + add_defs_persistent_mode(aflcc); + + add_runtime(aflcc); + + insert_param(aflcc, NULL); + +} + +/* Main entry point */ +classDiagram + class AFLCCMain { + +main(argc, argv, envp) + +initializeState() + +checkEnvironmentVariables() + +findDependencies() + +determineCompilerMode() + +determineInstrumentationMode() + +finalizeMode() + +processParameters() + +maybeShowUsage() + +notifyMode() + +debugArguments() + +editParameters() + +executeCompiler() + } + + class AFLCCState { + -cc_params: char** + -cc_par_cnt: u32 + -passthrough: u8 + -debug: u8 + +aflcc_state_init(state, prog_name) + } + + class EnvironmentChecker { + +check_environment_vars(envp) + } + + class DependencyFinder { + +find_built_deps(aflcc) + } + + class CompilerModeHandler { + +compiler_mode_by_callname(aflcc) + +compiler_mode_by_environ(aflcc) + +compiler_mode_by_cmdline(aflcc, argc, argv) + } + + class InstrumentationModeHandler { + +instrument_mode_by_environ(aflcc) + } + + class ModeFinalizer { + +mode_final_checkout(aflcc, argc, argv) + } + + class ParameterProcessor { + +process_params(aflcc, start_idx, argc, argv) + } + + class UsageNotifier { + +maybe_usage(aflcc, argc, argv) + } + + class ModeNotifier { + +mode_notification(aflcc) + } + + class Debugger { + +debugf_args(argc, argv) + } + + class ParameterEditor { + +edit_params(aflcc, argc, argv, envp) + } + + class Executor { + +execute_compiler(aflcc, argv) + } + + AFLCCMain --> AFLCCState : 使用 + AFLCCMain --> EnvironmentChecker : 使用 + AFLCCMain --> DependencyFinder : 使用 + AFLCCMain --> CompilerModeHandler : 使用 + AFLCCMain --> InstrumentationModeHandler : 使用 + AFLCCMain --> ModeFinalizer : 使用 + AFLCCMain --> ParameterProcessor : 使用 + AFLCCMain --> UsageNotifier : 使用 + AFLCCMain --> ModeNotifier : 使用 + AFLCCMain --> Debugger : 使用 + AFLCCMain --> ParameterEditor : 使用 + AFLCCMain --> Executor : 使用 \ No newline at end of file -- 2.34.1 From 09b3a9ad22627d8bec2256416afd54663fa3aed4 Mon Sep 17 00:00:00 2001 From: Five <1781919720@qq.com> Date: Sun, 12 Jan 2025 15:56:17 +0800 Subject: [PATCH 4/4] =?UTF-8?q?from=20=E4=BC=8D=E5=92=8C=E6=9E=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AFLplusplus-stable/src/afl-as.c | 615 +++++ src/AFLplusplus-stable/src/afl-cc.c | 3698 +++++++++++++++++++++++++++ 2 files changed, 4313 insertions(+) create mode 100644 src/AFLplusplus-stable/src/afl-as.c create mode 100644 src/AFLplusplus-stable/src/afl-cc.c diff --git a/src/AFLplusplus-stable/src/afl-as.c b/src/AFLplusplus-stable/src/afl-as.c new file mode 100644 index 0000000..f9fe2b9 --- /dev/null +++ b/src/AFLplusplus-stable/src/afl-as.c @@ -0,0 +1,615 @@ +/* + american fuzzy lop++ - wrapper for GNU as + ----------------------------------------- + + Originally written by Michal Zalewski + + Now maintained by Marc Heuse , + Heiko Eissfeldt and + Andrea Fioraldi + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2024 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + https://www.apache.org/licenses/LICENSE-2.0 + + The sole purpose of this wrapper is to preprocess assembly files generated + by GCC / clang and inject the instrumentation bits included from afl-as.h. It + is automatically invoked by the toolchain when compiling programs using + afl-gcc / afl-clang. + + Note that it's an explicit non-goal to instrument hand-written assembly, + be it in separate .s files or in __asm__ blocks. The only aspiration this + utility has right now is to be able to skip them gracefully and allow the + compilation process to continue. + + That said, see utils/clang_asm_normalize/ for a solution that may + allow clang users to make things work even with hand-crafted assembly. Just + note that there is no equivalent for GCC. + + */ + +#define AFL_MAIN + +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" + +#include "afl-as.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u8 **as_params; /* Parameters passed to the real 'as' */ + +static u8 *input_file; /* Originally specified input file */ +static u8 *modified_file; /* Instrumented file for the real 'as' */ + +static u8 be_quiet, /* Quiet mode (no stderr output) */ + clang_mode, /* Running in clang mode? */ + pass_thru, /* Just pass data through? */ + just_version, /* Just show version? */ + sanitizer; /* Using ASAN / MSAN */ + +static u32 inst_ratio = 100, /* Instrumentation probability (%) */ + as_par_cnt = 1; /* Number of params to 'as' */ + +/* If we don't find --32 or --64 in the command line, default to + instrumentation for whichever mode we were compiled with. This is not + perfect, but should do the trick for almost all use cases. */ + +#ifdef WORD_SIZE_64 + +static u8 use_64bit = 1; + +#else + +static u8 use_64bit = 0; + + #ifdef __APPLE__ + #error "Sorry, 32-bit Apple platforms are not supported." + #endif /* __APPLE__ */ + +#endif /* ^WORD_SIZE_64 */ + +/* Examine and modify parameters to pass to 'as'. Note that the file name + is always the last parameter passed by GCC, so we exploit this property + to keep the code simple. */ + +static void edit_params(int argc, char **argv) { + // 获取临时目录路径和AFL_AS环境变量 + u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS"); + u32 i, input_index; + +#ifdef __APPLE__ + u8 use_clang_as = 0; + + // 在MacOS下,若使用clang且没有设置AFL_AS,则使用clang作为汇编器 + if (clang_mode && !afl_as) { + use_clang_as = 1; + afl_as = getenv("AFL_CC"); // 获取AFL_CC环境变量 + if (!afl_as) afl_as = getenv("AFL_CXX"); // 获取AFL_CXX环境变量 + if (!afl_as) afl_as = "clang"; // 默认使用clang + } +#endif + + // 如果TMPDIR环境变量为空,尝试从TEMP和TMP获取临时目录 + if (!tmp_dir) { tmp_dir = getenv("TEMP"); } + if (!tmp_dir) { tmp_dir = getenv("TMP"); } + if (!tmp_dir) { tmp_dir = "/tmp"; } // 默认临时目录为"/tmp" + + // 为汇编参数分配内存,确保不会超过最大参数个数 + as_params = ck_alloc((argc + 32) * sizeof(u8 *)); + if (unlikely((INT_MAX - 32) < argc || !as_params)) { + FATAL("Too many parameters passed to as"); // 参数过多,终止 + } + + // 设置汇编器命令 + as_params[0] = afl_as ? afl_as : (u8 *)"as"; + as_params[argc] = 0; + + // 查找输入文件,通常位于命令行参数的最后 + for (input_index = argc - 1; input_index > 0; input_index--) { + input_file = argv[input_index]; + // 如果遇到调试选项,跳过 + if (strncmp(input_file, "-g", 2)) break; + } + + // 如果没有找到输入文件,输出错误信息 + if (input_index == 0) + FATAL("Could not find input file (not called through afl-gcc?)"); + + // 遍历参数,处理与汇编相关的选项 + for (i = 1; (s32)i < argc; i++) { + if (i == input_index) continue; // 跳过输入文件 + + // 处理64位和32位选项 + if (!strcmp(argv[i], "--64")) { + use_64bit = 1; + } else if (!strcmp(argv[i], "--32")) { + use_64bit = 0; + } + +#ifdef __APPLE__ + // 在MacOS下,处理架构选项 + if (!strcmp(argv[i], "-arch") && i + 1 < (u32)argc) { + if (!strcmp(argv[i + 1], "x86_64")) + use_64bit = 1; + else if (!strcmp(argv[i + 1], "i386")) + FATAL("Sorry, 32-bit Apple platforms are not supported."); + } +#endif + + // 跳过不相关的选项 + if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q"))) + continue; + + // 将参数添加到汇编器参数列表 + as_params[as_par_cnt++] = argv[i]; + } + +#ifdef __APPLE__ + // 如果使用clang作为汇编器,追加相关选项 + if (use_clang_as) { + as_params[as_par_cnt++] = "-c"; + as_params[as_par_cnt++] = "-x"; + as_params[as_par_cnt++] = "assembler"; + } +#endif + + // 如果输入文件是"-"参数,检查是否为版本信息 + if (input_file[0] == '-') { + if (!strcmp(input_file + 1, "-version")) { + just_version = 1; + modified_file = input_file; + goto wrap_things_up; // 跳到结束处理 + } + if (input_file[1]) { + FATAL("Incorrect use (not called through afl-gcc?)"); + } else { + input_file = NULL; // 没有文件参数 + } + } else { + // 检查输入文件是否为临时文件,并决定是否需要通过工具传递 + if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) && + strncmp(input_file, "/var/tmp/", 9) && + strncmp(input_file, "/tmp/", 5) && + getenv("AFL_AS_FORCE_INSTRUMENT") == NULL) { + pass_thru = 1; + } else if (getenv("AFL_AS_FORCE_INSTRUMENT")) { + unsetenv("AFL_AS_FORCE_INSTRUMENT"); // 强制插桩的环境变量取消 + } + } + + // 为汇编文件生成一个唯一的临时文件名 + modified_file = alloc_printf("%s/.afl-%u-%u-%u.s", tmp_dir, (u32)getpid(), + (u32)time(NULL), (u32)random()); +} + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 +wrap_things_up: + + as_params[as_par_cnt++] = modified_file; + as_params[as_par_cnt] = NULL; + +} + +/* Process input file, generate modified_file. Insert instrumentation in all + the appropriate places. */ + +static void add_instrumentation(void) { + + static u8 line[MAX_LINE]; + + FILE *inf; + FILE *outf; + s32 outfd; + u32 ins_lines = 0; + + u8 instr_ok = 0, skip_csect = 0, skip_next_label = 0, skip_intel = 0, + skip_app = 0, instrument_next = 0; + +#ifdef __APPLE__ +<<<<<<< HEAD + u8 *colon_pos; +======= + + u8 *colon_pos; + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 +#endif /* __APPLE__ */ + + if (input_file) { + + inf = fopen(input_file, "r"); + if (!inf) { PFATAL("Unable to read '%s'", input_file); } + + } else { + + inf = stdin; + + } + + outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, DEFAULT_PERMISSION); + + if (outfd < 0) { PFATAL("Unable to write to '%s'", modified_file); } + + outf = fdopen(outfd, "w"); + + if (!outf) { PFATAL("fdopen() failed"); } + + while (fgets(line, MAX_LINE, inf)) { + if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok && + instrument_next && line[0] == '\t' && isalpha(line[1])) { + + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + R(MAP_SIZE)); + + instrument_next = 0; + ins_lines++; + + } + + /* Output the actual line, call it a day in pass-thru mode. */ + + fputs(line, outf); + + if (pass_thru) { continue; } + + /* All right, this is where the actual fun begins. For one, we only want to + instrument the .text section. So, let's keep track of that in processed + files - and let's set instr_ok accordingly. */ + + if (line[0] == '\t' && line[1] == '.') { + + /* OpenBSD puts jump tables directly inline with the code, which is + a bit annoying. They use a specific format of p2align directives + around them, so we use that as a signal. */ + + if (!clang_mode && instr_ok && !strncmp(line + 2, "p2align ", 8) && + isdigit(line[10]) && line[11] == '\n') { + + skip_next_label = 1; + + } + + if (!strncmp(line + 2, "text\n", 5) || + !strncmp(line + 2, "section\t.text", 13) || + !strncmp(line + 2, "section\t__TEXT,__text", 21) || + !strncmp(line + 2, "section __TEXT,__text", 21)) { + + instr_ok = 1; + continue; + + } + + if (!strncmp(line + 2, "section\t", 8) || + !strncmp(line + 2, "section ", 8) || !strncmp(line + 2, "bss\n", 4) || + !strncmp(line + 2, "data\n", 5)) { + + instr_ok = 0; + continue; + + } + + } + + /* Detect off-flavor assembly (rare, happens in gdb). When this is + encountered, we set skip_csect until the opposite directive is + seen, and we do not instrument. */ + + if (strstr(line, ".code")) { + + if (strstr(line, ".code32")) { skip_csect = use_64bit; } + if (strstr(line, ".code64")) { skip_csect = !use_64bit; } + + } + + /* Detect syntax changes, as could happen with hand-written assembly. + Skip Intel blocks, resume instrumentation when back to AT&T. */ + + if (strstr(line, ".intel_syntax")) { skip_intel = 1; } + if (strstr(line, ".att_syntax")) { skip_intel = 0; } + + /* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */ + + if (line[0] == '#' || line[1] == '#') { + + if (strstr(line, "#APP")) { skip_app = 1; } + if (strstr(line, "#NO_APP")) { skip_app = 0; } + + } + + /* If we're in the right mood for instrumenting, check for function + names or conditional labels. This is a bit messy, but in essence, + we want to catch: + + ^main: - function entry point (always instrumented) + ^.L0: - GCC branch label + ^.LBB0_0: - clang branch label (but only in clang mode) + ^\tjnz foo - conditional branches + + ...but not: + + ^# BB#0: - clang comments + ^ # BB#0: - ditto + ^.Ltmp0: - clang non-branch labels + ^.LC0 - GCC non-branch labels + ^.LBB0_0: - ditto (when in GCC mode) + ^\tjmp foo - non-conditional jumps + + Additionally, clang and GCC on MacOS X follow a different convention + with no leading dots on labels, hence the weird maze of #ifdefs + later on. + + */ + + if (skip_intel || skip_app || skip_csect || !instr_ok || line[0] == '#' || + line[0] == ' ') { + + continue; + + } + + /* Conditional branch instruction (jnz, etc). We append the instrumentation + right after the branch (to instrument the not-taken path) and at the + branch destination label (handled later on). */ + + if (line[0] == '\t') { + + if (line[1] == 'j' && line[2] != 'm' && R(100) < (long)inst_ratio) { + + fprintf(outf, use_64bit ? trampoline_fmt_64 : trampoline_fmt_32, + R(MAP_SIZE)); + + ins_lines++; + + } + + continue; + + } + + /* Label of some sort. This may be a branch destination, but we need to + read carefully and account for several different formatting + conventions. */ + +#ifdef __APPLE__ + + /* Apple: L: */ + + if ((colon_pos = strstr(line, ":"))) { + + if (line[0] == 'L' && isdigit(*(colon_pos - 1))) { + +#else + + /* Everybody else: .L: */ + + if (strstr(line, ":")) { + + if (line[0] == '.') { + +#endif /* __APPLE__ */ + + /* .L0: or LBB0_0: style jump destination */ + +#ifdef __APPLE__ + + /* Apple: L / LBB */ + + if ((isdigit(line[1]) || (clang_mode && !strncmp(line, "LBB", 3))) && + R(100) < (long)inst_ratio) { + +#else + + /* Apple: .L / .LBB */ + + if ((isdigit(line[2]) || + (clang_mode && !strncmp(line + 1, "LBB", 3))) && + R(100) < (long)inst_ratio) { + +#endif /* __APPLE__ */ + + /* An optimization is possible here by adding the code only if the + label is mentioned in the code in contexts other than call / jmp. + That said, this complicates the code by requiring two-pass + processing (messy with stdin), and results in a speed gain + typically under 10%, because compilers are generally pretty good + about not generating spurious intra-function jumps. + + We use deferred output chiefly to avoid disrupting + .Lfunc_begin0-style exception handling calculations (a problem on + MacOS X). */ + + if (!skip_next_label) { + + instrument_next = 1; + + } else { + + skip_next_label = 0; + + } + + } + + } else { + + /* Function label (always instrumented, deferred mode). */ + + instrument_next = 1; + + } + + } + + } + + if (ins_lines) { fputs(use_64bit ? main_payload_64 : main_payload_32, outf); } + + if (input_file) { fclose(inf); } + fclose(outf); + + if (!be_quiet) { + + if (!ins_lines) { + + WARNF("No instrumentation targets found%s.", + pass_thru ? " (pass-thru mode)" : ""); + + } else { + + char modeline[100]; + snprintf(modeline, sizeof(modeline), "%s%s%s%s%s%s", + getenv("AFL_HARDEN") ? "hardened" : "non-hardened", + getenv("AFL_USE_ASAN") ? ", ASAN" : "", + getenv("AFL_USE_MSAN") ? ", MSAN" : "", + getenv("AFL_USE_TSAN") ? ", TSAN" : "", + getenv("AFL_USE_UBSAN") ? ", UBSAN" : "", + getenv("AFL_USE_LSAN") ? ", LSAN" : ""); + + OKF("Instrumented %u locations (%s-bit, %s mode, ratio %u%%).", ins_lines, + use_64bit ? "64" : "32", modeline, inst_ratio); + + } + + } + +} + +/* Main entry point */ + +int main(int argc, char **argv) { + + s32 pid; + u32 rand_seed, i, j; + int status; + u8 *inst_ratio_str = getenv("AFL_INST_RATIO"); + + struct timeval tv; + struct timezone tz; + + clang_mode = !!getenv(CLANG_ENV_VAR); + + if ((isatty(2) && !getenv("AFL_QUIET")) || getenv("AFL_DEBUG") != NULL) { + + SAYF(cCYA "afl-as" VERSION cRST " by Michal Zalewski\n"); + + } else { + + be_quiet = 1; + + } + + if (argc < 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { + + fprintf( + stdout, + "afl-as" VERSION + " by Michal Zalewski\n" + "\n%s [-h]\n\n" + "This is a helper application for afl-fuzz. It is a wrapper around GNU " + "'as',\n" + "executed by the toolchain whenever using afl-gcc or afl-clang. You " + "probably\n" + "don't want to run this program directly.\n\n" + + "Rarely, when dealing with extremely complex projects, it may be " + "advisable\n" + "to set AFL_INST_RATIO to a value less than 100 in order to reduce " + "the\n" + "odds of instrumenting every discovered branch.\n\n" + "Environment variables used:\n" + "AFL_AS: path to assembler to use for instrumented files\n" + "AFL_CC: fall back path to assembler\n" + "AFL_CXX: fall back path to assembler\n" + "TMPDIR: directory to use for temporary files\n" + "TEMP: fall back path to directory for temporary files\n" + "TMP: fall back path to directory for temporary files\n" + "AFL_INST_RATIO: user specified instrumentation ratio\n" + "AFL_QUIET: suppress verbose output\n" + "AFL_KEEP_ASSEMBLY: leave instrumented assembly files\n" + "AFL_AS_FORCE_INSTRUMENT: force instrumentation for asm sources\n" + "AFL_HARDEN, AFL_USE_ASAN, AFL_USE_MSAN, AFL_USE_UBSAN, AFL_USE_LSAN:\n" + " used in the instrumentation summary message\n", + argv[0]); + + exit(1); + + } + + gettimeofday(&tv, &tz); + + rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid(); + // in fast systems where pids can repeat in the same seconds we need this + for (i = 1; (s32)i < argc; i++) + for (j = 0; j < strlen(argv[i]); j++) + rand_seed += argv[i][j]; + + srandom(rand_seed); + + edit_params(argc, argv); + + if (inst_ratio_str) { + + if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100) { + + FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)"); + + } + + } + + if (getenv(AS_LOOP_ENV_VAR)) { + + FATAL("Endless loop when calling 'as' (remove '.' from your PATH)"); + + } + + setenv(AS_LOOP_ENV_VAR, "1", 1); + + /* When compiling with ASAN, we don't have a particularly elegant way to skip + ASAN-specific branches. But we can probabilistically compensate for + that... */ + + if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) { + + sanitizer = 1; + if (!getenv("AFL_INST_RATIO")) { inst_ratio /= 3; } + + } + + if (!just_version) { add_instrumentation(); } + + if (!(pid = fork())) { + + execvp(as_params[0], (char **)as_params); + FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]); + + } + + if (pid < 0) { PFATAL("fork() failed"); } + + if (waitpid(pid, &status, 0) <= 0) { PFATAL("waitpid() failed"); } + + if (!getenv("AFL_KEEP_ASSEMBLY")) { unlink(modified_file); } + + exit(WEXITSTATUS(status)); + +} + diff --git a/src/AFLplusplus-stable/src/afl-cc.c b/src/AFLplusplus-stable/src/afl-cc.c new file mode 100644 index 0000000..5dd4c8b --- /dev/null +++ b/src/AFLplusplus-stable/src/afl-cc.c @@ -0,0 +1,3698 @@ +/* + american fuzzy lop++ - compiler instrumentation wrapper + ------------------------------------------------------- + + Written by Michal Zalewski, Laszlo Szekeres and Marc Heuse + + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2024 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + https://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define AFL_MAIN + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE 1 +#endif + +#include "common.h" +#include "config.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "llvm-alternative-coverage.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (LLVM_MAJOR - 0 == 0) + #undef LLVM_MAJOR +#endif +#if !defined(LLVM_MAJOR) + #define LLVM_MAJOR 0 +#endif +#if (LLVM_MINOR - 0 == 0) + #undef LLVM_MINOR +#endif +#if !defined(LLVM_MINOR) + #define LLVM_MINOR 0 +#endif + +#ifndef MAX_PARAMS_NUM + #define MAX_PARAMS_NUM 2048 +#endif + +/** Global declarations -----BEGIN----- **/ + +typedef enum { + + PARAM_MISS, // not matched + PARAM_SCAN, // scan only + PARAM_KEEP, // kept as-is + PARAM_DROP, // ignored + +} param_st; + +typedef enum { + + INSTRUMENT_DEFAULT = 0, + INSTRUMENT_CLASSIC = 1, + INSTRUMENT_AFL = 1, + INSTRUMENT_PCGUARD = 2, + INSTRUMENT_CFG = 3, + INSTRUMENT_LTO = 4, + INSTRUMENT_LLVMNATIVE = 5, + INSTRUMENT_GCC = 6, + INSTRUMENT_CLANG = 7, + INSTRUMENT_OPT_CTX = 8, + INSTRUMENT_OPT_NGRAM = 16, + INSTRUMENT_OPT_CALLER = 32, + INSTRUMENT_OPT_CTX_K = 64, + INSTRUMENT_OPT_CODECOV = 128, + +} instrument_mode_id; + +typedef enum { + + UNSET = 0, + LTO = 1, + LLVM = 2, + GCC_PLUGIN = 3, + GCC = 4, + CLANG = 5 + +} compiler_mode_id; + +static u8 cwd[4096]; + +char instrument_mode_string[18][18] = { + + "DEFAULT", + "CLASSIC", + "PCGUARD", + "CFG", + "LTO", + "PCGUARD-NATIVE", + "GCC", + "CLANG", + "CTX", + "CALLER", + "", + "", + "", + "", + "", + "", + "NGRAM", + "" + +}; + +char compiler_mode_string[7][12] = { + + "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", + "GCC", "CLANG", "" + +}; + +u8 *instrument_mode_2str(instrument_mode_id i) { + + return instrument_mode_string[i]; + +} + +u8 *compiler_mode_2str(compiler_mode_id i) { + + return compiler_mode_string[i]; + +} + +u8 *getthecwd() { + + if (getcwd(cwd, sizeof(cwd)) == NULL) { + + static u8 fail[] = ""; + return fail; + + } + + return cwd; + +} + +typedef struct aflcc_state { + + u8 **cc_params; /* Parameters passed to the real CC */ + u32 cc_par_cnt; /* Param count, including argv0 */ + + u8 *argv0; /* Original argv0 (by strdup) */ + u8 *callname; /* Executable file argv0 indicated */ + + u8 debug; + + u8 compiler_mode, plusplus_mode, lto_mode; + + u8 *lto_flag; + + u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k; + + u8 cmplog_mode; + + u8 have_instr_env, have_gcc, have_clang, have_llvm, have_gcc_plugin, have_lto, + have_optimized_pcguard, have_instr_list; + + u8 fortify_set, x_set, bit_mode, preprocessor_only, have_unroll, have_o, + have_pic, have_c, shared_linking, partial_linking, non_dash, have_fp, + have_flto, have_hidden, have_fortify, have_fcf, have_staticasan, + have_rust_asanrt, have_asan, have_msan, have_ubsan, have_lsan, have_tsan, + have_cfisan; + + // u8 *march_opt; + u8 need_aflpplib; + int passthrough; + + u8 use_stdin; /* dummy */ + u8 *argvnull; /* dummy */ + +} aflcc_state_t; + +void aflcc_state_init(aflcc_state_t *, u8 *argv0); + +u8 *find_object(aflcc_state_t *, u8 *obj); + +void find_built_deps(aflcc_state_t *); + +/* Insert param into the new argv, raise error if MAX_PARAMS_NUM exceeded. */ +static inline void insert_param(aflcc_state_t *aflcc, u8 *param) { + + if (unlikely(aflcc->cc_par_cnt + 1 >= MAX_PARAMS_NUM)) + FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM."); + + aflcc->cc_params[aflcc->cc_par_cnt++] = param; + +} + +/* + Insert a param which contains path to the object file. It uses find_object to + get the path based on the name `obj`, and then uses a sprintf like method to + format it with `fmt`. If `fmt` is NULL, the inserted arg is same as the path. + If `msg` provided, it should be an error msg raised if the path can't be + found. `obj` must not be NULL. +*/ +static inline void insert_object(aflcc_state_t *aflcc, u8 *obj, u8 *fmt, + u8 *msg) { + + u8 *_obj_path = find_object(aflcc, obj); + if (!_obj_path) { + + if (msg) + FATAL("%s", msg); + else + FATAL("Unable to find '%s'", obj); + + } else { + + if (fmt) { + + u8 *_obj_path_fmt = alloc_printf(fmt, _obj_path); + ck_free(_obj_path); + aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path_fmt; + + } else { + + aflcc->cc_params[aflcc->cc_par_cnt++] = _obj_path; + + } + + } + +} + +/* Insert params into the new argv, make clang load the pass. */ +static inline void load_llvm_pass(aflcc_state_t *aflcc, u8 *pass) { + +#if LLVM_MAJOR >= 11 /* use new pass manager */ + #if LLVM_MAJOR < 16 + insert_param(aflcc, "-fexperimental-new-pass-manager"); + #endif + insert_object(aflcc, pass, "-fpass-plugin=%s", 0); +#else + insert_param(aflcc, "-Xclang"); + insert_param(aflcc, "-load"); + insert_param(aflcc, "-Xclang"); + insert_object(aflcc, pass, 0, 0); +#endif + +} + +static inline void debugf_args(int argc, char **argv) { + + DEBUGF("cd '%s';", getthecwd()); + for (int i = 0; i < argc; i++) + SAYF(" '%s'", argv[i]); + SAYF("\n"); + fflush(stdout); + fflush(stderr); + +} + +void compiler_mode_by_callname(aflcc_state_t *); +void compiler_mode_by_environ(aflcc_state_t *); +void compiler_mode_by_cmdline(aflcc_state_t *, int argc, char **argv); +void instrument_mode_by_environ(aflcc_state_t *); +void mode_final_checkout(aflcc_state_t *, int argc, char **argv); +void mode_notification(aflcc_state_t *); + +void add_real_argv0(aflcc_state_t *); + +void add_defs_common(aflcc_state_t *); +void add_defs_selective_instr(aflcc_state_t *); +void add_defs_persistent_mode(aflcc_state_t *); +void add_defs_fortify(aflcc_state_t *, u8); +void add_defs_lsan_ctrl(aflcc_state_t *); + +param_st parse_fsanitize(aflcc_state_t *, u8 *, u8); +void add_sanitizers(aflcc_state_t *, char **envp); +void add_optimized_pcguard(aflcc_state_t *); +void add_native_pcguard(aflcc_state_t *); + +void add_assembler(aflcc_state_t *); +void add_gcc_plugin(aflcc_state_t *); + +param_st parse_misc_params(aflcc_state_t *, u8 *, u8); +void add_misc_params(aflcc_state_t *); + +param_st parse_linking_params(aflcc_state_t *, u8 *, u8, u8 *skip_next, + char **argv); + +void add_lto_linker(aflcc_state_t *); +void add_lto_passes(aflcc_state_t *); +void add_runtime(aflcc_state_t *); + +/** Global declarations -----END----- **/ + +/* + Init global state struct. We also extract the callname, + check debug options and if in C++ mode here. +*/ +void aflcc_state_init(aflcc_state_t *aflcc, u8 *argv0) { + + // Default NULL/0 is a good start + memset(aflcc, 0, sizeof(aflcc_state_t)); + + aflcc->cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *)); + aflcc->cc_par_cnt = 1; + + aflcc->lto_flag = AFL_CLANG_FLTO; + + // aflcc->march_opt = CFLAGS_OPT; + + /* callname & if C++ mode */ + + aflcc->argv0 = ck_strdup(argv0); + + char *cname = NULL; + + if ((cname = strrchr(aflcc->argv0, '/')) != NULL) { + + cname++; + + } else { + + cname = aflcc->argv0; + + } + + aflcc->callname = cname; + + if (strlen(cname) > 2 && (strncmp(cname + strlen(cname) - 2, "++", 2) == 0 || + strstr(cname, "-g++") != NULL)) { + + aflcc->plusplus_mode = 1; + + } + + /* debug */ + + if (getenv("AFL_DEBUG")) { + + aflcc->debug = 1; + if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + + } else if (getenv("AFL_QUIET")) { + + be_quiet = 1; + + } + + if ((getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) && (!aflcc->debug)) { + + be_quiet = 1; + + } + +} + +/* + Try to find a specific runtime we need, in here: + + 1. firstly we check the $AFL_PATH environment variable location if set + 2. next we check argv[0] if it has path information and use it + a) we also check ../lib/afl + 3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + FreeBSD with procfs) + a) and check here in ../lib/afl too + 4. we look into the AFL_PATH define (usually /usr/local/lib/afl) + 5. we finally try the current directory + + if all these attempts fail - we return NULL and the caller has to decide + what to do. Otherwise the path to obj would be allocated and returned. +*/ +u8 *find_object(aflcc_state_t *aflcc, u8 *obj) { + + u8 *argv0 = aflcc->argv0; + + u8 *afl_path = getenv("AFL_PATH"); + u8 *slash = NULL, *tmp; + + if (afl_path) { + + tmp = alloc_printf("%s/%s", afl_path, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + + if (argv0) { + + slash = strrchr(argv0, '/'); + + if (slash) { + + u8 *dir = ck_strdup(argv0); + + slash = strrchr(dir, '/'); + *slash = 0; + + tmp = alloc_printf("%s/%s", dir, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + ck_free(dir); + return tmp; + + } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { + + ck_free(dir); + return tmp; + + } + + ck_free(tmp); + ck_free(dir); + + } + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) || \ + defined(__ANDROID__) || defined(__NetBSD__) + #define HAS_PROC_FS 1 +#endif +#ifdef HAS_PROC_FS + else { + + char *procname = NULL; + #if defined(__FreeBSD__) || defined(__DragonFly__) + procname = "/proc/curproc/file"; + #elif defined(__linux__) || defined(__ANDROID__) + procname = "/proc/self/exe"; + #elif defined(__NetBSD__) + procname = "/proc/curproc/exe"; + #endif + if (procname) { + + char exepath[PATH_MAX]; + ssize_t exepath_len = readlink(procname, exepath, sizeof(exepath)); + if (exepath_len > 0 && exepath_len < PATH_MAX) { + + exepath[exepath_len] = 0; + slash = strrchr(exepath, '/'); + + if (slash) { + + *slash = 0; + tmp = alloc_printf("%s/%s", exepath, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + } + + } + + } + + } + +#endif +#undef HAS_PROC_FS + + } + + tmp = alloc_printf("%s/%s", AFL_PATH, obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + tmp = alloc_printf("./%s", obj); + + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); + + if (!access(tmp, R_OK)) { return tmp; } + + ck_free(tmp); + + if (aflcc->debug) DEBUGF("Trying ... giving up\n"); + + return NULL; + +} + +/* + Deduce some info about compiler toolchains in current system, + from the building results of AFL++ +*/ +void find_built_deps(aflcc_state_t *aflcc) { + + char *ptr = NULL; + +#if defined(__x86_64__) || defined(__i386__) + if ((ptr = find_object(aflcc, "afl-as")) != NULL) { + + #ifndef __APPLE__ + // on OSX clang masquerades as GCC + aflcc->have_gcc = 1; + #endif + aflcc->have_clang = 1; + ck_free(ptr); + + } + +#endif + + if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) { + + aflcc->have_optimized_pcguard = 1; + ck_free(ptr); + + } + +#if (LLVM_MAJOR >= 3) + + if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) { + + aflcc->have_lto = 1; + ck_free(ptr); + + } + + if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) { + + aflcc->have_llvm = 1; + ck_free(ptr); + + } + +#endif + +#ifdef __ANDROID__ + aflcc->have_llvm = 1; +#endif + + if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) { + + aflcc->have_gcc_plugin = 1; + ck_free(ptr); + + } + +#if !defined(__ANDROID__) && !defined(ANDROID) + ptr = find_object(aflcc, "afl-compiler-rt.o"); + + if (!ptr) { + + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); + + } + + if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); } + + ck_free(ptr); +#endif + +} + +/** compiler_mode & instrument_mode selecting -----BEGIN----- **/ + +/* Select compiler_mode by callname, such as "afl-clang-fast", etc. */ +void compiler_mode_by_callname(aflcc_state_t *aflcc) { + + if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) { + + /* afl-clang-fast is always created there by makefile + just like afl-clang, burdened with special purposes: + - If llvm-config is not available (i.e. LLVM_MAJOR is 0), + or too old, it falls back to LLVM-NATIVE mode and let + the actual compiler complain if doesn't work. + - Otherwise try default llvm instruments except LTO. + */ +#if (LLVM_MAJOR >= 3) + aflcc->compiler_mode = LLVM; +#else + aflcc->compiler_mode = CLANG; +#endif + + } else + +#if (LLVM_MAJOR >= 3) + + if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 || + + strncmp(aflcc->callname, "afl-lto", 7) == 0) { + + aflcc->compiler_mode = LTO; + + } else + +#endif + + if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 || + + strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 || + + strncmp(aflcc->callname, "afl-g++", 7) == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strcmp(aflcc->callname, "afl-clang") == 0 || + + strcmp(aflcc->callname, "afl-clang++") == 0) { + + aflcc->compiler_mode = CLANG; + + } + +} + +/* + Select compiler_mode by env AFL_CC_COMPILER. And passthrough mode can be + regarded as a special compiler_mode, so we check for it here, too. +*/ +void compiler_mode_by_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + + aflcc->passthrough = 1; + + } + + char *ptr = getenv("AFL_CC_COMPILER"); + + if (!ptr) { return; } + + if (aflcc->compiler_mode) { + + if (!be_quiet) { + + WARNF( + "\"AFL_CC_COMPILER\" is set but a specific compiler was already " + "selected by command line parameter or symlink, ignoring the " + "environment variable!"); + + } + + } else { + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + aflcc->compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + aflcc->compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strcasecmp(ptr, "CLANG") == 0) { + + aflcc->compiler_mode = CLANG; + + } else + + FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); + + } + +} + +/* + Select compiler_mode by command line options --afl-... + If it can be inferred, instrument_mode would also be set. + This can supersedes previous result based on callname + or AFL_CC_COMPILER. And "--afl_noopt"/"--afl-noopt" will + be overwritten by "-g". +*/ +void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) { + + char *ptr = NULL; + + for (int i = 1; i < argc; i++) { + + if (strncmp(argv[i], "--afl", 5) == 0) { + + if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) { + + aflcc->passthrough = 1; + argv[i] = "-g"; // we have to overwrite it, -g is always good + continue; + + } + + if (aflcc->compiler_mode && !be_quiet) { + + WARNF( + "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " + "symlink compiler selection!"); + + } + + ptr = argv[i]; + ptr += 5; + while (*ptr == '-') + ptr++; + + if (strncasecmp(ptr, "LTO", 3) == 0) { + + aflcc->compiler_mode = LTO; + + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + + aflcc->compiler_mode = LLVM; + + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + + strncasecmp(ptr, "PC-GUARD", 8) == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } else if (strcasecmp(ptr, "INSTRIM") == 0 || + + strcasecmp(ptr, "CFG") == 0) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and " + "PCGUARD (default in afl-cc).\n"); + + } else if (strcasecmp(ptr, "AFL") == 0 || + + strcasecmp(ptr, "CLASSIC") == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + + strcasecmp(ptr, "NATIVE") == 0 || + strcasecmp(ptr, "LLVM-NATIVE") == 0) { + + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { + + aflcc->compiler_mode = GCC_PLUGIN; + + } else if (strcasecmp(ptr, "GCC") == 0) { + + aflcc->compiler_mode = GCC; + + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { + + aflcc->compiler_mode = CLANG; + + } else + + FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); + + } + + } + +} + +/* + Select instrument_mode by those envs in old style: + - USE_TRACE_PC, AFL_USE_TRACE_PC, AFL_LLVM_USE_TRACE_PC, AFL_TRACE_PC + - AFL_LLVM_CALLER, AFL_LLVM_CTX, AFL_LLVM_CTX_K + - AFL_LLVM_NGRAM_SIZE +*/ +static void instrument_mode_old_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || + getenv("INSTRIM_LIB")) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD " + "(default in afl-cc).\n"); + + } + + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || + getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { + + if (aflcc->instrument_mode == 0) + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + else if (aflcc->instrument_mode != INSTRUMENT_PCGUARD) + FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); + + } + + if (getenv("AFL_LLVM_CTX")) aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; + if (getenv("AFL_LLVM_CALLER") || getenv("AFL_LLVM_LTO_CALLER") || + getenv("AFL_LLVM_LTO_CTX")) + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + if (getenv("AFL_LLVM_NGRAM_SIZE")) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; + aflcc->ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); + if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) + FATAL( + "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " + "(%u)", + NGRAM_SIZE_MAX); + + } + + if (getenv("AFL_LLVM_CTX_K")) { + + aflcc->ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); + if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) + FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", + CTX_MAX_K); + if (aflcc->ctx_k == 1) { + + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + + } else { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; + + } + + } + +} + +/* + Select instrument_mode by env 'AFL_LLVM_INSTRUMENT'. + Previous compiler_mode will be superseded, if required by some + values of instrument_mode. +*/ +static void instrument_mode_new_environ(aflcc_state_t *aflcc) { + + if (!getenv("AFL_LLVM_INSTRUMENT")) { return; } + + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + + while (ptr2) { + + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { + + if (aflcc->instrument_mode == INSTRUMENT_LTO) { + + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->lto_mode = 1; + + } else if (!aflcc->instrument_mode || + + aflcc->instrument_mode == INSTRUMENT_AFL) { + + aflcc->instrument_mode = INSTRUMENT_AFL; + + } else { + + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_PCGUARD) + + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 || + strncasecmp(ptr2, "native", strlen("native")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 || + strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) { + + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CODECOV; + + } else { + + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || + strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { + + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and " + "PCGUARD (default in afl-cc).\n"); + + } + + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { + + aflcc->lto_mode = 1; + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO) + + aflcc->instrument_mode = INSTRUMENT_LTO; + + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + if (strcasecmp(ptr2, "gcc") == 0) { + + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC) + + aflcc->instrument_mode = INSTRUMENT_GCC; + + else if (aflcc->instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + aflcc->compiler_mode = GCC; + + } + + if (strcasecmp(ptr2, "clang") == 0) { + + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG) + + aflcc->instrument_mode = INSTRUMENT_CLANG; + + else if (aflcc->instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); + + aflcc->compiler_mode = CLANG; + + } + + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 || + strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 || + strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) { + + u8 *ptr3 = ptr2; + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_CTX_K")) == NULL) + FATAL( + "you must set the K-CTX K with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ctx-2"); + + } + + aflcc->ctx_k = atoi(ptr3); + if (aflcc->ctx_k < 1 || aflcc->ctx_k > CTX_MAX_K) + FATAL( + "K-CTX instrumentation option must be between 1 and CTX_MAX_K " + "(%u)", + CTX_MAX_K); + + if (aflcc->ctx_k == 1) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + unsetenv("AFL_LLVM_CTX_K"); + + } else { + + aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); + u8 *ptr4 = alloc_printf("%u", aflcc->ctx_k); + setenv("AFL_LLVM_CTX_K", ptr4, 1); + + } + + } + + if (strcasecmp(ptr2, "ctx") == 0) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); + + } + + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { + + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); + + } + + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { + + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) { + + ptr3++; + + } + + if (!*ptr3) { + + if ((ptr3 = getenv("AFL_LLVM_NGRAM_SIZE")) == NULL) + FATAL( + "you must set the NGRAM size with (e.g. for value 2) " + "AFL_LLVM_INSTRUMENT=ngram-2"); + + } + + aflcc->ngram_size = atoi(ptr3); + + if (aflcc->ngram_size < 2 || aflcc->ngram_size > NGRAM_SIZE_MAX) { + + FATAL( + "NGRAM instrumentation option must be between 2 and " + "NGRAM_SIZE_MAX (%u)", + NGRAM_SIZE_MAX); + + } + + aflcc->instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); + u8 *ptr4 = alloc_printf("%u", aflcc->ngram_size); + setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); + + } + + ptr2 = strtok(NULL, ":,;"); + + } + +} + +/* + Select instrument_mode by envs, the top wrapper. We check + have_instr_env firstly, then call instrument_mode_old_environ + and instrument_mode_new_environ sequentially. +*/ +void instrument_mode_by_environ(aflcc_state_t *aflcc) { + + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { + + aflcc->have_instr_env = 1; + + } + + if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) { + + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); + + } + + instrument_mode_old_environ(aflcc); + instrument_mode_new_environ(aflcc); + +} + +/* + Workaround to ensure CALLER, CTX, K-CTX and NGRAM + instrumentation were used correctly. +*/ +static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) { + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + + FATAL("you cannot set CTX and CALLER together"); + + } + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CTX and K-CTX together"); + + } + + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + + FATAL("you cannot set CALLER and K-CTX together"); + + } + + if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM && + !((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + aflcc->compiler_mode == LTO)) + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); + + if (aflcc->instrument_opt_mode && + aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV && + aflcc->instrument_mode != INSTRUMENT_CLASSIC && + !(aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER && + aflcc->compiler_mode == LTO)) + FATAL( + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); + +} + +/* + Last step of compiler_mode & instrument_mode selecting. + We have a few of workarounds here, to check any corner cases, + prepare for a series of fallbacks, and raise warnings or errors. +*/ +void mode_final_checkout(aflcc_state_t *aflcc, int argc, char **argv) { + + if (aflcc->instrument_opt_mode && + aflcc->instrument_mode == INSTRUMENT_DEFAULT && + (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == UNSET)) { + + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->compiler_mode = LLVM; + + } + + if (!aflcc->compiler_mode) { + + // lto is not a default because outside of afl-cc RANLIB and AR have to + // be set to LLVM versions so this would work + if (aflcc->have_llvm) + aflcc->compiler_mode = LLVM; + else if (aflcc->have_gcc_plugin) + aflcc->compiler_mode = GCC_PLUGIN; + else if (aflcc->have_gcc) + aflcc->compiler_mode = GCC; + else if (aflcc->have_clang) + aflcc->compiler_mode = CLANG; + else if (aflcc->have_lto) + aflcc->compiler_mode = LTO; + else + FATAL("no compiler mode available"); + + } + + switch (aflcc->compiler_mode) { + + case GCC: + if (!aflcc->have_gcc) FATAL("afl-gcc is not available on your platform!"); + break; + case CLANG: + if (!aflcc->have_clang) + FATAL("afl-clang is not available on your platform!"); + break; + case LLVM: + if (!aflcc->have_llvm) + FATAL( + "LLVM mode is not available, please install LLVM 13+ and recompile " + "AFL++"); + break; + case GCC_PLUGIN: + if (!aflcc->have_gcc_plugin) + FATAL( + "GCC_PLUGIN mode is not available, install gcc plugin support and " + "recompile AFL++"); + break; + case LTO: + if (!aflcc->have_lto) + FATAL( + "LTO mode is not available, please install LLVM 13+ and lld of the " + "same version and recompile AFL++"); + break; + default: + FATAL("no compiler mode available"); + + } + + if (aflcc->compiler_mode == GCC) { aflcc->instrument_mode = INSTRUMENT_GCC; } + + if (aflcc->compiler_mode == CLANG) { + + /* if our PCGUARD implementation is not available then silently switch to + native LLVM PCGUARD. Or classic asm instrument is explicitly preferred. */ + if (!aflcc->have_optimized_pcguard && + (aflcc->instrument_mode == INSTRUMENT_DEFAULT || + aflcc->instrument_mode == INSTRUMENT_PCGUARD)) { + + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else { + + aflcc->instrument_mode = INSTRUMENT_CLANG; + setenv(CLANG_ENV_VAR, "1", 1); // used by afl-as + + } + + } + + if (aflcc->compiler_mode == LTO) { + + if (aflcc->instrument_mode == 0 || + aflcc->instrument_mode == INSTRUMENT_LTO || + aflcc->instrument_mode == INSTRUMENT_CFG || + aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + + aflcc->lto_mode = 1; + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } else if (aflcc->instrument_mode == INSTRUMENT_CLASSIC) { + + aflcc->lto_mode = 1; + + } else { + + if (!be_quiet) { + + WARNF("afl-clang-lto called with mode %s, using that mode instead", + instrument_mode_2str(aflcc->instrument_mode)); + + } + + } + + } + + if (aflcc->instrument_mode == 0 && aflcc->compiler_mode < GCC_PLUGIN) { + +#if LLVM_MAJOR >= 7 + #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + if (aflcc->have_instr_env) { + + aflcc->instrument_mode = INSTRUMENT_AFL; + if (!be_quiet) { + + WARNF( + "Switching to classic instrumentation because " + "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); + + } + + } else + + #endif + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + +#else + aflcc->instrument_mode = INSTRUMENT_AFL; +#endif + + } + + if (!aflcc->instrument_opt_mode && aflcc->lto_mode && + aflcc->instrument_mode == INSTRUMENT_CFG) { + + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + + } + +#ifndef AFL_CLANG_FLTO + if (aflcc->lto_mode) + FATAL( + "instrumentation mode LTO specified but LLVM support not available " + "(requires LLVM 11 or higher)"); +#endif + + if (aflcc->lto_mode) { + + if (aflcc->lto_flag[0] != '-') + FATAL( + "Using afl-clang-lto is not possible because Makefile magic did not " + "identify the correct -flto flag"); + else + aflcc->compiler_mode = LTO; + + } + + if (getenv("AFL_LLVM_SKIP_NEVERZERO") && getenv("AFL_LLVM_NOT_ZERO")) + FATAL( + "AFL_LLVM_NOT_ZERO and AFL_LLVM_SKIP_NEVERZERO can not be set " + "together"); + +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) + + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD && aflcc->have_instr_env) { + + FATAL( + "Instrumentation type PCGUARD does not support " + "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + + } + +#endif + + instrument_opt_mode_exclude(aflcc); + + u8 *ptr2; + + if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') + FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); + + if (getenv("AFL_LLVM_LAF_ALL")) { + + setenv("AFL_LLVM_LAF_SPLIT_SWITCHES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_COMPARES", "1", 1); + setenv("AFL_LLVM_LAF_SPLIT_FLOATS", "1", 1); + setenv("AFL_LLVM_LAF_TRANSFORM_COMPARES", "1", 1); + + } + + if (getenv("AFL_LLVM_DICT2FILE") && + (getenv("AFL_LLVM_LAF_SPLIT_SWITCHES") || + getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES"))) + FATAL("AFL_LLVM_DICT2FILE is incompatible with AFL_LLVM_LAF_*"); + + aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || + getenv("AFL_GCC_CMPLOG"); + +} + +/* + Print welcome message on screen, giving brief notes about + compiler_mode and instrument_mode. +*/ +void mode_notification(aflcc_state_t *aflcc) { + + char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k); + + char *ptr1 = alloc_printf( + "%s%s%s%s%s", instrument_mode_2str(aflcc->instrument_mode), + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); + + ck_free(ptr2); + ck_free(ptr3); + + if ((isatty(2) && !be_quiet) || aflcc->debug) { + + SAYF(cCYA + "afl-cc" VERSION cRST + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", + compiler_mode_2str(aflcc->compiler_mode), ptr1); + + } + + ck_free(ptr1); + + if (!be_quiet && + (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG)) { + + WARNF( + "You are using outdated instrumentation, install LLVM and/or " + "gcc-plugin and use afl-clang-fast/afl-clang-lto/afl-gcc-fast " + "instead!"); + + } + +} + +/* + Set argv[0] required by execvp. It can be + - specified by env AFL_CXX + - g++ or clang++ + - CLANGPP_BIN or LLVM_BINDIR/clang++ + when in C++ mode, or + - specified by env AFL_CC + - gcc or clang + - CLANG_BIN or LLVM_BINDIR/clang + otherwise. +*/ +void add_real_argv0(aflcc_state_t *aflcc) { + + static u8 llvm_fullpath[PATH_MAX]; + + if (aflcc->plusplus_mode) { + + u8 *alt_cxx = getenv("AFL_CXX"); + + if (!alt_cxx) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { + + alt_cxx = "g++"; + + } else if (aflcc->compiler_mode == CLANG) { + + alt_cxx = "clang++"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang++", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANGPP_BIN); + alt_cxx = llvm_fullpath; + + } + + } + + aflcc->cc_params[0] = alt_cxx; + + } else { + + u8 *alt_cc = getenv("AFL_CC"); + + if (!alt_cc) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { + + alt_cc = "gcc"; + + } else if (aflcc->compiler_mode == CLANG) { + + alt_cc = "clang"; + + } else { + + if (USE_BINDIR) + snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", + LLVM_BINDIR); + else + snprintf(llvm_fullpath, sizeof(llvm_fullpath), CLANG_BIN); + alt_cc = llvm_fullpath; + + } + + } + + aflcc->cc_params[0] = alt_cc; + + } + +} + +/** compiler_mode & instrument_mode selecting -----END----- **/ + +/** Macro defs for the preprocessor -----BEGIN----- **/ + +void add_defs_common(aflcc_state_t *aflcc) { + + insert_param(aflcc, "-D__AFL_COMPILER=1"); + insert_param(aflcc, "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"); + +} + +/* + __afl_coverage macro defs. See + instrumentation/README.instrument_list.md# + 2-selective-instrumentation-with-_afl_coverage-directives +*/ +void add_defs_selective_instr(aflcc_state_t *aflcc) { + + if (aflcc->plusplus_mode) { + + insert_param(aflcc, + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "extern \"C\" void __afl_coverage_discard();" + "extern \"C\" void __afl_coverage_skip();" + "extern \"C\" void __afl_coverage_on();" + "extern \"C\" void __afl_coverage_off();"); + + } else { + + insert_param(aflcc, + "-D__AFL_COVERAGE()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_skip();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"); + + } + + insert_param( + aflcc, + "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " + "1;"); + insert_param(aflcc, "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"); + insert_param(aflcc, "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"); + insert_param(aflcc, "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"); + insert_param(aflcc, "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"); + +} + +/* + Macro defs for persistent mode. As documented in + instrumentation/README.persistent_mode.md, deferred forkserver initialization + and persistent mode are not available in afl-gcc and afl-clang. +*/ +void add_defs_persistent_mode(aflcc_state_t *aflcc) { + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) return; + + insert_param(aflcc, "-D__AFL_HAVE_MANUAL_CONTROL=1"); + + /* When the user tries to use persistent or deferred forkserver modes by + appending a single line to the program, we want to reliably inject a + signature into the binary (to be picked up by afl-fuzz) and we want + to call a function from the runtime .o file. This is unnecessarily + painful for three reasons: + + 1) We need to convince the compiler not to optimize out the signature. + This is done with __attribute__((used)). + + 2) We need to convince the linker, when called with -Wl,--gc-sections, + not to do the same. This is done by forcing an assignment to a + 'volatile' pointer. + + 3) We need to declare __afl_persistent_loop() in the global namespace, + but doing this within a method in a class is hard - :: and extern "C" + are forbidden and __attribute__((alias(...))) doesn't work. Hence the + __asm__ aliasing trick. + + */ + + insert_param(aflcc, + "-D__AFL_FUZZ_INIT()=" + "int __afl_sharedmem_fuzzing = 1;" + "extern __attribute__((visibility(\"default\"))) " + "unsigned int *__afl_fuzz_len;" + "extern __attribute__((visibility(\"default\"))) " + "unsigned char *__afl_fuzz_ptr;" + "unsigned char __afl_fuzz_alt[1048576];" + "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"); + + insert_param(aflcc, + "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " + "__afl_fuzz_alt_ptr)"); + + insert_param( + aflcc, + "-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : " + "(*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff " + "? 0 : *__afl_fuzz_len)"); + + insert_param( + aflcc, + "-D__AFL_LOOP(_A)=" + "({ static volatile const char *_B __attribute__((used,unused)); " + " _B = (const char*)\"" PERSIST_SIG + "\"; " + "extern __attribute__((visibility(\"default\"))) int __afl_connected;" +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " +#else + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " +#endif /* ^__APPLE__ */ + // if afl is connected, we run _A times, else once. + "_L(__afl_connected ? _A : 1); })"); + + insert_param( + aflcc, + "-D__AFL_INIT()=" + "do { static volatile const char *_A __attribute__((used,unused)); " + " _A = (const char*)\"" DEFER_SIG + "\"; " +#ifdef __APPLE__ + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " +#else + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " +#endif /* ^__APPLE__ */ + "_I(); } while (0)"); + +} + +/* + Control macro def of _FORTIFY_SOURCE. It will do nothing + if we detect this routine has been called previously, or + the macro already here in these existing args. +*/ +void add_defs_fortify(aflcc_state_t *aflcc, u8 action) { + + if (aflcc->have_fortify) { return; } + + switch (action) { + + case 1: + insert_param(aflcc, "-D_FORTIFY_SOURCE=1"); + break; + + case 2: + insert_param(aflcc, "-D_FORTIFY_SOURCE=2"); + break; + + default: // OFF + insert_param(aflcc, "-U_FORTIFY_SOURCE"); + break; + + } + + aflcc->have_fortify = 1; + +} + +/* Macro defs of __AFL_LEAK_CHECK, __AFL_LSAN_ON and __AFL_LSAN_OFF */ +void add_defs_lsan_ctrl(aflcc_state_t *aflcc) { + + insert_param(aflcc, "-includesanitizer/lsan_interface.h"); + insert_param( + aflcc, + "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) " + "_exit(23); }"); + insert_param(aflcc, "-D__AFL_LSAN_OFF()=__lsan_disable();"); + insert_param(aflcc, "-D__AFL_LSAN_ON()=__lsan_enable();"); + +} + +/** Macro defs for the preprocessor -----END----- **/ + +/** About -fsanitize -----BEGIN----- **/ + +/* For input "-fsanitize=...", it: + + 1. may have various OOB traps :) if ... doesn't contain ',' or + the input has bad syntax such as "-fsantiz=," + 2. strips any fuzzer* in ... and writes back (may result in "-fsanitize=") + 3. rets 1 if exactly "fuzzer" found, otherwise rets 0 +*/ +static u8 fsanitize_fuzzer_comma(char *string) { + + u8 detect_single_fuzzer = 0; + + char *p, *ptr = string + strlen("-fsanitize="); + // ck_alloc will check alloc failure + char *new = ck_alloc(strlen(string) + 1); + char *tmp = ck_alloc(strlen(ptr) + 1); + u32 count = 0, len, ende = 0; + + strcpy(new, "-fsanitize="); + + do { + + p = strchr(ptr, ','); + if (!p) { + + p = ptr + strlen(ptr) + 1; + ende = 1; + + } + + len = p - ptr; + if (len) { + + strncpy(tmp, ptr, len); + tmp[len] = 0; + // fprintf(stderr, "Found: %s\n", tmp); + ptr += len + 1; + if (*tmp) { + + u32 copy = 1; + if (!strcmp(tmp, "fuzzer")) { + + detect_single_fuzzer = 1; + copy = 0; + + } else if (!strncmp(tmp, "fuzzer", 6)) { + + copy = 0; + + } + + if (copy) { + + if (count) { strcat(new, ","); } + strcat(new, tmp); + ++count; + + } + + } + + } else { + + ptr++; + + } + + } while (!ende); + + strcpy(string, new); + + ck_free(tmp); + ck_free(new); + + return detect_single_fuzzer; + +} + +/* + Parse and process possible -fsanitize related args, return PARAM_MISS + if nothing matched. We have 3 main tasks here for these args: + - Check which one of those sanitizers present here. + - Check if libfuzzer present. We need to block the request of enable + libfuzzer, and link harness with our libAFLDriver.a later. + - Check if SanCov allow/denylist options present. We need to try switching + to LLVMNATIVE instead of using our optimized PCGUARD anyway. If we + can't make it finally for various reasons, just drop these options. +*/ +param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { + + param_st final_ = PARAM_MISS; + +// MACRO START +#define HAVE_SANITIZER_SCAN_KEEP(v, k) \ + do { \ + \ + if (strstr(cur_argv, "=" STRINGIFY(k)) || \ + strstr(cur_argv, "," STRINGIFY(k))) { \ + \ + if (scan) { \ + \ + aflcc->have_##v = 1; \ + final_ = PARAM_SCAN; \ + \ + } else { \ + \ + final_ = PARAM_KEEP; \ + \ + } \ + \ + } \ + \ + } while (0) + + // MACRO END + + if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize="))) { + + HAVE_SANITIZER_SCAN_KEEP(asan, address); + HAVE_SANITIZER_SCAN_KEEP(msan, memory); + HAVE_SANITIZER_SCAN_KEEP(ubsan, undefined); + HAVE_SANITIZER_SCAN_KEEP(tsan, thread); + HAVE_SANITIZER_SCAN_KEEP(lsan, leak); + HAVE_SANITIZER_SCAN_KEEP(cfisan, cfi); + + } + +#undef HAVE_SANITIZER_SCAN_KEEP + + // We can't use a "else if" there, because some of the following + // matching rules overlap with those in the if-statement above. + if (!strcmp(cur_argv, "-fsanitize=fuzzer")) { + + if (scan) { + + aflcc->need_aflpplib = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } else if (!strncmp(cur_argv, "-fsanitize=", strlen("-fsanitize=")) && + + strchr(cur_argv, ',') && + !strstr(cur_argv, "=,")) { // avoid OOB errors + + if (scan) { + + u8 *cur_argv_ = ck_strdup(cur_argv); + + if (fsanitize_fuzzer_comma(cur_argv_)) { + + aflcc->need_aflpplib = 1; + final_ = PARAM_SCAN; + + } + + ck_free(cur_argv_); + + } else { + + fsanitize_fuzzer_comma(cur_argv); + if (!cur_argv || strlen(cur_argv) <= strlen("-fsanitize=")) + final_ = PARAM_DROP; // this means it only has "fuzzer" previously. + + } + + } else if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) && + + strstr(cur_argv, "list=")) { + + if (scan) { + + aflcc->have_instr_list = 1; + final_ = PARAM_SCAN; + + } else { + + if (aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE) { + + if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); } + final_ = PARAM_DROP; + + } else { + + final_ = PARAM_KEEP; + + } + + } + + } + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/* + Add params for sanitizers. Here we need to consider: + - Use static runtime for asan, as much as possible. + - ASAN, MSAN, AFL_HARDEN are mutually exclusive. + - Add options if not found there, on request of AFL_USE_ASAN, AFL_USE_MSAN, + etc. + - Update have_* so that functions called after this can have correct context. + However this also means any functions called before should NOT depend on + these have_*, otherwise they may not work as expected. +*/ +void add_sanitizers(aflcc_state_t *aflcc, char **envp) { + // 如果启用了 ASAN (地址消毒器),则进行相关配置 + if (getenv("AFL_USE_ASAN") || aflcc->have_asan) { + + // 如果同时启用了 MSAN (内存消毒器),则报错,因为 ASAN 和 MSAN 不能同时使用 + if (getenv("AFL_USE_MSAN") || aflcc->have_msan) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 如果启用了 AFL_HARDEN,则报错,因为 ASAN 和 AFL_HARDEN 不能同时使用 + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + + // 如果是 GCC 插件模式,并且没有启用静态 ASAN 库,则添加静态 ASAN 库的选项 + if (aflcc->compiler_mode == GCC_PLUGIN && !aflcc->have_staticasan) { + insert_param(aflcc, "-static-libasan"); + } + + // 添加 fortify 配置,0表示没有额外的强化 + add_defs_fortify(aflcc, 0); + + // 如果没有启用 ASAN,则添加相应的编译选项来启用地址消毒 + if (!aflcc->have_asan) { + insert_param(aflcc, "-fsanitize=address"); + insert_param(aflcc, "-fno-common"); + } + + aflcc->have_asan = 1; // 标记已经启用了 ASAN + } + // 如果启用了 MSAN (内存消毒器),则进行相关配置 + else if (getenv("AFL_USE_MSAN") || aflcc->have_msan) { + + // 如果同时启用了 ASAN,则报错,因为 ASAN 和 MSAN 不能同时使用 + if (getenv("AFL_USE_ASAN") || aflcc->have_asan) + FATAL("ASAN and MSAN are mutually exclusive"); + + // 如果启用了 AFL_HARDEN,则报错,因为 MSAN 和 AFL_HARDEN 不能同时使用 + if (getenv("AFL_HARDEN")) + FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + + // 添加 fortify 配置,0表示没有额外的强化 + add_defs_fortify(aflcc, 0); + + // 如果没有启用 MSAN,则添加相应的编译选项来启用内存消毒 + if (!aflcc->have_msan) { insert_param(aflcc, "-fsanitize=memory"); } + + aflcc->have_msan = 1; // 标记已经启用了 MSAN + } + + // 如果启用了 UBSAN (未定义行为消毒器),则进行相关配置 + if (getenv("AFL_USE_UBSAN") || aflcc->have_ubsan) { + + // 如果没有启用 UBSAN,则添加相应的编译选项来启用未定义行为消毒 + if (!aflcc->have_ubsan) { + insert_param(aflcc, "-fsanitize=undefined"); + insert_param(aflcc, "-fsanitize-undefined-trap-on-error"); + insert_param(aflcc, "-fno-sanitize-recover=all"); + } + + // 如果没有启用帧指针,则添加相应的选项来启用帧指针 + if (!aflcc->have_fp) { + insert_param(aflcc, "-fno-omit-frame-pointer"); + aflcc->have_fp = 1; + } + + aflcc->have_ubsan = 1; // 标记已经启用了 UBSAN + } + + // 如果启用了 TSAN (线程消毒器),则进行相关配置 + if (getenv("AFL_USE_TSAN") || aflcc->have_tsan) { + + // 如果没有启用帧指针,则添加相应的选项来启用帧指针 + if (!aflcc->have_fp) { + insert_param(aflcc, "-fno-omit-frame-pointer"); + aflcc->have_fp = 1; + } + + // 如果没有启用 TSAN,则添加相应的编译选项来启用线程消毒 + if (!aflcc->have_tsan) { insert_param(aflcc, "-fsanitize=thread"); } + + aflcc->have_tsan = 1; // 标记已经启用了 TSAN + } + + // 如果启用了 LSAN (泄漏消毒器),则进行相关配置 + if (getenv("AFL_USE_LSAN") && !aflcc->have_lsan) { + + // 添加编译选项来启用泄漏消毒 + insert_param(aflcc, "-fsanitize=leak"); + + // 添加 LSAN 控制的定义 + add_defs_lsan_ctrl(aflcc); + + aflcc->have_lsan = 1; // 标记已经启用了 LSAN + } + + // 如果启用了 CFISAN (控制流完整性消毒器),则进行相关配置 + if (getenv("AFL_USE_CFISAN") || aflcc->have_cfisan) { + + // 如果是 GCC 插件模式或 GCC 模式,则启用完整的控制流保护 + if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) { + + // 如果没有启用控制流保护,则添加相应选项 + if (!aflcc->have_fcf) { insert_param(aflcc, "-fcf-protection=full"); } + + } else { + + // 如果没有启用 LTO (链接时优化),则添加 LTO 选项 + if (!aflcc->lto_mode && !aflcc->have_flto) { + uint32_t i = 0, found = 0; + while (envp[i] != NULL && !found) { + if (strncmp("-flto", envp[i++], 5) == 0) found = 1; + } + if (!found) { insert_param(aflcc, "-flto"); } + aflcc->have_flto = 1; // 标记已经启用了 LTO + } + + // 如果没有启用 CFISAN,则添加相应选项来启用控制流完整性消毒 + if (!aflcc->have_cfisan) { insert_param(aflcc, "-fsanitize=cfi"); } + + // 如果没有启用隐藏符号,则启用隐藏符号选项 + if (!aflcc->have_hidden) { + insert_param(aflcc, "-fvisibility=hidden"); + aflcc->have_hidden = 1; + } + + aflcc->have_cfisan = 1; // 标记已经启用了 CFISAN + } + } +} + +/* Add params to enable LLVM SanCov, the native PCGUARD */ +void add_native_pcguard(aflcc_state_t *aflcc) { + + /* If there is a rust ASan runtime on the command line, it is likely we're + * linking from rust and adding native flags requiring the sanitizer runtime + * will trigger native clang to add yet another runtime, causing linker + * errors. For now we shouldn't add instrumentation here, we're linking + * anyway. + */ + if (aflcc->have_rust_asanrt) { return; } + + /* If llvm-config doesn't figure out LLVM_MAJOR, just + go on anyway and let compiler complain if doesn't work. */ + +#if LLVM_MAJOR > 0 && LLVM_MAJOR < 6 + FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+"); +#else + #if LLVM_MAJOR == 0 + WARNF( + "pcguard instrumentation with pc-table requires LLVM 6.0.1+" + " otherwise the compiler will fail"); + #endif + if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { + + insert_param(aflcc, + "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); + + } else { + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard,pc-table"); + + } + +#endif + +} + +/* + Add params to launch our optimized PCGUARD on request. + It will fallback to use the native PCGUARD in some cases. If so, plz + bear in mind that instrument_mode will be set to INSTRUMENT_LLVMNATIVE. +*/ +void add_optimized_pcguard(aflcc_state_t *aflcc) { + +#if LLVM_MAJOR >= 13 + #if defined __ANDROID__ || ANDROID + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + #else + + if (aflcc->have_instr_list) { + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST instead.\n"); + + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + } else { + + /* Since LLVM_MAJOR >= 13 we use new pass manager */ + #if LLVM_MAJOR < 16 + insert_param(aflcc, "-fexperimental-new-pass-manager"); + #endif + insert_object(aflcc, "SanitizerCoveragePCGUARD.so", "-fpass-plugin=%s", 0); + + } + + #endif // defined __ANDROID__ || ANDROID +#else // LLVM_MAJOR < 13 + #if LLVM_MAJOR >= 4 + + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for " + "enhanced version.\n"); + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; + + #else + + FATAL("pcguard instrumentation requires LLVM 4.0.1+"); + + #endif +#endif + +} + +/** About -fsanitize -----END----- **/ + +/** Linking behaviors -----BEGIN----- **/ + +/* + Parse and process possible linking stage related args, + return PARAM_MISS if nothing matched. +*/ +param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan, + u8 *skip_next, char **argv) { + + if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) { + + FATAL( + "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or " + "use afl-clang-fast!"); + + } + + param_st final_ = PARAM_MISS; + + if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) { + + if (scan) { + + aflcc->shared_linking = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_KEEP; + + } + + } else if (!strcmp(cur_argv, "-Wl,-r") || !strcmp(cur_argv, "-Wl,-i") || + + !strcmp(cur_argv, "-Wl,--relocatable") || + !strcmp(cur_argv, "-r") || !strcmp(cur_argv, "--relocatable")) { + + if (scan) { + + aflcc->partial_linking = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_KEEP; + + } + + } else if (!strncmp(cur_argv, "-fuse-ld=", 9) || + + !strncmp(cur_argv, "--ld-path=", 10)) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + if (aflcc->lto_mode) + final_ = PARAM_DROP; + else + final_ = PARAM_KEEP; + + } + + } else if (!strcmp(cur_argv, "-Wl,-z,defs") || + + !strcmp(cur_argv, "-Wl,--no-undefined") || + !strcmp(cur_argv, "-Wl,-no-undefined") || + !strcmp(cur_argv, "--no-undefined") || + strstr(cur_argv, "afl-compiler-rt") || + strstr(cur_argv, "afl-llvm-rt")) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } else if (!strcmp(cur_argv, "-z") || !strcmp(cur_argv, "-Wl,-z")) { + + u8 *param = *(argv + 1); + if (param && (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs"))) { + + *skip_next = 1; + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_DROP; + + } + + } + + } + + // Try to warn user for some unsupported cases + if (scan && final_ == PARAM_MISS) { + + u8 *ptr_ = NULL; + + if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) { + + if (!strcmp(ptr_, "defs")) { + + WARNF("'-Xlinker' 'defs' detected. This may result in a bad link."); + + } else if (strstr(ptr_, "-no-undefined")) { + + WARNF( + "'-Xlinker' '%s' detected. The latter option may be dropped and " + "result in a bad link.", + ptr_); + + } + + } else if (!strncmp(cur_argv, "-Wl,", 4) && + + (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) { + + ptr_ = cur_argv + 4; + + if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may break shared " + "linking.", + ptr_); + + } + + if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") || + strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may break partial " + "linking.", + ptr_); + + } + + if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) { + + WARNF( + "'%s': multiple link options after '-Wl,' may enable report " + "unresolved symbol references and result in a bad link.", + ptr_); + + } + + } + + } + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/* Add params to specify the linker used in LTO */ +void add_lto_linker(aflcc_state_t *aflcc) { + + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); + + u8 *ld_path = NULL; + if (getenv("AFL_REAL_LD")) { + + ld_path = strdup(getenv("AFL_REAL_LD")); + + } else { + + ld_path = strdup(AFL_REAL_LD); + + } + + if (!ld_path || !*ld_path) { + + if (ld_path) { + + // Freeing empty string + free(ld_path); + + } + + ld_path = strdup("ld.lld"); + + } + + if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); } +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 + insert_param(aflcc, alloc_printf("--ld-path=%s", ld_path)); +#else + insert_param(aflcc, alloc_printf("-fuse-ld=%s", ld_path)); +#endif + free(ld_path); + +} + +/* Add params to launch SanitizerCoverageLTO.so when linking */ +void add_lto_passes(aflcc_state_t *aflcc) { + +#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15 + // The NewPM implementation only works fully since LLVM 15. + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,--load-pass-plugin=%s", + 0); +#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 + insert_param(aflcc, "-Wl,--lto-legacy-pass-manager"); + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); +#else + insert_param(aflcc, "-fno-experimental-new-pass-manager"); + insert_object(aflcc, "SanitizerCoverageLTO.so", "-Wl,-mllvm=-load=%s", 0); +#endif + + insert_param(aflcc, "-Wl,--allow-multiple-definition"); + +} + +/* Add params to link with libAFLDriver.a on request */ +static void add_aflpplib(aflcc_state_t *aflcc) { + + if (!aflcc->need_aflpplib) return; + + u8 *afllib = find_object(aflcc, "libAFLDriver.a"); + + if (!be_quiet) { + + OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a"); + + } + + if (!afllib) { + + if (!be_quiet) { + + WARNF( + "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in " + "the flags - this will fail!"); + + } + + } else { + + insert_param(aflcc, afllib); + +#ifdef __APPLE__ + insert_param(aflcc, "-Wl,-undefined,dynamic_lookup"); +#endif + + } + +} + +/* Add params to link with runtimes depended by our instrumentation */ +void add_runtime(aflcc_state_t *aflcc) { + + if (aflcc->preprocessor_only || aflcc->have_c || !aflcc->non_dash) { + + /* 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. */ + return; + + } + + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC && + !getenv("AFL_LLVM_NO_RPATH")) { + + // in case LLVM is installed not via a package manager or "make install" + // e.g. compiled download or compiled from github then its ./lib directory + // might not be in the search path. Add it if so. + const char *libdir = LLVM_LIBDIR; + if (aflcc->plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && + strncmp(libdir, "/lib", 4)) { + +#ifdef __APPLE__ + u8 *libdir_opt = strdup("-Wl,-rpath," LLVM_LIBDIR); +#else + u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR); +#endif + insert_param(aflcc, libdir_opt); + + } + + } + +#ifndef __ANDROID__ + + #define M32_ERR_MSG "-m32 is not supported by your compiler" + #define M64_ERR_MSG "-m64 is not supported by your compiler" + + if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) { + + switch (aflcc->bit_mode) { + + case 0: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt.o", 0, 0); + if (aflcc->lto_mode) insert_object(aflcc, "afl-llvm-rt-lto.o", 0, 0); + break; + + case 32: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt-32.o", 0, M32_ERR_MSG); + if (aflcc->lto_mode) + insert_object(aflcc, "afl-llvm-rt-lto-32.o", 0, M32_ERR_MSG); + break; + + case 64: + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "afl-compiler-rt-64.o", 0, M64_ERR_MSG); + if (aflcc->lto_mode) + insert_object(aflcc, "afl-llvm-rt-lto-64.o", 0, M64_ERR_MSG); + break; + + } + + #if __AFL_CODE_COVERAGE + // Required for dladdr used in afl-compiler-rt.o + insert_param(aflcc, "-ldl"); + #endif + + #if !defined(__APPLE__) && !defined(__sun) + if (!aflcc->shared_linking && !aflcc->partial_linking) + insert_object(aflcc, "dynamic_list.txt", "-Wl,--dynamic-list=%s", 0); + #endif + + #if defined(__APPLE__) + if (aflcc->shared_linking || aflcc->partial_linking) { + + insert_param(aflcc, "-Wl,-U"); + insert_param(aflcc, "-Wl,___afl_area_ptr"); + insert_param(aflcc, "-Wl,-U"); + insert_param(aflcc, "-Wl,___sanitizer_cov_trace_pc_guard_init"); + + } + + #endif + + } + +#endif + + add_aflpplib(aflcc); + +#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ + insert_param(aflcc, "-Wl,-lrt"); +#endif + +} + +/** Linking behaviors -----END----- **/ + +/** Miscellaneous routines -----BEGIN----- **/ + +/* + Add params to make compiler driver use our afl-as + as assembler, required by the vanilla instrumentation. +*/ +void add_assembler(aflcc_state_t *aflcc) { + + u8 *afl_as = find_object(aflcc, "afl-as"); + + if (!afl_as) FATAL("Cannot find 'afl-as'."); + + u8 *slash = strrchr(afl_as, '/'); + if (slash) *slash = 0; + + // Search for 'as' may be unreliable in some cases (see #2058) + // so use 'afl-as' instead, because 'as' is usually a symbolic link, + // or can be a renamed copy of 'afl-as' created in the same dir. + // Now we should verify if the compiler can find the 'as' we need. + +#define AFL_AS_ERR "(should be a symlink or copy of 'afl-as')" + + u8 *afl_as_dup = alloc_printf("%s/as", afl_as); + + int fd = open(afl_as_dup, O_RDONLY); + if (fd < 0) { PFATAL("Unable to open '%s' " AFL_AS_ERR, afl_as_dup); } + + struct stat st; + if (fstat(fd, &st) < 0) { + + PFATAL("Unable to fstat '%s' " AFL_AS_ERR, afl_as_dup); + + } + + u32 f_len = st.st_size; + + u8 *f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); + if (f_data == MAP_FAILED) { + + PFATAL("Unable to mmap file '%s' " AFL_AS_ERR, afl_as_dup); + + } + + close(fd); + + // "AFL_AS" is a const str passed to getenv in afl-as.c + if (!memmem(f_data, f_len, "AFL_AS", strlen("AFL_AS") + 1)) { + + FATAL( + "Looks like '%s' is not a valid symlink or copy of '%s/afl-as'. " + "It is a prerequisite to override system-wide 'as' for " + "instrumentation.", + afl_as_dup, afl_as); + + } + + if (munmap(f_data, f_len)) { PFATAL("unmap() failed"); } + + ck_free(afl_as_dup); + +#undef AFL_AS_ERR + + insert_param(aflcc, "-B"); + insert_param(aflcc, afl_as); + + if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as"); + +} + +/* Add params to launch the gcc plugins for instrumentation. */ +void add_gcc_plugin(aflcc_state_t *aflcc) { + + if (aflcc->cmplog_mode) { + + insert_object(aflcc, "afl-gcc-cmplog-pass.so", "-fplugin=%s", 0); + insert_object(aflcc, "afl-gcc-cmptrs-pass.so", "-fplugin=%s", 0); + + } + + insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0); + + insert_param(aflcc, "-fno-if-conversion"); + insert_param(aflcc, "-fno-if-conversion2"); + +} + +/* Add some miscellaneous params required by our instrumentation. */ +void add_misc_params(aflcc_state_t *aflcc) { + + // 如果环境变量 AFl_NO_BUILTIN 或其他相关环境变量被设置,或者启用了 LTO 模式 + // 则禁用内置的字符串和内存比较函数 + if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") || + aflcc->lto_mode) { + + // 禁用常见的字符串和内存比较函数的内置实现,防止与模糊测试产生冲突 + insert_param(aflcc, "-fno-builtin-strcmp"); + insert_param(aflcc, "-fno-builtin-strncmp"); + insert_param(aflcc, "-fno-builtin-strcasecmp"); + insert_param(aflcc, "-fno-builtin-strncasecmp"); + insert_param(aflcc, "-fno-builtin-memcmp"); + insert_param(aflcc, "-fno-builtin-bcmp"); + insert_param(aflcc, "-fno-builtin-strstr"); + insert_param(aflcc, "-fno-builtin-strcasestr"); + + } + + // 如果没有启用位置无关代码(PIC),则添加 -fPIC 参数 + if (!aflcc->have_pic) { + insert_param(aflcc, "-fPIC"); + } + + // 如果环境变量 AFL_HARDEN 被设置,启用栈保护等安全选项 + if (getenv("AFL_HARDEN")) { + + // 启用所有函数的栈保护 + insert_param(aflcc, "-fstack-protector-all"); + + // 如果未设置 Fortify,设置 Fortify 防护等级 + if (!aflcc->fortify_set) + add_defs_fortify(aflcc, 2); + } + + // 如果环境变量 AFL_DONT_OPTIMIZE 未设置,启用优化选项 + if (!getenv("AFL_DONT_OPTIMIZE")) { + + // 启用调试符号生成 + insert_param(aflcc, "-g"); + + // 如果没有设置 -O 优化级别,设置为 -O3(最高优化) + if (!aflcc->have_o) + insert_param(aflcc, "-O3"); + + // 如果没有设置循环展开,启用循环展开优化 + if (!aflcc->have_unroll) + insert_param(aflcc, "-funroll-loops"); + + // 以下代码被注释掉了,但如果有指定架构优化选项(如 -march),也可以启用 + // if (strlen(aflcc->march_opt) > 1 && aflcc->march_opt[0] == '-') + // insert_param(aflcc, aflcc->march_opt); + } + + // 如果设置了 x_set 标志,插入 -x none 参数 + if (aflcc->x_set) { + + insert_param(aflcc, "-x"); + insert_param(aflcc, "none"); + } + +} + +/* + Parse and process a variety of args under our matching rules, + return PARAM_MISS if nothing matched. +*/ +param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { + + param_st final_ = PARAM_MISS; + +// MACRO START +#define SCAN_KEEP(dst, src) \ + do { \ + \ + if (scan) { \ + \ + dst = src; \ + final_ = PARAM_SCAN; \ + \ + } else { \ + \ + final_ = PARAM_KEEP; \ + \ + } \ + \ + } while (0) + + // MACRO END + + if (!strncasecmp(cur_argv, "-fpic", 5)) { + + SCAN_KEEP(aflcc->have_pic, 1); + + } else if (!strcmp(cur_argv, "-m32") || + + !strcmp(cur_argv, "armv7a-linux-androideabi")) { + + SCAN_KEEP(aflcc->bit_mode, 32); + + } else if (!strcmp(cur_argv, "-m64")) { + + SCAN_KEEP(aflcc->bit_mode, 64); + + } else if (strstr(cur_argv, "FORTIFY_SOURCE")) { + + SCAN_KEEP(aflcc->fortify_set, 1); + + } else if (!strcmp(cur_argv, "-x")) { + + SCAN_KEEP(aflcc->x_set, 1); + + } else if (!strcmp(cur_argv, "-E")) { + + SCAN_KEEP(aflcc->preprocessor_only, 1); + + } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) { + + SCAN_KEEP(aflcc->passthrough, 1); + + } else if (!strcmp(cur_argv, "-c")) { + + SCAN_KEEP(aflcc->have_c, 1); + + } else if (!strcmp(cur_argv, "-static-libasan")) { + + SCAN_KEEP(aflcc->have_staticasan, 1); + + } else if (strstr(cur_argv, "librustc") && strstr(cur_argv, "_rt.asan.a")) { + + SCAN_KEEP(aflcc->have_rust_asanrt, 1); + + } else if (!strcmp(cur_argv, "-fno-omit-frame-pointer")) { + + SCAN_KEEP(aflcc->have_fp, 1); + + } else if (!strcmp(cur_argv, "-fvisibility=hidden")) { + + SCAN_KEEP(aflcc->have_hidden, 1); + + } else if (!strcmp(cur_argv, "-flto") || !strcmp(cur_argv, "-flto=full")) { + + SCAN_KEEP(aflcc->have_flto, 1); + + } else if (!strncmp(cur_argv, "-D_FORTIFY_SOURCE", + + strlen("-D_FORTIFY_SOURCE"))) { + + SCAN_KEEP(aflcc->have_fortify, 1); + + } else if (!strncmp(cur_argv, "-fcf-protection", strlen("-fcf-protection"))) { + + SCAN_KEEP(aflcc->have_cfisan, 1); + + } else if (!strncmp(cur_argv, "-O", 2)) { + + SCAN_KEEP(aflcc->have_o, 1); + + } else if (!strncmp(cur_argv, "-funroll-loop", 13)) { + + SCAN_KEEP(aflcc->have_unroll, 1); + + } else if (!strncmp(cur_argv, "--afl", 5)) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strncmp(cur_argv, "-fno-unroll", 11)) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) { + + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; + + } else if (!strncmp(cur_argv, "-stdlib=", 8) && + + (aflcc->compiler_mode == GCC || + aflcc->compiler_mode == GCC_PLUGIN)) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv); + final_ = PARAM_DROP; + + } + + } else if (cur_argv[0] != '-') { + + /* It's a weak, loose pattern, with very different purpose + than others. We handle it at last, cautiously and robustly. */ + + if (scan && cur_argv[0] != '@') // response file support + aflcc->non_dash = 1; + + } + +#undef SCAN_KEEP + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; + +} + +/** Miscellaneous routines -----END----- **/ + +/* Print help message on request */ +static void maybe_usage(aflcc_state_t *aflcc, int argc, char **argv) { + + if (argc < 2 || strncmp(argv[1], "-h", 2) == 0) { + + printf("afl-cc" VERSION + " by Michal Zalewski, Laszlo Szekeres, Marc Heuse\n"); + + SAYF( + "\n" + "afl-cc/afl-c++ [options]\n" + "\n" + "This is a helper application for afl-fuzz. It serves as a drop-in " + "replacement\n" + "for gcc and clang, letting you recompile third-party code with the " + "required\n" + "runtime instrumentation. A common use pattern would be one of the " + "following:\n\n" + + " CC=afl-cc CXX=afl-c++ ./configure --disable-shared\n" + " cmake -DCMAKE_C_COMPILERC=afl-cc -DCMAKE_CXX_COMPILER=afl-c++ .\n" + " CC=afl-cc CXX=afl-c++ meson\n\n"); + + SAYF( + " |------------- FEATURES " + "-------------|\n" + "MODES: NCC PERSIST DICT LAF " + "CMPLOG SELECT\n" + " [LLVM] LLVM: %s%s\n" + " PCGUARD %s yes yes module yes yes " + "yes\n" + " NATIVE AVAILABLE no yes no no " + "part. yes\n" + " CLASSIC %s no yes module yes yes " + "yes\n" + " - NORMAL\n" + " - CALLER\n" + " - CTX\n" + " - NGRAM-{2-16}\n" + " [LTO] LLVM LTO: %s%s\n" + " PCGUARD DEFAULT yes yes yes yes yes " + " yes\n" + " CLASSIC yes yes yes yes yes " + " yes\n" + " [GCC_PLUGIN] gcc plugin: %s%s\n" + " CLASSIC DEFAULT no yes no no no " + "yes\n" + " [GCC/CLANG] simple gcc/clang: %s%s\n" + " CLASSIC DEFAULT no no no no no " + "no\n\n", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->compiler_mode == LLVM ? " [SELECTED]" : "", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->have_llvm ? "AVAILABLE " : "unavailable!", + aflcc->have_lto ? "AVAILABLE" : "unavailable!", + aflcc->compiler_mode == LTO ? " [SELECTED]" : "", + aflcc->have_gcc_plugin ? "AVAILABLE" : "unavailable!", + aflcc->compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", + aflcc->have_gcc && aflcc->have_clang + ? "AVAILABLE" + : (aflcc->have_gcc + ? "GCC ONLY " + : (aflcc->have_clang ? "CLANG ONLY" : "unavailable!")), + (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) + ? " [SELECTED]" + : ""); + + SAYF( + "Modes:\n" + " To select the compiler mode use a symlink version (e.g. " + "afl-clang-fast), set\n" + " the environment variable AFL_CC_COMPILER to a mode (e.g. LLVM) or " + "use the\n" + " command line parameter --afl-MODE (e.g. --afl-llvm). If none is " + "selected,\n" + " afl-cc will select the best available (LLVM -> GCC_PLUGIN -> GCC).\n" + " The best is LTO but it often needs RANLIB and AR settings outside " + "of afl-cc.\n\n"); + +#if LLVM_MAJOR >= 11 || (LLVM_MAJOR == 10 && LLVM_MINOR > 0) + #define NATIVE_MSG \ + " LLVM-NATIVE: use llvm's native PCGUARD instrumentation (less " \ + "performant)\n" +#else + #define NATIVE_MSG "" +#endif + + SAYF( + "Sub-Modes: (set via env AFL_LLVM_INSTRUMENT, afl-cc selects the best " + "available)\n" + " PCGUARD: Dominator tree instrumentation (best!) (README.llvm.md)\n" + + NATIVE_MSG + + " CLASSIC: decision target instrumentation (README.llvm.md)\n" + " CALLER: CLASSIC + single callee context " + "(instrumentation/README.ctx.md)\n" + " CTX: CLASSIC + full callee context " + "(instrumentation/README.ctx.md)\n" + " NGRAM-x: CLASSIC + previous path " + "((instrumentation/README.ngram.md)\n\n"); + +#undef NATIVE_MSG + + SAYF( + "Features: (see documentation links)\n" + " NCC: non-colliding coverage [automatic] (that is an amazing " + "thing!)\n" + " (instrumentation/README.lto.md)\n" + " PERSIST: persistent mode support [code] (huge speed increase!)\n" + " (instrumentation/README.persistent_mode.md)\n" + " DICT: dictionary in the target [yes=automatic or LLVM module " + "pass]\n" + " (instrumentation/README.lto.md + " + "instrumentation/README.llvm.md)\n" + " LAF: comparison splitting [env] " + "(instrumentation/README.laf-intel.md)\n" + " CMPLOG: input2state exploration [env] " + "(instrumentation/README.cmplog.md)\n" + " SELECT: selective instrumentation (allow/deny) on filename or " + "function [env]\n" + " (instrumentation/README.instrument_list.md)\n\n"); + + if (argc < 2 || strncmp(argv[1], "-hh", 3)) { + + SAYF( + "To see all environment variables for the configuration of afl-cc " + "use \"-hh\".\n"); + + } else { + + SAYF( + "Environment variables used:\n" + " AFL_CC: path to the C compiler to use\n" + " AFL_CXX: path to the C++ compiler to use\n" + " AFL_DEBUG: enable developer debugging output\n" + " AFL_DONT_OPTIMIZE: disable optimization instead of -O3\n" + " AFL_NO_BUILTIN: no builtins for string compare functions (for " + "libtokencap.so)\n" + " AFL_NOOPT: behave like a normal compiler (to pass configure " + "tests)\n" + " AFL_PATH: path to instrumenting pass and runtime " + "(afl-compiler-rt.*o)\n" + " AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n" + " AFL_INST_RATIO: percentage of branches to instrument\n" + " AFL_QUIET: suppress verbose output\n" + " AFL_HARDEN: adds code hardening to catch memory bugs\n" + " AFL_USE_ASAN: activate address sanitizer\n" + " AFL_USE_CFISAN: activate control flow sanitizer\n" + " AFL_USE_MSAN: activate memory sanitizer\n" + " AFL_USE_UBSAN: activate undefined behaviour sanitizer\n" + " AFL_USE_TSAN: activate thread sanitizer\n" + " AFL_USE_LSAN: activate leak-checker sanitizer\n"); + + if (aflcc->have_gcc_plugin) + SAYF( + "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" + " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" + " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" + " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " + "filename\n"); + +#if LLVM_MAJOR >= 9 + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_SKIP_NEVERZERO: do not skip zero on trace counters\n" +#else + #define COUNTER_BEHAVIOUR \ + " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" +#endif + if (aflcc->have_llvm) + SAYF( + "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " + "variables:\n" + " AFL_LLVM_THREADSAFE_INST: instrument with thread safe counters, " + "disables neverzero\n" + + COUNTER_BEHAVIOUR + + " AFL_LLVM_DICT2FILE: generate an afl dictionary based on found " + "comparisons\n" + " AFL_LLVM_DICT2FILE_NO_MAIN: skip parsing main() for the " + "dictionary\n" + " AFL_LLVM_INJECTIONS_ALL: enables all injections hooking\n" + " AFL_LLVM_INJECTIONS_SQL: enables SQL injections hooking\n" + " AFL_LLVM_INJECTIONS_LDAP: enables LDAP injections hooking\n" + " AFL_LLVM_INJECTIONS_XSS: enables XSS injections hooking\n" + " AFL_LLVM_LAF_ALL: enables all LAF splits/transforms\n" + " AFL_LLVM_LAF_SPLIT_COMPARES: enable cascaded comparisons\n" + " AFL_LLVM_LAF_SPLIT_COMPARES_BITW: size limit (default 8)\n" + " AFL_LLVM_LAF_SPLIT_SWITCHES: cascaded comparisons on switches\n" + " AFL_LLVM_LAF_SPLIT_FLOATS: cascaded comparisons on floats\n" + " AFL_LLVM_LAF_TRANSFORM_COMPARES: cascade comparisons for string " + "functions\n" + " AFL_LLVM_ALLOWLIST/AFL_LLVM_DENYLIST: enable " + "instrument allow/\n" + " deny listing (selective instrumentation)\n"); + + if (aflcc->have_llvm) + SAYF( + " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " + "mutator)\n" + " AFL_LLVM_INSTRUMENT: set instrumentation mode:\n" + " CLASSIC, PCGUARD, LTO, GCC, CLANG, CALLER, CTX, NGRAM-2 " + "..-16\n" + " You can also use the old environment variables instead:\n" + " AFL_LLVM_USE_TRACE_PC: use LLVM trace-pc-guard instrumentation\n" + " AFL_LLVM_CALLER: use single context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_CTX: use full context sensitive coverage (for " + "CLASSIC)\n" + " AFL_LLVM_NGRAM_SIZE: use ngram prev_loc count coverage (for " + "CLASSIC)\n" + " AFL_LLVM_NO_RPATH: disable rpath setting for custom LLVM " + "locations\n"); + +#ifdef AFL_CLANG_FLTO + if (aflcc->have_lto) + SAYF( + "\nLTO/afl-clang-lto specific environment variables:\n" + " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " + "e.g. " + "0x10000\n" + " AFL_LLVM_DOCUMENT_IDS: write all edge IDs and the corresponding " + "functions\n" + " into this file (LTO mode)\n" + " AFL_LLVM_LTO_CALLER: activate CALLER/CTX instrumentation\n" + " AFL_LLVM_LTO_CALLER_DEPTH: skip how many empty functions\n" + " AFL_LLVM_LTO_DONTWRITEID: don't write the highest ID used to a " + "global var\n" + " AFL_LLVM_LTO_STARTID: from which ID to start counting from for " + "a bb\n" + " AFL_REAL_LD: use this lld linker instead of the compiled in " + "path\n" + " AFL_LLVM_LTO_SKIPINIT: don't inject initialization code " + "(used in WAFL mode)\n" + "If anything fails - be sure to read README.lto.md!\n"); +#endif + + SAYF( + "\nYou can supply --afl-noopt to not instrument, like AFL_NOOPT. " + "(this is helpful\n" + "in some build systems if you do not want to instrument " + "everything.\n"); + + } + + SAYF( + "\nFor any information on the available instrumentations and options " + "please \n" + "consult the README.md, especially section 3.1 about instrumenting " + "targets.\n\n"); + +#if (LLVM_MAJOR >= 3) + if (aflcc->have_lto) + SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); + if (aflcc->have_llvm) + SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, + LLVM_BINDIR); +#endif + +#ifdef USEMMAP + #if !defined(__HAIKU__) + SAYF("Compiled with shm_open support.\n"); + #else + SAYF("Compiled with shm_open support (adds -lrt when linking).\n"); + #endif +#else + SAYF("Compiled with shmat support.\n"); +#endif + SAYF("\n"); + + SAYF( + "Do not be overwhelmed :) afl-cc uses good defaults if no options are " + "selected.\n" + "Read the documentation for FEATURES though, all are good but few are " + "defaults.\n" + "Recommended is afl-clang-lto with AFL_LLVM_CMPLOG or afl-clang-fast " + "with\n" + "AFL_LLVM_CMPLOG and " + "AFL_LLVM_DICT2FILE+AFL_LLVM_DICT2FILE_NO_MAIN.\n\n"); + + if (LLVM_MAJOR < 13) { + + SAYF( + "Warning: It is highly recommended to use at least LLVM version 13 " + "(or better, higher) rather than %d!\n\n", + LLVM_MAJOR); + + } + + exit(1); + + } + +} + +/* + Process params passed to afl-cc. + + We have two working modes, *scan* and *non-scan*. In scan mode, + the main task is to set some variables in aflcc according to current argv[i], + while in non-scan mode, is to choose keep or drop current argv[i]. + + We have several matching routines being called sequentially in the while-loop, + and each of them try to parse and match current argv[i] according to their own + rules. If one miss match, the next will then take over. In non-scan mode, each + argv[i] mis-matched by all the routines will be kept. + + These routines are: + 1. parse_misc_params + 2. parse_fsanitize + 3. parse_linking_params + 4. `if (*cur == '@') {...}`, i.e., parse response files +*/ +static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc, + char **argv) { + +<<<<<<< HEAD + u8 skip_next = 0; + while (--argc) { + u8 *cur = *(++argv); + if (skip_next > 0) { + skip_next--; + continue; + } + if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; + if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; + if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) + continue; + if (*cur == '@') { + u8 *filename = cur + 1; + if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } + FILE *f = fopen(filename, "r"); + if (!f) { + if (!scan) insert_param(aflcc, cur); + continue; + } + struct stat st; + if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) { + fclose(f); + if (!scan) insert_param(aflcc, cur); + continue; + } + static u32 rsp_count = 2000; + if (scan) { + if (rsp_count == 0) FATAL("Too many response files provided!"); + --rsp_count; + } + u32 argc_read = 1; + char **argv_read = ck_alloc(sizeof(char *)); + argv_read[0] = ""; + char *arg_buf = NULL; + u64 arg_len = 0; + enum fsm_state { +======= + // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]); + + /* Process the argument list. */ + + u8 skip_next = 0; + while (--argc) { + + u8 *cur = *(++argv); + + if (skip_next > 0) { + + skip_next--; + continue; + + } + + if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; + + if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; + + if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) + continue; + + /* Response file support -----BEGIN----- + We have two choices - move everything to the command line or + rewrite the response files to temporary files and delete them + afterwards. We choose the first for easiness. + For clang, llvm::cl::ExpandResponseFiles does this, however it + only has C++ interface. And for gcc there is expandargv in libiberty, + written in C, but we can't simply copy-paste since its LGPL licensed. + So here we use an equivalent FSM as alternative, and try to be compatible + with the two above. See: + - https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html + - driver::expand_at_files in gcc.git/gcc/gcc.c + - expandargv in gcc.git/libiberty/argv.c + - llvm-project.git/clang/tools/driver/driver.cpp + - ExpandResponseFiles in + llvm-project.git/llvm/lib/Support/CommandLine.cpp + */ + if (*cur == '@') { + + u8 *filename = cur + 1; + if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } + + // Check not found or empty? let the compiler complain if so. + FILE *f = fopen(filename, "r"); + if (!f) { + + if (!scan) insert_param(aflcc, cur); + continue; + + } + + struct stat st; + if (fstat(fileno(f), &st) || !S_ISREG(st.st_mode) || st.st_size < 1) { + + fclose(f); + if (!scan) insert_param(aflcc, cur); + continue; + + } + + // Limit the number of response files, the max value + // just keep consistent with expandargv. Only do this in + // scan mode, and not touch rsp_count anymore in the next. + static u32 rsp_count = 2000; + if (scan) { + + if (rsp_count == 0) FATAL("Too many response files provided!"); + + --rsp_count; + + } + + // argc, argv acquired from this rsp file. Note that + // process_params ignores argv[0], we need to put a const "" here. + u32 argc_read = 1; + char **argv_read = ck_alloc(sizeof(char *)); + argv_read[0] = ""; + + char *arg_buf = NULL; + u64 arg_len = 0; + + enum fsm_state { + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 + fsm_whitespace, // whitespace seen so far + fsm_double_quote, // have unpaired double quote + fsm_single_quote, // have unpaired single quote + fsm_backslash, // a backslash is seen with no unpaired quote + fsm_normal // a normal char is seen +<<<<<<< HEAD + }; +======= + + }; + + // Workaround to append c to arg buffer, and append the buffer to argv +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 +#define ARG_ALLOC(c) \ + do { \ + \ + ++arg_len; \ + arg_buf = ck_realloc(arg_buf, (arg_len + 1) * sizeof(char)); \ + arg_buf[arg_len] = '\0'; \ + arg_buf[arg_len - 1] = (char)c; \ + \ + } while (0) + +#define ARG_STORE() \ + do { \ + \ + ++argc_read; \ + argv_read = ck_realloc(argv_read, argc_read * sizeof(char *)); \ + argv_read[argc_read - 1] = arg_buf; \ + arg_buf = NULL; \ + arg_len = 0; \ + \ + } while (0) + + int cur_chr = (int)' '; // init as whitespace, as a good start :) + enum fsm_state state_ = fsm_whitespace; + + while (cur_chr != EOF) { + + switch (state_) { + + case fsm_whitespace: + + if (arg_buf) { + + ARG_STORE(); + break; + + } + + if (isspace(cur_chr)) { + + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_single_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'"') { + + state_ = fsm_double_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\\') { + + state_ = fsm_backslash; + cur_chr = fgetc(f); + + } else { + + state_ = fsm_normal; + + } + + break; + + case fsm_normal: + + if (isspace(cur_chr)) { + + state_ = fsm_whitespace; + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_single_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\"') { + + state_ = fsm_double_quote; + cur_chr = fgetc(f); + + } else if (cur_chr == (int)'\\') { + + state_ = fsm_backslash; + cur_chr = fgetc(f); + + } else { + + ARG_ALLOC(cur_chr); + cur_chr = fgetc(f); + + } + + break; + + case fsm_backslash: + + ARG_ALLOC(cur_chr); + cur_chr = fgetc(f); + state_ = fsm_normal; + + break; + + case fsm_single_quote: + + if (cur_chr == (int)'\\') { + + cur_chr = fgetc(f); + if (cur_chr == EOF) break; + ARG_ALLOC(cur_chr); + + } else if (cur_chr == (int)'\'') { + + state_ = fsm_normal; + + } else { + + ARG_ALLOC(cur_chr); + + } + + cur_chr = fgetc(f); + break; + + case fsm_double_quote: + + if (cur_chr == (int)'\\') { + + cur_chr = fgetc(f); + if (cur_chr == EOF) break; + ARG_ALLOC(cur_chr); + + } else if (cur_chr == (int)'"') { + + state_ = fsm_normal; + + } else { + + ARG_ALLOC(cur_chr); + + } + + cur_chr = fgetc(f); + break; + + default: + break; + + } + + } + + if (arg_buf) { ARG_STORE(); } // save the pending arg after EOF + +#undef ARG_ALLOC +#undef ARG_STORE + + if (argc_read > 1) { process_params(aflcc, scan, argc_read, argv_read); } + + // We cannot free argv_read[] unless we don't need to keep any + // reference in cc_params. Never free argv[0], the const "". + if (scan) { + + while (argc_read > 1) + ck_free(argv_read[--argc_read]); + + ck_free(argv_read); + + } + + continue; + + } /* Response file support -----END----- */ + + if (!scan) insert_param(aflcc, cur); + + } + +} + +/* Process each of the existing argv, also add a few new args. */ +static void edit_params(aflcc_state_t *aflcc, u32 argc, char **argv, + char **envp) { +<<<<<<< HEAD + add_real_argv0(aflcc); + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) { + insert_param(aflcc, "-Wno-unused-command-line-argument"); + } + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { + add_assembler(aflcc); + } + if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } + if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { + if (aflcc->lto_mode && aflcc->have_instr_env) { + load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); + } + if (getenv("AFL_LLVM_DICT2FILE")) { + load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); + } + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + load_llvm_pass(aflcc, "split-switches-pass.so"); + } + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + load_llvm_pass(aflcc, "compare-transform-pass.so"); + } + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + load_llvm_pass(aflcc, "split-compares-pass.so"); + } + if (aflcc->cmplog_mode) { + insert_param(aflcc, "-fno-inline"); + load_llvm_pass(aflcc, "cmplog-switches-pass.so"); + load_llvm_pass(aflcc, "split-switches-pass.so"); + } + if (aflcc->lto_mode) { + insert_param(aflcc, aflcc->lto_flag); + if (!aflcc->have_c) { + add_lto_linker(aflcc); + add_lto_passes(aflcc); + } + } else { + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + add_optimized_pcguard(aflcc); + } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + add_native_pcguard(aflcc); + } else { + load_llvm_pass(aflcc, "afl-llvm-pass.so"); + } + } +======= + + add_real_argv0(aflcc); + + // prevent unnecessary build errors + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC) { + + insert_param(aflcc, "-Wno-unused-command-line-argument"); + + } + + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { + + add_assembler(aflcc); + + } + + if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } + + if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { + + if (aflcc->lto_mode && aflcc->have_instr_env) { + + load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); + + } + + if (getenv("AFL_LLVM_DICT2FILE")) { + + load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); + + } + + // laf + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + + load_llvm_pass(aflcc, "split-switches-pass.so"); + + } + + if (getenv("LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + + load_llvm_pass(aflcc, "compare-transform-pass.so"); + + } + + if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || + getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + + load_llvm_pass(aflcc, "split-compares-pass.so"); + + } + + // /laf + + if (aflcc->cmplog_mode) { + + insert_param(aflcc, "-fno-inline"); + + load_llvm_pass(aflcc, "cmplog-switches-pass.so"); + // reuse split switches from laf + load_llvm_pass(aflcc, "split-switches-pass.so"); + + } + + // #if LLVM_MAJOR >= 13 + // // Use the old pass manager in LLVM 14 which the AFL++ passes still + // use. insert_param(aflcc, "-flegacy-pass-manager"); + // #endif + + if (aflcc->lto_mode) { + + insert_param(aflcc, aflcc->lto_flag); + + if (!aflcc->have_c) { + + add_lto_linker(aflcc); + add_lto_passes(aflcc); + + } + + } else { + + if (aflcc->instrument_mode == INSTRUMENT_PCGUARD) { + + add_optimized_pcguard(aflcc); + + } else if (aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) { + + add_native_pcguard(aflcc); + + } else { + + load_llvm_pass(aflcc, "afl-llvm-pass.so"); + + } + + } + +>>>>>>> e12b99bad19de97a02e9fe14b9b2c048338b2ab7 + if (aflcc->cmplog_mode) { + + load_llvm_pass(aflcc, "cmplog-instructions-pass.so"); + load_llvm_pass(aflcc, "cmplog-routines-pass.so"); + + } + + if (getenv("AFL_LLVM_INJECTIONS_ALL") || + getenv("AFL_LLVM_INJECTIONS_SQL") || + getenv("AFL_LLVM_INJECTIONS_LDAP") || + getenv("AFL_LLVM_INJECTIONS_XSS")) { + + load_llvm_pass(aflcc, "injection-pass.so"); + + } + + // insert_param(aflcc, "-Qunused-arguments"); + + } + + /* Inspect the command line parameters. */ + + process_params(aflcc, 0, argc, argv); + + add_sanitizers(aflcc, envp); + + add_misc_params(aflcc); + + add_defs_common(aflcc); + add_defs_selective_instr(aflcc); + add_defs_persistent_mode(aflcc); + + add_runtime(aflcc); + + insert_param(aflcc, NULL); + +} + +/* Main entry point */ +classDiagram + class AFLCCMain { + +main(argc, argv, envp) + +initializeState() + +checkEnvironmentVariables() + +findDependencies() + +determineCompilerMode() + +determineInstrumentationMode() + +finalizeMode() + +processParameters() + +maybeShowUsage() + +notifyMode() + +debugArguments() + +editParameters() + +executeCompiler() + } + + class AFLCCState { + -cc_params: char** + -cc_par_cnt: u32 + -passthrough: u8 + -debug: u8 + +aflcc_state_init(state, prog_name) + } + + class EnvironmentChecker { + +check_environment_vars(envp) + } + + class DependencyFinder { + +find_built_deps(aflcc) + } + + class CompilerModeHandler { + +compiler_mode_by_callname(aflcc) + +compiler_mode_by_environ(aflcc) + +compiler_mode_by_cmdline(aflcc, argc, argv) + } + + class InstrumentationModeHandler { + +instrument_mode_by_environ(aflcc) + } + + class ModeFinalizer { + +mode_final_checkout(aflcc, argc, argv) + } + + class ParameterProcessor { + +process_params(aflcc, start_idx, argc, argv) + } + + class UsageNotifier { + +maybe_usage(aflcc, argc, argv) + } + + class ModeNotifier { + +mode_notification(aflcc) + } + + class Debugger { + +debugf_args(argc, argv) + } + + class ParameterEditor { + +edit_params(aflcc, argc, argv, envp) + } + + class Executor { + +execute_compiler(aflcc, argv) + } + + AFLCCMain --> AFLCCState : 使用 + AFLCCMain --> EnvironmentChecker : 使用 + AFLCCMain --> DependencyFinder : 使用 + AFLCCMain --> CompilerModeHandler : 使用 + AFLCCMain --> InstrumentationModeHandler : 使用 + AFLCCMain --> ModeFinalizer : 使用 + AFLCCMain --> ParameterProcessor : 使用 + AFLCCMain --> UsageNotifier : 使用 + AFLCCMain --> ModeNotifier : 使用 + AFLCCMain --> Debugger : 使用 + AFLCCMain --> ParameterEditor : 使用 + AFLCCMain --> Executor : 使用 \ No newline at end of file -- 2.34.1