diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/afl-cc.c | 3318 | ||||
-rw-r--r-- | src/afl-fuzz-run.c | 41 | ||||
-rw-r--r-- | src/afl-fuzz.c | 4 |
3 files changed, 1946 insertions, 1417 deletions
diff --git a/src/afl-cc.c b/src/afl-cc.c index 54c733c9..192c5423 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -47,23 +47,22 @@ #define LLVM_MINOR 0 #endif -static u8 *obj_path; /* Path to runtime libraries */ -static u8 **cc_params; /* Parameters passed to the real CC */ -static u32 cc_par_cnt = 1; /* Param count, including argv0 */ -static u8 clang_mode; /* Invoked as afl-clang*? */ -static u8 llvm_fullpath[PATH_MAX]; -static u8 instrument_mode, instrument_opt_mode, ngram_size, ctx_k, lto_mode; -static u8 compiler_mode, plusplus_mode, have_instr_env = 0, need_aflpplib = 0; -static u8 have_gcc, have_llvm, have_gcc_plugin, have_lto, have_instr_list = 0; -static u8 *lto_flag = AFL_CLANG_FLTO, *argvnull; -static u8 debug; -static u8 cwd[4096]; -static u8 cmplog_mode; -u8 use_stdin; /* dummy */ -static int passthrough; -// static u8 *march_opt = CFLAGS_OPT; - -enum { +#ifndef MAX_PARAMS_NUM + #define MAX_PARAMS_NUM 2048 +#endif + +/* Global declarations */ + +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, @@ -80,7 +79,20 @@ enum { 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] = { @@ -105,17 +117,6 @@ char instrument_mode_string[18][18] = { }; -enum { - - UNSET = 0, - LTO = 1, - LLVM = 2, - GCC_PLUGIN = 3, - GCC = 4, - CLANG = 5 - -}; - char compiler_mode_string[7][12] = { "AUTOSELECT", "LLVM-LTO", "LLVM", "GCC_PLUGIN", @@ -123,6 +124,18 @@ char compiler_mode_string[7][12] = { }; +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) { @@ -136,26 +149,228 @@ u8 *getthecwd() { } -/* Try to find a specific runtime we need, returns NULL on fail. */ +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, asan_set, x_set, bit_mode, preprocessor_only, have_unroll, + have_o, have_pic, have_c, shared_linking, partial_linking, non_dash; + + // 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); + +/* Try to find a specific runtime we need, the path to obj would be + allocated and returned. Otherwise it returns NULL on fail. */ +u8 *find_object(aflcc_state_t *, u8 *obj); + +void find_built_deps(aflcc_state_t *); + +static inline void limit_params(aflcc_state_t *aflcc, u32 add) { + + if (aflcc->cc_par_cnt + add >= MAX_PARAMS_NUM) + FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM."); + +} + +static inline void insert_param(aflcc_state_t *aflcc, u8 *param) { + + aflcc->cc_params[aflcc->cc_par_cnt++] = param; + +} + +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; + + } + + } + +} + +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 *); + +/* Working state */ + +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; + + } + +} /* in find_object() we look here: - 1. if obj_path is already set we look there first - 2. then we check the $AFL_PATH environment variable location if set - 3. next we check argv[0] if it has path information and use it + 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 - 4. if 3. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and + 3. if 2. failed we check /proc (only Linux, Android, NetBSD, DragonFly, and FreeBSD with procfs) a) and check here in ../lib/afl too - 5. we look into the AFL_PATH define (usually /usr/local/lib/afl) - 6. we finally try the current directory + 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. + what to do. Otherwise the path to obj would be allocated and returned. */ -static u8 *find_object(u8 *obj, u8 *argv0) { +u8 *find_object(aflcc_state_t *aflcc, u8 *obj) { + + u8 *argv0 = aflcc->argv0; u8 *afl_path = getenv("AFL_PATH"); u8 *slash = NULL, *tmp; @@ -164,14 +379,9 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", afl_path, obj); - if (debug) DEBUGF("Trying %s\n", tmp); + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - if (!access(tmp, R_OK)) { - - obj_path = afl_path; - return tmp; - - } + if (!access(tmp, R_OK)) { return tmp; } ck_free(tmp); @@ -190,11 +400,11 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", dir, obj); - if (debug) DEBUGF("Trying %s\n", tmp); + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); if (!access(tmp, R_OK)) { - obj_path = dir; + ck_free(dir); return tmp; } @@ -202,12 +412,10 @@ static u8 *find_object(u8 *obj, u8 *argv0) { ck_free(tmp); tmp = alloc_printf("%s/../lib/afl/%s", dir, obj); - if (debug) DEBUGF("Trying %s\n", tmp); + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); if (!access(tmp, R_OK)) { - u8 *dir2 = alloc_printf("%s/../lib/afl", dir); - obj_path = dir2; ck_free(dir); return tmp; @@ -247,26 +455,16 @@ static u8 *find_object(u8 *obj, u8 *argv0) { *slash = 0; tmp = alloc_printf("%s/%s", exepath, obj); - if (!access(tmp, R_OK)) { - - u8 *dir = alloc_printf("%s", exepath); - obj_path = dir; - return tmp; - - } + if (!access(tmp, R_OK)) { return tmp; } ck_free(tmp); tmp = alloc_printf("%s/../lib/afl/%s", exepath, obj); - if (debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - u8 *dir = alloc_printf("%s/../lib/afl/", exepath); - obj_path = dir; - return tmp; + if (!access(tmp, R_OK)) { return tmp; } - } + ck_free(tmp); } @@ -283,1844 +481,2037 @@ static u8 *find_object(u8 *obj, u8 *argv0) { tmp = alloc_printf("%s/%s", AFL_PATH, obj); - if (debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { - - obj_path = AFL_PATH; - return tmp; + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - } + if (!access(tmp, R_OK)) { return tmp; } ck_free(tmp); - tmp = alloc_printf("./%s", obj); - if (debug) DEBUGF("Trying %s\n", tmp); - - if (!access(tmp, R_OK)) { + if (aflcc->debug) DEBUGF("Trying %s\n", tmp); - obj_path = "."; - return tmp; - - } + if (!access(tmp, R_OK)) { return tmp; } ck_free(tmp); - if (debug) DEBUGF("Trying ... giving up\n"); + if (aflcc->debug) DEBUGF("Trying ... giving up\n"); return NULL; } -void parse_fsanitize(char *string) { +void find_built_deps(aflcc_state_t *aflcc) { - char *p, *ptr = string + strlen("-fsanitize="); - char *new = malloc(strlen(string) + 1); - char *tmp = malloc(strlen(ptr) + 1); - u32 count = 0, len, ende = 0; + char *ptr = NULL; - if (!new || !tmp) { FATAL("could not acquire memory"); } - strcpy(new, "-fsanitize="); +#if defined(__x86_64__) + if ((ptr = find_object(aflcc, "as")) != NULL) { - do { + #ifndef __APPLE__ + // on OSX clang masquerades as GCC + aflcc->have_gcc = 1; + #endif + aflcc->have_clang = 1; + ck_free(ptr); - p = strchr(ptr, ','); - if (!p) { + } - p = ptr + strlen(ptr) + 1; - ende = 1; +#endif - } + if ((ptr = find_object(aflcc, "SanitizerCoveragePCGUARD.so")) != NULL) { - len = p - ptr; - if (len) { + aflcc->have_optimized_pcguard = 1; + ck_free(ptr); - 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")) { +#if (LLVM_MAJOR >= 3) - need_aflpplib = 1; - copy = 0; + if ((ptr = find_object(aflcc, "SanitizerCoverageLTO.so")) != NULL) { - } else if (!strncmp(tmp, "fuzzer", 6)) { + aflcc->have_lto = 1; + ck_free(ptr); - copy = 0; + } - } + if ((ptr = find_object(aflcc, "cmplog-routines-pass.so")) != NULL) { - if (copy) { + aflcc->have_llvm = 1; + ck_free(ptr); - if (count) { strcat(new, ","); } - strcat(new, tmp); - ++count; + } - } +#endif - } +#ifdef __ANDROID__ + aflcc->have_llvm = 1; +#endif - } else { + if ((ptr = find_object(aflcc, "afl-gcc-pass.so")) != NULL) { - ptr++; /*fprintf(stderr, "NO!\n"); */ + aflcc->have_gcc_plugin = 1; + ck_free(ptr); - } + } - } while (!ende); +#if !defined(__ANDROID__) && !defined(ANDROID) + ptr = find_object(aflcc, "afl-compiler-rt.o"); - strcpy(string, new); - // fprintf(stderr, "string: %s\n", string); - // fprintf(stderr, "new: %s\n", new); + if (!ptr) { -} + FATAL( + "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " + "environment variable."); -static u8 fortify_set = 0, asan_set = 0, x_set = 0, bit_mode = 0, - shared_linking = 0, preprocessor_only = 0, have_unroll = 0, - have_o = 0, have_pic = 0, have_c = 0, partial_linking = 0, - non_dash = 0; + } -#ifndef MAX_PARAMS_NUM - #define MAX_PARAMS_NUM 2048 -#endif + if (aflcc->debug) { DEBUGF("rt=%s\n", ptr); } -static void process_params(u32 argc, char **argv) { + ck_free(ptr); +#endif - if (cc_par_cnt + argc >= MAX_PARAMS_NUM) { +} - FATAL("Too many command line parameters, please increase MAX_PARAMS_NUM."); +/* compiler_mode & instrument_mode selecting */ - } +void compiler_mode_by_callname(aflcc_state_t *aflcc) { - // reset - have_instr_list = 0; - have_c = 0; + if (strncmp(aflcc->callname, "afl-clang-fast", 14) == 0) { - if (lto_mode && argc > 1) { + /* 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 - u32 idx; - for (idx = 1; idx < argc; idx++) { + } else - if (!strncasecmp(argv[idx], "-fpic", 5)) { have_pic = 1; } +#if (LLVM_MAJOR >= 3) - } + if (strncmp(aflcc->callname, "afl-clang-lto", 13) == 0 || - } + strncmp(aflcc->callname, "afl-lto", 7) == 0) { - // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]); + aflcc->compiler_mode = LTO; - /* Process the argument list. */ + } else - u8 skip_next = 0; - while (--argc) { +#endif - u8 *cur = *(++argv); + if (strncmp(aflcc->callname, "afl-gcc-fast", 12) == 0 || - if (skip_next) { + strncmp(aflcc->callname, "afl-g++-fast", 12) == 0) { - skip_next = 0; - continue; + aflcc->compiler_mode = GCC_PLUGIN; - } +#if defined(__x86_64__) - if (cur[0] != '-') { non_dash = 1; } - if (!strncmp(cur, "--afl", 5)) continue; + } else if (strncmp(aflcc->callname, "afl-gcc", 7) == 0 || - if (lto_mode && !strncmp(cur, "-flto=thin", 10)) { + strncmp(aflcc->callname, "afl-g++", 7) == 0) { - FATAL( - "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or " - "use afl-clang-fast!"); + aflcc->compiler_mode = GCC; - } +#endif - if (lto_mode && !strncmp(cur, "-fuse-ld=", 9)) continue; - if (lto_mode && !strncmp(cur, "--ld-path=", 10)) continue; - if (!strncmp(cur, "-fno-unroll", 11)) continue; - if (strstr(cur, "afl-compiler-rt") || strstr(cur, "afl-llvm-rt")) continue; - if (!strcmp(cur, "-Wl,-z,defs") || !strcmp(cur, "-Wl,--no-undefined") || - !strcmp(cur, "--no-undefined")) { +#if defined(__x86_64__) - continue; + } else if (strcmp(aflcc->callname, "afl-clang") == 0 || - } + strcmp(aflcc->callname, "afl-clang++") == 0) { - if (compiler_mode == GCC_PLUGIN && !strcmp(cur, "-pipe")) { continue; } + aflcc->compiler_mode = CLANG; - if (!strcmp(cur, "-z") || !strcmp(cur, "-Wl,-z")) { +#endif - u8 *param = *(argv + 1); - if (!strcmp(param, "defs") || !strcmp(param, "-Wl,defs")) { + } - skip_next = 1; - continue; +} - } +void compiler_mode_by_environ(aflcc_state_t *aflcc) { - } + if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { - if ((compiler_mode == GCC || compiler_mode == GCC_PLUGIN) && - !strncmp(cur, "-stdlib=", 8)) { + aflcc->passthrough = 1; - if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); } - continue; + } - } + char *ptr = getenv("AFL_CC_COMPILER"); - if (!strncmp(cur, "-fsanitize-coverage-", 20) && strstr(cur, "list=")) { + if (!ptr) { return; } - have_instr_list = 1; + if (aflcc->compiler_mode) { - } + if (!be_quiet) { - if (!strncmp(cur, "-fsanitize=", strlen("-fsanitize=")) && - strchr(cur, ',')) { + WARNF( + "\"AFL_CC_COMPILER\" is set but a specific compiler was already " + "selected by command line parameter or symlink, ignoring the " + "environment variable!"); - parse_fsanitize(cur); - if (!cur || strlen(cur) <= strlen("-fsanitize=")) { continue; } + } - } else if ((!strncmp(cur, "-fsanitize=fuzzer-", + } else { - strlen("-fsanitize=fuzzer-")) || - !strncmp(cur, "-fsanitize-coverage", - strlen("-fsanitize-coverage"))) && - (strncmp(cur, "sanitize-coverage-allow", - strlen("sanitize-coverage-allow")) && - strncmp(cur, "sanitize-coverage-deny", - strlen("sanitize-coverage-deny")) && - instrument_mode != INSTRUMENT_LLVMNATIVE)) { + if (strncasecmp(ptr, "LTO", 3) == 0) { - if (!be_quiet) { WARNF("Found '%s' - stripping!", cur); } - continue; + aflcc->compiler_mode = LTO; - } + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { - if (need_aflpplib || !strcmp(cur, "-fsanitize=fuzzer")) { + aflcc->compiler_mode = LLVM; - u8 *afllib = find_object("libAFLDriver.a", argv[0]); + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || - if (!be_quiet) { + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { - OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a"); + aflcc->compiler_mode = GCC_PLUGIN; - } +#if defined(__x86_64__) - if (!afllib) { + } else if (strcasecmp(ptr, "GCC") == 0) { - if (!be_quiet) { + aflcc->compiler_mode = GCC; - WARNF( - "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in " - "the flags - this will fail!"); +#endif - } +#if defined(__x86_64__) - } else { + } else if (strcasecmp(ptr, "CLANG") == 0) { - cc_params[cc_par_cnt++] = afllib; + aflcc->compiler_mode = CLANG; -#ifdef __APPLE__ - cc_params[cc_par_cnt++] = "-undefined"; - cc_params[cc_par_cnt++] = "dynamic_lookup"; #endif - } + } else - if (need_aflpplib) { + FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); - need_aflpplib = 0; + } - } else { +} - continue; +// If it can be inferred, instrument_mode would also be set +void compiler_mode_by_cmdline(aflcc_state_t *aflcc, int argc, char **argv) { - } + char *ptr = NULL; - } + for (int i = 1; i < argc; i++) { - if (!strcmp(cur, "-m32")) bit_mode = 32; - if (!strcmp(cur, "armv7a-linux-androideabi")) bit_mode = 32; - if (!strcmp(cur, "-m64")) bit_mode = 64; + if (strncmp(argv[i], "--afl", 5) == 0) { - if (!strcmp(cur, "-fsanitize=address") || !strcmp(cur, "-fsanitize=memory")) - asan_set = 1; + if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) { - if (strstr(cur, "FORTIFY_SOURCE")) fortify_set = 1; + aflcc->passthrough = 1; + argv[i] = "-g"; // we have to overwrite it, -g is always good + continue; - if (!strcmp(cur, "-x")) x_set = 1; - if (!strcmp(cur, "-E")) preprocessor_only = 1; - if (!strcmp(cur, "-shared")) shared_linking = 1; - if (!strcmp(cur, "-dynamiclib")) shared_linking = 1; - if (!strcmp(cur, "--target=wasm32-wasi")) passthrough = 1; - if (!strcmp(cur, "-Wl,-r")) partial_linking = 1; - if (!strcmp(cur, "-Wl,-i")) partial_linking = 1; - if (!strcmp(cur, "-Wl,--relocatable")) partial_linking = 1; - if (!strcmp(cur, "-r")) partial_linking = 1; - if (!strcmp(cur, "--relocatable")) partial_linking = 1; - if (!strcmp(cur, "-c")) have_c = 1; + } - if (!strncmp(cur, "-O", 2)) have_o = 1; - if (!strncmp(cur, "-funroll-loop", 13)) have_unroll = 1; + if (aflcc->compiler_mode && !be_quiet) { - if (*cur == '@') { + WARNF( + "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " + "symlink compiler selection!"); - // response file support. - // 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. - // We do *not* support quotes in the rsp files to cope with spaces in - // filenames etc! If you need that then send a patch! - u8 *filename = cur + 1; - if (debug) { DEBUGF("response file=%s\n", filename); } - FILE *f = fopen(filename, "r"); - struct stat st; + } - // Check not found or empty? let the compiler complain if so. - if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) { + ptr = argv[i]; + ptr += 5; + while (*ptr == '-') + ptr++; - cc_params[cc_par_cnt++] = cur; - continue; + if (strncasecmp(ptr, "LTO", 3) == 0) { - } + aflcc->compiler_mode = LTO; - u8 *tmpbuf = malloc(st.st_size + 2), *ptr; - char **args = malloc(sizeof(char *) * (st.st_size >> 1)); - int count = 1, cont = 0, cont_act = 0; + } else if (strncasecmp(ptr, "LLVM", 4) == 0) { - while (fgets(tmpbuf, st.st_size + 1, f)) { + aflcc->compiler_mode = LLVM; - ptr = tmpbuf; - // fprintf(stderr, "1: %s\n", ptr); - // no leading whitespace - while (isspace(*ptr)) { + } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || - ++ptr; - cont_act = 0; + strncasecmp(ptr, "PC-GUARD", 8) == 0) { - } + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_PCGUARD; - // no comments, no empty lines - if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; } - // remove LF - if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; } - // remove CR - if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; } - // handle \ at end of line - if (*ptr && ptr[strlen(ptr) - 1] == '\\') { + } else if (strcasecmp(ptr, "INSTRIM") == 0 || - cont = 1; - ptr[strlen(ptr) - 1] = 0; + strcasecmp(ptr, "CFG") == 0) { - } + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and " + "PCGUARD (default in afl-cc).\n"); - // fprintf(stderr, "2: %s\n", ptr); + } else if (strcasecmp(ptr, "AFL") == 0 || - // remove whitespace at end - while (*ptr && isspace(ptr[strlen(ptr) - 1])) { + strcasecmp(ptr, "CLASSIC") == 0) { - ptr[strlen(ptr) - 1] = 0; - cont = 0; + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_CLASSIC; - } + } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || - // fprintf(stderr, "3: %s\n", ptr); - if (*ptr) { + strcasecmp(ptr, "NATIVE") == 0 || + strcasecmp(ptr, "LLVM-NATIVE") == 0) { - do { + aflcc->compiler_mode = LLVM; + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - u8 *value = ptr; - while (*ptr && !isspace(*ptr)) { + } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || - ++ptr; + strncasecmp(ptr, "GCC-P", 5) == 0 || + strncasecmp(ptr, "GCCP", 4) == 0) { - } + aflcc->compiler_mode = GCC_PLUGIN; - while (*ptr && isspace(*ptr)) { +#if defined(__x86_64__) - *ptr++ = 0; + } else if (strcasecmp(ptr, "GCC") == 0) { - } + aflcc->compiler_mode = GCC; - if (cont_act) { +#endif - u32 len = strlen(args[count - 1]) + strlen(value) + 1; - u8 *tmp = malloc(len); - snprintf(tmp, len, "%s%s", args[count - 1], value); - free(args[count - 1]); - args[count - 1] = tmp; - cont_act = 0; +#if defined(__x86_64__) - } else { + } else if (strncasecmp(ptr, "CLANG", 5) == 0) { - args[count++] = strdup(value); + aflcc->compiler_mode = CLANG; - } +#endif - } while (*ptr); + } else - } + FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); - if (cont) { + } - cont_act = 1; - cont = 0; + } - } +} - } +static void instrument_mode_old_environ(aflcc_state_t *aflcc) { - if (count) { process_params(count, args); } + if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || + getenv("INSTRIM_LIB")) { - // we cannot free args[] - free(tmpbuf); + FATAL( + "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD " + "(default in afl-cc).\n"); - continue; + } - } + if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || + getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { - cc_params[cc_par_cnt++] = cur; + 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")) + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; -/* Copy argv to cc_params, making the necessary edits. */ - -static void edit_params(u32 argc, char **argv, char **envp) { + if (getenv("AFL_LLVM_NGRAM_SIZE")) { - cc_params = ck_alloc(MAX_PARAMS_NUM * sizeof(u8 *)); + 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); - for (u32 c = 1; c < argc; ++c) { + } - if (!strcmp(argv[c], "-c")) have_c = 1; - if (!strncmp(argv[c], "-fsanitize-coverage-", 20) && - strstr(argv[c], "list=")) { + if (getenv("AFL_LLVM_CTX_K")) { - have_instr_list = 1; + 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 { - if (lto_mode) { + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; - if (lto_flag[0] != '-') - FATAL( - "Using afl-clang-lto is not possible because Makefile magic did not " - "identify the correct -flto flag"); - else - compiler_mode = LTO; + } } - if (plusplus_mode) { +} - u8 *alt_cxx = getenv("AFL_CXX"); +// compiler_mode would also be set if depended by the instrument_mode +static void instrument_mode_new_environ(aflcc_state_t *aflcc) { - if (!alt_cxx) { + if (!getenv("AFL_LLVM_INSTRUMENT")) { return; } - if (compiler_mode >= GCC_PLUGIN) { + u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); - if (compiler_mode == GCC) { + while (ptr2) { - alt_cxx = clang_mode ? "clang++" : "g++"; + if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || + strncasecmp(ptr2, "classic", strlen("classic")) == 0) { - } else if (compiler_mode == CLANG) { + if (aflcc->instrument_mode == INSTRUMENT_LTO) { - alt_cxx = "clang++"; + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->lto_mode = 1; - } else { + } else if (!aflcc->instrument_mode || - alt_cxx = "g++"; + aflcc->instrument_mode == INSTRUMENT_AFL) { - } + aflcc->instrument_mode = INSTRUMENT_AFL; } 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; + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); } } - cc_params[0] = alt_cxx; + if (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || + strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { - } else { + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_PCGUARD) - u8 *alt_cc = getenv("AFL_CC"); + aflcc->instrument_mode = INSTRUMENT_PCGUARD; - if (!alt_cc) { + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); - if (compiler_mode >= GCC_PLUGIN) { + } - if (compiler_mode == GCC) { + if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || + strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 || + strncasecmp(ptr2, "native", strlen("native")) == 0) { - alt_cc = clang_mode ? "clang" : "gcc"; + if (!aflcc->instrument_mode || + aflcc->instrument_mode == INSTRUMENT_LLVMNATIVE) - } else if (compiler_mode == CLANG) { + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - alt_cc = "clang"; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); - } else { + } - alt_cc = "gcc"; + 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 { - if (USE_BINDIR) - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s/clang", - LLVM_BINDIR); - else - snprintf(llvm_fullpath, sizeof(llvm_fullpath), "%s", CLANG_BIN); - alt_cc = llvm_fullpath; + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); } } - cc_params[0] = alt_cc; + 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 (compiler_mode == GCC || compiler_mode == CLANG) { + } + + if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { - cc_params[cc_par_cnt++] = "-B"; - cc_params[cc_par_cnt++] = obj_path; + aflcc->lto_mode = 1; + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_LTO) - if (clang_mode || compiler_mode == CLANG) { + aflcc->instrument_mode = INSTRUMENT_LTO; - cc_params[cc_par_cnt++] = "-no-integrated-as"; + else + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); } - } +#if defined(__x86_64__) + if (strcasecmp(ptr2, "gcc") == 0) { - if (compiler_mode == GCC_PLUGIN) { + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_GCC) - char *fplugin_arg; + aflcc->instrument_mode = INSTRUMENT_GCC; - if (cmplog_mode) { + else if (aflcc->instrument_mode != INSTRUMENT_GCC) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); - fplugin_arg = - alloc_printf("-fplugin=%s/afl-gcc-cmplog-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; - fplugin_arg = - alloc_printf("-fplugin=%s/afl-gcc-cmptrs-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; + aflcc->compiler_mode = GCC; } - fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; - cc_params[cc_par_cnt++] = "-fno-if-conversion"; - cc_params[cc_par_cnt++] = "-fno-if-conversion2"; +#endif - } +#if defined(__x86_64__) + if (strcasecmp(ptr2, "clang") == 0) { - if (compiler_mode == LLVM || compiler_mode == LTO) { + if (!aflcc->instrument_mode || aflcc->instrument_mode == INSTRUMENT_CLANG) - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + aflcc->instrument_mode = INSTRUMENT_CLANG; - if (lto_mode && have_instr_env) { + else if (aflcc->instrument_mode != INSTRUMENT_CLANG) + FATAL("main instrumentation mode already set with %s", + instrument_mode_2str(aflcc->instrument_mode)); -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = alloc_printf( - "-fpass-plugin=%s/afl-llvm-lto-instrumentlist.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-lto-instrumentlist.so", obj_path); -#endif + aflcc->compiler_mode = CLANG; } - if (getenv("AFL_LLVM_DICT2FILE")) { - -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/afl-llvm-dict2file.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-dict2file.so", obj_path); #endif - } + if (strncasecmp(ptr2, "ctx-", strlen("ctx-")) == 0 || + strncasecmp(ptr2, "kctx-", strlen("c-ctx-")) == 0 || + strncasecmp(ptr2, "k-ctx-", strlen("k-ctx-")) == 0) { - // laf - if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { + u8 *ptr3 = ptr2; + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) + ptr3++; -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); -#endif + 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 (getenv("LAF_TRANSFORM_COMPARES") || - getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) { + if (strcasecmp(ptr2, "ctx") == 0) { -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/compare-transform-pass.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/compare-transform-pass.so", obj_path); -#endif + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CTX; + setenv("AFL_LLVM_CTX", "1", 1); } - if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") || - getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) { + if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/split-compares-pass.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-compares-pass.so", obj_path); -#endif + aflcc->instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + setenv("AFL_LLVM_CALLER", "1", 1); } - // /laf + if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { - unsetenv("AFL_LD"); - unsetenv("AFL_LD_CALLER"); + u8 *ptr3 = ptr2 + strlen("ngram"); + while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) { - if (cmplog_mode) { + ptr3++; - cc_params[cc_par_cnt++] = "-fno-inline"; + } -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/cmplog-switches-pass.so", obj_path); - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/split-switches-pass.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-switches-pass.so", obj_path); + if (!*ptr3) { - // reuse split switches from laf - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/split-switches-pass.so", obj_path); -#endif + 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); } - // #if LLVM_MAJOR >= 13 - // // Use the old pass manager in LLVM 14 which the AFL++ passes still - // use. cc_params[cc_par_cnt++] = "-flegacy-pass-manager"; - // #endif + ptr2 = strtok(NULL, ":,;"); - if (lto_mode && !have_c) { + } - u8 *ld_path = NULL; - if (getenv("AFL_REAL_LD")) { +} - ld_path = strdup(getenv("AFL_REAL_LD")); +void instrument_mode_by_environ(aflcc_state_t *aflcc) { - } else { + if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || + getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || + getenv("AFL_LLVM_BLOCKLIST")) { - ld_path = strdup(AFL_REAL_LD); + aflcc->have_instr_env = 1; - } + } - if (!ld_path || !*ld_path) { + if (aflcc->have_instr_env && getenv("AFL_DONT_OPTIMIZE") && !be_quiet) { - if (ld_path) { + WARNF( + "AFL_LLVM_ALLOWLIST/DENYLIST and AFL_DONT_OPTIMIZE cannot be combined " + "for file matching, only function matching!"); - // Freeing empty string - free(ld_path); + } - } + instrument_mode_old_environ(aflcc); + instrument_mode_new_environ(aflcc); - ld_path = strdup("ld.lld"); +} - } +static void instrument_opt_mode_exclude(aflcc_state_t *aflcc) { - if (!ld_path) { PFATAL("Could not allocate mem for ld_path"); } -#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 12 - cc_params[cc_par_cnt++] = alloc_printf("--ld-path=%s", ld_path); -#else - cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", ld_path); -#endif - free(ld_path); + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { -#if defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 15 - // The NewPM implementation only works fully since LLVM 15. - cc_params[cc_par_cnt++] = alloc_printf( - "-Wl,--load-pass-plugin=%s/SanitizerCoverageLTO.so", obj_path); -#elif defined(AFL_CLANG_LDPATH) && LLVM_MAJOR >= 13 - cc_params[cc_par_cnt++] = "-Wl,--lto-legacy-pass-manager"; - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-fno-experimental-new-pass-manager"; - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,-mllvm=-load=%s/SanitizerCoverageLTO.so", obj_path); -#endif + FATAL("you cannot set CTX and CALLER together"); - cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition"; - cc_params[cc_par_cnt++] = lto_flag; + } - } else { + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { - if (instrument_mode == INSTRUMENT_PCGUARD) { + FATAL("you cannot set CTX and K-CTX together"); -#if LLVM_MAJOR >= 13 - #if defined __ANDROID__ || ANDROID - cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; - instrument_mode = INSTRUMENT_LLVMNATIVE; - #else - if (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_LLMV_DENYLIST instead.\n"); - cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; - instrument_mode = INSTRUMENT_LLVMNATIVE; - - } else { - - #if LLVM_MAJOR >= 13 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = alloc_printf( - "-fpass-plugin=%s/SanitizerCoveragePCGUARD.so", obj_path); - #else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/SanitizerCoveragePCGUARD.so", obj_path); - #endif + } - } + if ((aflcc->instrument_opt_mode & INSTRUMENT_OPT_CALLER) && + (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { - #endif -#else - #if LLVM_MAJOR >= 4 - if (!be_quiet) - SAYF( - "Using unoptimized trace-pc-guard, upgrade to LLVM 13+ for " - "enhanced version.\n"); - cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; - instrument_mode = INSTRUMENT_LLVMNATIVE; - #else - FATAL("pcguard instrumentation requires LLVM 4.0.1+"); - #endif -#endif + FATAL("you cannot set CALLER and K-CTX together"); - } else if (instrument_mode == INSTRUMENT_LLVMNATIVE) { + } -#if LLVM_MAJOR >= 4 - if (instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { + if (aflcc->instrument_opt_mode && aflcc->compiler_mode != LLVM) + FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); - #if LLVM_MAJOR >= 6 - cc_params[cc_par_cnt++] = - "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"; - #else - FATAL("pcguard instrumentation with pc-table requires LLVM 6.0.1+"); - #endif + if (aflcc->instrument_opt_mode && + aflcc->instrument_opt_mode != INSTRUMENT_OPT_CODECOV && + aflcc->instrument_mode != INSTRUMENT_CLASSIC) + FATAL( + "CALLER, CTX and NGRAM instrumentation options can only be used with " + "the LLVM CLASSIC instrumentation mode."); - } else { +} - cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard"; +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)) { -#else - FATAL("pcguard instrumentation requires LLVM 4.0.1+"); -#endif + aflcc->instrument_mode = INSTRUMENT_CLASSIC; + aflcc->compiler_mode = LLVM; - } else { + } -#if LLVM_MAJOR >= 11 /* use new pass manager */ - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/afl-llvm-pass.so", obj_path); -#else + if (!aflcc->compiler_mode) { - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path); -#endif + // 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 not available on your platform!"); + break; + case CLANG: + if (!aflcc->have_clang) + FATAL("afl-clang 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 (cmplog_mode) { + } -#if LLVM_MAJOR >= 11 - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = alloc_printf( - "-fpass-plugin=%s/cmplog-instructions-pass.so", obj_path); - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; - #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/cmplog-routines-pass.so", obj_path); -#else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-instructions-pass.so", obj_path); - - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = - alloc_printf("%s/cmplog-routines-pass.so", obj_path); -#endif + 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; + // force CFG + // if (!aflcc->instrument_mode) { + + 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 (getenv("AFL_LLVM_INJECTIONS_ALL") || - getenv("AFL_LLVM_INJECTIONS_SQL") || - getenv("AFL_LLVM_INJECTIONS_LDAP") || - getenv("AFL_LLVM_INJECTIONS_XSS")) { + } + + 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 -#if LLVM_MAJOR >= 11 - #if LLVM_MAJOR < 16 - cc_params[cc_par_cnt++] = "-fexperimental-new-pass-manager"; #endif - cc_params[cc_par_cnt++] = - alloc_printf("-fpass-plugin=%s/injection-pass.so", obj_path); + aflcc->instrument_mode = INSTRUMENT_PCGUARD; + #else - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = "-load"; - cc_params[cc_par_cnt++] = "-Xclang"; - cc_params[cc_par_cnt++] = alloc_printf("%s/injection-pass.so", obj_path); + aflcc->instrument_mode = INSTRUMENT_AFL; #endif - } + } - // cc_params[cc_par_cnt++] = "-Qunused-arguments"; + if (!aflcc->instrument_opt_mode && aflcc->lto_mode && + aflcc->instrument_mode == INSTRUMENT_CFG) { - } + aflcc->instrument_mode = INSTRUMENT_PCGUARD; - /* Inspect the command line parameters. */ + } - process_params(argc, argv); +#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 (!have_pic) { + if (aflcc->lto_mode) { - cc_params[cc_par_cnt++] = "-fPIC"; - have_pic = 1; + 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 (compiler_mode != GCC_PLUGIN && compiler_mode != GCC && - !getenv("AFL_LLVM_NO_RPATH")) { + 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"); - // 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 (plusplus_mode && strlen(libdir) && strncmp(libdir, "/usr", 4) && - strncmp(libdir, "/lib", 4)) { +#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) - u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR); - cc_params[cc_par_cnt++] = libdir_opt; + 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."); } - if (getenv("AFL_HARDEN")) { +#endif - cc_params[cc_par_cnt++] = "-fstack-protector-all"; + instrument_opt_mode_exclude(aflcc); - if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2"; + u8 *ptr2; - } + if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') + FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); - if (!asan_set) { + if (getenv("AFL_LLVM_LAF_ALL")) { - if (getenv("AFL_USE_ASAN")) { + 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_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); + } - if (getenv("AFL_HARDEN")) - FATAL("ASAN and AFL_HARDEN are mutually exclusive"); + aflcc->cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || + getenv("AFL_GCC_CMPLOG"); - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=address"; +} - } else if (getenv("AFL_USE_MSAN")) { +void mode_notification(aflcc_state_t *aflcc) { - if (getenv("AFL_USE_ASAN")) FATAL("ASAN and MSAN are mutually exclusive"); + char *ptr2 = alloc_printf(" + NGRAM-%u", aflcc->ngram_size); + char *ptr3 = alloc_printf(" + K-CTX-%u", aflcc->ctx_k); - if (getenv("AFL_HARDEN")) - FATAL("MSAN and AFL_HARDEN are mutually exclusive"); + 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 : ""); - cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE"; - cc_params[cc_par_cnt++] = "-fsanitize=memory"; + 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); } - if (getenv("AFL_USE_UBSAN")) { + ck_free(ptr1); - cc_params[cc_par_cnt++] = "-fsanitize=undefined"; - cc_params[cc_par_cnt++] = "-fsanitize-undefined-trap-on-error"; - cc_params[cc_par_cnt++] = "-fno-sanitize-recover=all"; - cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer"; + 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!"); } - if (getenv("AFL_USE_TSAN")) { +} - cc_params[cc_par_cnt++] = "-fsanitize=thread"; - cc_params[cc_par_cnt++] = "-fno-omit-frame-pointer"; +void add_real_argv0(aflcc_state_t *aflcc) { - } + static u8 llvm_fullpath[PATH_MAX]; - if (getenv("AFL_USE_LSAN")) { + if (aflcc->plusplus_mode) { - cc_params[cc_par_cnt++] = "-fsanitize=leak"; - cc_params[cc_par_cnt++] = "-includesanitizer/lsan_interface.h"; - cc_params[cc_par_cnt++] = - "-D__AFL_LEAK_CHECK()={if(__lsan_do_recoverable_leak_check() > 0) " - "_exit(23); }"; - cc_params[cc_par_cnt++] = "-D__AFL_LSAN_OFF()=__lsan_disable();"; - cc_params[cc_par_cnt++] = "-D__AFL_LSAN_ON()=__lsan_enable();"; + u8 *alt_cxx = getenv("AFL_CXX"); - } + if (!alt_cxx) { - if (getenv("AFL_USE_CFISAN")) { + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { - if (compiler_mode == GCC_PLUGIN || compiler_mode == GCC) { + alt_cxx = "g++"; - cc_params[cc_par_cnt++] = "-fcf-protection=full"; + } else if (aflcc->compiler_mode == CLANG) { - } else { + alt_cxx = "clang++"; - if (!lto_mode) { + } else { - uint32_t i = 0, found = 0; - while (envp[i] != NULL && !found) - if (strncmp("-flto", envp[i++], 5) == 0) found = 1; - if (!found) cc_params[cc_par_cnt++] = "-flto"; + 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; } - cc_params[cc_par_cnt++] = "-fsanitize=cfi"; - cc_params[cc_par_cnt++] = "-fvisibility=hidden"; - } - } + aflcc->cc_params[0] = alt_cxx; - if (!getenv("AFL_DONT_OPTIMIZE")) { + } else { - cc_params[cc_par_cnt++] = "-g"; - if (!have_o) cc_params[cc_par_cnt++] = "-O3"; - if (!have_unroll) cc_params[cc_par_cnt++] = "-funroll-loops"; - // if (strlen(march_opt) > 1 && march_opt[0] == '-') - // cc_params[cc_par_cnt++] = march_opt; + u8 *alt_cc = getenv("AFL_CC"); - } + if (!alt_cc) { - if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || - getenv("LAF_TRANSFORM_COMPARES") || getenv("AFL_LLVM_LAF_ALL") || - lto_mode) { + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == GCC_PLUGIN) { - cc_params[cc_par_cnt++] = "-fno-builtin-strcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-memcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-bcmp"; - cc_params[cc_par_cnt++] = "-fno-builtin-strstr"; - cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr"; + alt_cc = "gcc"; - } + } else if (aflcc->compiler_mode == CLANG) { -#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ - if (!have_c) cc_params[cc_par_cnt++] = "-lrt"; -#endif + alt_cc = "clang"; - cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1"; - cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"; + } else { - /* As documented in instrumentation/README.persistent_mode.md, deferred - forkserver initialization and persistent mode are not available in afl-gcc - and afl-clang. */ - if (compiler_mode != GCC && compiler_mode != CLANG) { + 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; - cc_params[cc_par_cnt++] = "-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)). + aflcc->cc_params[0] = alt_cc; - 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. +} - */ +/* Macro defs for the preprocessor */ - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_INIT()=" - "int __afl_sharedmem_fuzzing = 1;" - "extern unsigned int *__afl_fuzz_len;" - "extern unsigned char *__afl_fuzz_ptr;" - "unsigned char __afl_fuzz_alt[1048576];" - "unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;"; +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"); - if (plusplus_mode) { +} + +/* 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) { - cc_params[cc_par_cnt++] = - "-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();"; + 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 { - cc_params[cc_par_cnt++] = - "-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()=int __afl_selective_coverage = 1;" + "void __afl_coverage_discard();" + "void __afl_coverage_skip();" + "void __afl_coverage_on();" + "void __afl_coverage_off();"); } - cc_params[cc_par_cnt++] = + insert_param( + aflcc, "-D__AFL_COVERAGE_START_OFF()=int __afl_selective_coverage_start_off = " - "1;"; - cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_ON()=__afl_coverage_on()"; - cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_OFF()=__afl_coverage_off()"; - cc_params[cc_par_cnt++] = - "-D__AFL_COVERAGE_DISCARD()=__afl_coverage_discard()"; - cc_params[cc_par_cnt++] = "-D__AFL_COVERAGE_SKIP()=__afl_coverage_skip()"; - cc_params[cc_par_cnt++] = - "-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : " - "__afl_fuzz_alt_ptr)"; - cc_params[cc_par_cnt++] = - "-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)"; + "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()"); - if (compiler_mode != GCC && compiler_mode != CLANG) { +} + +/* 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 unsigned int *__afl_fuzz_len;" + "extern 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)"); - cc_params[cc_par_cnt++] = - "-D__AFL_LOOP(_A)=" - "({ static volatile const char *_B __attribute__((used,unused)); " - " _B = (const char*)\"" PERSIST_SIG - "\"; " - "extern int __afl_connected;" + 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 int __afl_connected;" #ifdef __APPLE__ - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " + "__attribute__((visibility(\"default\"))) " + "int _L(unsigned int) __asm__(\"___afl_persistent_loop\"); " #else - "__attribute__((visibility(\"default\"))) " - "int _L(unsigned int) __asm__(\"__afl_persistent_loop\"); " + "__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); })"; - - cc_params[cc_par_cnt++] = - "-D__AFL_INIT()=" - "do { static volatile const char *_A __attribute__((used,unused)); " - " _A = (const char*)\"" DEFER_SIG - "\"; " + // 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\"); " + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"___afl_manual_init\"); " #else - "__attribute__((visibility(\"default\"))) " - "void _I(void) __asm__(\"__afl_manual_init\"); " + "__attribute__((visibility(\"default\"))) " + "void _I(void) __asm__(\"__afl_manual_init\"); " #endif /* ^__APPLE__ */ - "_I(); } while (0)"; + "_I(); } while (0)"); - } +} - if (x_set) { +/* Control _FORTIFY_SOURCE */ +void add_defs_fortify(aflcc_state_t *aflcc, u8 action) { - cc_params[cc_par_cnt++] = "-x"; - cc_params[cc_par_cnt++] = "none"; + switch (action) { - } + case 1: + insert_param(aflcc, "-D_FORTIFY_SOURCE=1"); + break; - // prevent unnecessary build errors - if (compiler_mode != GCC_PLUGIN && compiler_mode != GCC) { + case 2: + insert_param(aflcc, "-D_FORTIFY_SOURCE=2"); + break; - cc_params[cc_par_cnt++] = "-Wno-unused-command-line-argument"; + default: // OFF + insert_param(aflcc, "-U_FORTIFY_SOURCE"); + break; } - if (preprocessor_only || have_c || !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. */ - cc_params[cc_par_cnt] = NULL; - return; +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();"); -#ifndef __ANDROID__ +} - if (compiler_mode != GCC && compiler_mode != CLANG) { +/* About fsanitize (including PCGUARD features) */ - switch (bit_mode) { +/* For input "-fsanitize=...", it: - case 0: - if (!shared_linking && !partial_linking) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt.o", obj_path); - if (lto_mode) - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto.o", obj_path); - break; + 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) { - case 32: - if (!shared_linking && !partial_linking) { + u8 detect_single_fuzzer = 0; - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); + 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="); - if (lto_mode) { + do { - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-32.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m32 is not supported by your compiler"); + p = strchr(ptr, ','); + if (!p) { - } + p = ptr + strlen(ptr) + 1; + ende = 1; - break; + } - case 64: - if (!shared_linking && !partial_linking) { + 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)) { - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-compiler-rt-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); + copy = 0; } - if (lto_mode) { + if (copy) { - cc_params[cc_par_cnt++] = - alloc_printf("%s/afl-llvm-rt-lto-64.o", obj_path); - if (access(cc_params[cc_par_cnt - 1], R_OK)) - FATAL("-m64 is not supported by your compiler"); + if (count) { strcat(new, ","); } + strcat(new, tmp); + ++count; } - break; + } + + } else { + + ptr++; } - #if !defined(__APPLE__) && !defined(__sun) - if (!shared_linking && !partial_linking) - cc_params[cc_par_cnt++] = - alloc_printf("-Wl,--dynamic-list=%s/dynamic_list.txt", obj_path); - #endif + } while (!ende); - #if defined(__APPLE__) - if (shared_linking || partial_linking) { + strcpy(string, new); + + ck_free(tmp); + ck_free(new); + + return detect_single_fuzzer; + +} - cc_params[cc_par_cnt++] = "-Wl,-U"; - cc_params[cc_par_cnt++] = "-Wl,___afl_area_ptr"; - cc_params[cc_par_cnt++] = "-Wl,-U"; - cc_params[cc_par_cnt++] = "-Wl,___sanitizer_cov_trace_pc_guard_init"; +param_st parse_fsanitize(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { + + param_st final_ = PARAM_MISS; + + if (!strncmp(cur_argv, "-fsanitize-coverage-", 20) && + strstr(cur_argv, "list=")) { + + if (scan) { + + aflcc->have_instr_list = 1; + final_ = PARAM_SCAN; + + } else { + + final_ = PARAM_KEEP; // may be set to DROP next } - #endif + } + + 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=fuzzer-", + + strlen("-fsanitize=fuzzer-")) || + !strncmp(cur_argv, "-fsanitize-coverage", + strlen("-fsanitize-coverage"))) && + (strncmp(cur_argv, "sanitize-coverage-allow", + strlen("sanitize-coverage-allow")) && + strncmp(cur_argv, "sanitize-coverage-deny", + strlen("sanitize-coverage-deny")) && + aflcc->instrument_mode != INSTRUMENT_LLVMNATIVE)) { + + if (scan) { + + final_ = PARAM_SCAN; + + } else { + + if (!be_quiet) { WARNF("Found '%s' - stripping!", cur_argv); } + final_ = PARAM_DROP; + + } } - #if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ - cc_params[cc_par_cnt++] = "-lrt"; - #endif + if (!strcmp(cur_argv, "-fsanitize=address") || + !strcmp(cur_argv, "-fsanitize=memory")) { -#endif + if (scan) { + + // "-fsanitize=undefined,address" may be un-treated, but it's OK. + aflcc->asan_set = 1; + final_ = PARAM_SCAN; + + } else { - cc_params[cc_par_cnt] = NULL; + // It's impossible that final_ is PARAM_DROP before, + // so no checks are needed here. + final_ = PARAM_KEEP; + + } + + } + + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); + + return final_; } -/* Main entry point */ +void add_sanitizers(aflcc_state_t *aflcc, char **envp) { -int main(int argc, char **argv, char **envp) { + if (!aflcc->asan_set) { - int i; - char *callname = argv[0], *ptr = NULL; + if (getenv("AFL_USE_ASAN")) { - if (getenv("AFL_DEBUG")) { + if (getenv("AFL_USE_MSAN")) FATAL("ASAN and MSAN are mutually exclusive"); - debug = 1; - if (strcmp(getenv("AFL_DEBUG"), "0") == 0) unsetenv("AFL_DEBUG"); + if (getenv("AFL_HARDEN")) + FATAL("ASAN and AFL_HARDEN are mutually exclusive"); - } else if (getenv("AFL_QUIET")) + add_defs_fortify(aflcc, 0); + insert_param(aflcc, "-fsanitize=address"); - be_quiet = 1; + } else if (getenv("AFL_USE_MSAN")) { - if (getenv("AFL_LLVM_INSTRUMENT_FILE") || getenv("AFL_LLVM_WHITELIST") || - getenv("AFL_LLVM_ALLOWLIST") || getenv("AFL_LLVM_DENYLIST") || - getenv("AFL_LLVM_BLOCKLIST")) { + if (getenv("AFL_USE_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); + insert_param(aflcc, "-fsanitize=memory"); - have_instr_env = 1; + } } - if (getenv("AFL_PASSTHROUGH") || getenv("AFL_NOOPT")) { + if (getenv("AFL_USE_UBSAN")) { - passthrough = 1; - if (!debug) { be_quiet = 1; } + insert_param(aflcc, "-fsanitize=undefined"); + insert_param(aflcc, "-fsanitize-undefined-trap-on-error"); + insert_param(aflcc, "-fno-sanitize-recover=all"); + insert_param(aflcc, "-fno-omit-frame-pointer"); } - if ((ptr = strrchr(callname, '/')) != NULL) callname = ptr + 1; - argvnull = (u8 *)argv[0]; - check_environment_vars(envp); + if (getenv("AFL_USE_TSAN")) { - if ((ptr = find_object("as", argv[0])) != NULL) { + insert_param(aflcc, "-fsanitize=thread"); + insert_param(aflcc, "-fno-omit-frame-pointer"); - have_gcc = 1; - ck_free(ptr); + } + + if (getenv("AFL_USE_LSAN")) { + + insert_param(aflcc, "-fsanitize=leak"); + add_defs_lsan_ctrl(aflcc); } -#if (LLVM_MAJOR >= 3) + if (getenv("AFL_USE_CFISAN")) { - if ((ptr = find_object("SanitizerCoverageLTO.so", argv[0])) != NULL) { + if (aflcc->compiler_mode == GCC_PLUGIN || aflcc->compiler_mode == GCC) { - have_lto = 1; - ck_free(ptr); + insert_param(aflcc, "-fcf-protection=full"); - } + } else { - if ((ptr = find_object("cmplog-routines-pass.so", argv[0])) != NULL) { + if (!aflcc->lto_mode) { - have_llvm = 1; - ck_free(ptr); + 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"); + + } + + insert_param(aflcc, "-fsanitize=cfi"); + insert_param(aflcc, "-fvisibility=hidden"); + + } } -#endif +} -#ifdef __ANDROID__ - have_llvm = 1; +void add_native_pcguard(aflcc_state_t *aflcc) { + + /* If llvm-config doesn't figure out LLVM_MAJOR, just + go on anyway and let compiler complain if doesn't work. */ + + if (aflcc->instrument_opt_mode & INSTRUMENT_OPT_CODECOV) { + +#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 + insert_param(aflcc, + "-fsanitize-coverage=trace-pc-guard,bb,no-prune,pc-table"); #endif - if ((ptr = find_object("afl-gcc-pass.so", argv[0])) != NULL) { + } else { - have_gcc_plugin = 1; - ck_free(ptr); +#if LLVM_MAJOR > 0 && LLVM_MAJOR < 4 + FATAL("pcguard instrumentation requires LLVM 4.0.1+"); +#else + #if LLVM_MAJOR == 0 + WARNF( + "pcguard instrumentation requires LLVM 4.0.1+" + " otherwise the compiler will fail"); + #endif + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); +#endif } -#if (LLVM_MAJOR >= 3) +} + +void add_optimized_pcguard(aflcc_state_t *aflcc) { + +#if LLVM_MAJOR >= 13 + #if defined __ANDROID__ || ANDROID - if (strncmp(callname, "afl-clang-fast", 14) == 0) { + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - compiler_mode = LLVM; + #else - } else if (strncmp(callname, "afl-clang-lto", 13) == 0 || + if (aflcc->have_instr_list) { - strncmp(callname, "afl-lto", 7) == 0) { + if (!be_quiet) + SAYF( + "Using unoptimized trace-pc-guard, due usage of " + "-fsanitize-coverage-allow/denylist, you can use " + "AFL_LLVM_ALLOWLIST/AFL_LLMV_DENYLIST instead.\n"); - compiler_mode = LTO; + insert_param(aflcc, "-fsanitize-coverage=trace-pc-guard"); + aflcc->instrument_mode = INSTRUMENT_LLVMNATIVE; - } else + } else { -#endif - if (strncmp(callname, "afl-gcc-fast", 12) == 0 || + /* 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; - strncmp(callname, "afl-g++-fast", 12) == 0) { + #else - compiler_mode = GCC_PLUGIN; + FATAL("pcguard instrumentation requires LLVM 4.0.1+"); - } else if (strncmp(callname, "afl-gcc", 7) == 0 || + #endif +#endif - strncmp(callname, "afl-g++", 7) == 0) { +} - compiler_mode = GCC; +/* Linking behaviors */ - } else if (strcmp(callname, "afl-clang") == 0 || +param_st parse_linking_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan, + u8 *skip_next, char **argv) { - strcmp(callname, "afl-clang++") == 0) { + if (aflcc->lto_mode && !strncmp(cur_argv, "-flto=thin", 10)) { - compiler_mode = CLANG; + FATAL( + "afl-clang-lto cannot work with -flto=thin. Switch to -flto=full or " + "use afl-clang-fast!"); } - if ((ptr = getenv("AFL_CC_COMPILER"))) { + param_st final_ = PARAM_MISS; - if (compiler_mode) { + if (!strcmp(cur_argv, "-shared") || !strcmp(cur_argv, "-dynamiclib")) { - if (!be_quiet) { + if (scan) { - WARNF( - "\"AFL_CC_COMPILER\" is set but a specific compiler was already " - "selected by command line parameter or symlink, ignoring the " - "environment variable!"); + 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 { - if (strncasecmp(ptr, "LTO", 3) == 0) { + final_ = PARAM_KEEP; - compiler_mode = LTO; + } - } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + } else if (!strncmp(cur_argv, "-fuse-ld=", 9) || - compiler_mode = LLVM; + !strncmp(cur_argv, "--ld-path=", 10)) { - } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || + if (scan) { - strncasecmp(ptr, "GCC-P", 5) == 0 || - strncasecmp(ptr, "GCCP", 4) == 0) { + final_ = PARAM_SCAN; - compiler_mode = GCC_PLUGIN; + } else { - } else if (strcasecmp(ptr, "GCC") == 0) { + if (aflcc->lto_mode) + final_ = PARAM_DROP; + else + final_ = PARAM_KEEP; - compiler_mode = GCC; + } - } else + } 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 { - FATAL("Unknown AFL_CC_COMPILER mode: %s\n", ptr); + 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 (strcmp(callname, "afl-clang") == 0 || - strcmp(callname, "afl-clang++") == 0) { + if (scan) { - clang_mode = 1; - compiler_mode = CLANG; + final_ = PARAM_SCAN; - if (strcmp(callname, "afl-clang++") == 0) { plusplus_mode = 1; } + } else { + + final_ = PARAM_DROP; + + } + + } } - for (i = 1; i < argc; i++) { + // Try to warn user for some unsupported cases + if (scan && final_ == PARAM_MISS) { - if (strncmp(argv[i], "--afl", 5) == 0) { + u8 *ptr_ = NULL; - if (!strcmp(argv[i], "--afl_noopt") || !strcmp(argv[i], "--afl-noopt")) { + if (!strcmp(cur_argv, "-Xlinker") && (ptr_ = *(argv + 1))) { - passthrough = 1; - argv[i] = "-g"; // we have to overwrite it, -g is always good - continue; + if (!strcmp(ptr_, "defs")) { - } + WARNF("'-Xlinker' 'defs' detected. This may result in a bad link."); - if (compiler_mode && !be_quiet) { + } else if (strstr(ptr_, "-no-undefined")) { WARNF( - "--afl-... compiler mode supersedes the AFL_CC_COMPILER and " - "symlink compiler selection!"); + "'-Xlinker' '%s' detected. The latter option may be dropped and " + "result in a bad link.", + ptr_); } - ptr = argv[i]; - ptr += 5; - while (*ptr == '-') - ptr++; + } else if (!strncmp(cur_argv, "-Wl,", 4) && - if (strncasecmp(ptr, "LTO", 3) == 0) { + (u8 *)strrchr(cur_argv, ',') != (cur_argv + 3)) { - compiler_mode = LTO; + ptr_ = cur_argv + 4; - } else if (strncasecmp(ptr, "LLVM", 4) == 0) { + if (strstr(ptr_, "-shared") || strstr(ptr_, "-dynamiclib")) { - compiler_mode = LLVM; + WARNF( + "'%s': multiple link options after '-Wl,' may break shared " + "linking.", + ptr_); - } else if (strncasecmp(ptr, "PCGUARD", 7) == 0 || + } - strncasecmp(ptr, "PC-GUARD", 8) == 0) { + if (strstr(ptr_, "-r,") || strstr(ptr_, "-i,") || strstr(ptr_, ",-r") || + strstr(ptr_, ",-i") || strstr(ptr_, "--relocatable")) { - compiler_mode = LLVM; - instrument_mode = INSTRUMENT_PCGUARD; + WARNF( + "'%s': multiple link options after '-Wl,' may break partial " + "linking.", + ptr_); - } else if (strcasecmp(ptr, "INSTRIM") == 0 || + } - strcasecmp(ptr, "CFG") == 0) { + if (strstr(ptr_, "defs") || strstr(ptr_, "no-undefined")) { - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and " - "PCGUARD (default in afl-cc).\n"); + WARNF( + "'%s': multiple link options after '-Wl,' may enable report " + "unresolved symbol references and result in a bad link.", + ptr_); - } else if (strcasecmp(ptr, "AFL") == 0 || + } - strcasecmp(ptr, "CLASSIC") == 0) { + } - compiler_mode = LLVM; - instrument_mode = INSTRUMENT_CLASSIC; + } - } else if (strcasecmp(ptr, "LLVMNATIVE") == 0 || + if (final_ == PARAM_KEEP) insert_param(aflcc, cur_argv); - strcasecmp(ptr, "NATIVE") == 0 || - strcasecmp(ptr, "LLVM-NATIVE") == 0) { + return final_; - compiler_mode = LLVM; - instrument_mode = INSTRUMENT_LLVMNATIVE; +} - } else if (strncasecmp(ptr, "GCC_P", 5) == 0 || +void add_lto_linker(aflcc_state_t *aflcc) { - strncasecmp(ptr, "GCC-P", 5) == 0 || - strncasecmp(ptr, "GCCP", 4) == 0) { + unsetenv("AFL_LD"); + unsetenv("AFL_LD_CALLER"); - compiler_mode = GCC_PLUGIN; + u8 *ld_path = NULL; + if (getenv("AFL_REAL_LD")) { - } else if (strcasecmp(ptr, "GCC") == 0) { + ld_path = strdup(getenv("AFL_REAL_LD")); - compiler_mode = GCC; + } else { - } else if (strncasecmp(ptr, "CLANG", 5) == 0) { + ld_path = strdup(AFL_REAL_LD); - compiler_mode = CLANG; + } - } else + if (!ld_path || !*ld_path) { - FATAL("Unknown --afl-... compiler mode: %s\n", argv[i]); + if (ld_path) { + + // Freeing empty string + free(ld_path); } + ld_path = strdup("ld.lld"); + } - if (strlen(callname) > 2 && - (strncmp(callname + strlen(callname) - 2, "++", 2) == 0 || - strstr(callname, "-g++") != NULL)) - plusplus_mode = 1; + 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); + +} - if (getenv("USE_TRACE_PC") || getenv("AFL_USE_TRACE_PC") || - getenv("AFL_LLVM_USE_TRACE_PC") || getenv("AFL_TRACE_PC")) { +void add_lto_passes(aflcc_state_t *aflcc) { - if (instrument_mode == 0) - instrument_mode = INSTRUMENT_PCGUARD; - else if (instrument_mode != INSTRUMENT_PCGUARD) - FATAL("you cannot set AFL_LLVM_INSTRUMENT and AFL_TRACE_PC together"); +#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"); - if (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!"); +static void add_aflpplib(aflcc_state_t *aflcc) { - } + if (!aflcc->need_aflpplib) return; - if (getenv("AFL_LLVM_INSTRIM") || getenv("INSTRIM") || - getenv("INSTRIM_LIB")) { + u8 *afllib = find_object(aflcc, "libAFLDriver.a"); - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and PCGUARD " - "(default in afl-cc).\n"); + if (!be_quiet) { + + OKF("Found '-fsanitize=fuzzer', replacing with libAFLDriver.a"); } - if (getenv("AFL_LLVM_CTX")) instrument_opt_mode |= INSTRUMENT_OPT_CTX; - if (getenv("AFL_LLVM_CALLER")) instrument_opt_mode |= INSTRUMENT_OPT_CALLER; + if (!afllib) { - if (getenv("AFL_LLVM_NGRAM_SIZE")) { + if (!be_quiet) { - instrument_opt_mode |= INSTRUMENT_OPT_NGRAM; - ngram_size = atoi(getenv("AFL_LLVM_NGRAM_SIZE")); - if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) - FATAL( - "NGRAM instrumentation mode must be between 2 and NGRAM_SIZE_MAX " - "(%u)", - NGRAM_SIZE_MAX); + WARNF( + "Cannot find 'libAFLDriver.a' to replace '-fsanitize=fuzzer' in " + "the flags - this will fail!"); - } + } - if (getenv("AFL_LLVM_CTX_K")) { + } else { - ctx_k = atoi(getenv("AFL_LLVM_CTX_K")); - if (ctx_k < 1 || ctx_k > CTX_MAX_K) - FATAL("K-CTX instrumentation mode must be between 1 and CTX_MAX_K (%u)", - CTX_MAX_K); - if (ctx_k == 1) { + insert_param(aflcc, afllib); - setenv("AFL_LLVM_CALLER", "1", 1); - unsetenv("AFL_LLVM_CTX_K"); - instrument_opt_mode |= INSTRUMENT_OPT_CALLER; +#ifdef __APPLE__ + insert_param(aflcc, "-Wl,-undefined"); + insert_param(aflcc, "dynamic_lookup"); +#endif - } else { + } - instrument_opt_mode |= INSTRUMENT_OPT_CTX_K; +} - } +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 (getenv("AFL_LLVM_INSTRUMENT")) { + if (aflcc->compiler_mode != GCC_PLUGIN && aflcc->compiler_mode != GCC && + !getenv("AFL_LLVM_NO_RPATH")) { - u8 *ptr2 = strtok(getenv("AFL_LLVM_INSTRUMENT"), ":,;"); + // 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)) { - while (ptr2) { + u8 *libdir_opt = strdup("-Wl,-rpath=" LLVM_LIBDIR); + insert_param(aflcc, libdir_opt); - if (strncasecmp(ptr2, "afl", strlen("afl")) == 0 || - strncasecmp(ptr2, "classic", strlen("classic")) == 0) { + } - if (instrument_mode == INSTRUMENT_LTO) { + } - instrument_mode = INSTRUMENT_CLASSIC; - lto_mode = 1; +#ifndef __ANDROID__ - } else if (!instrument_mode || instrument_mode == INSTRUMENT_AFL) { + #define M32_ERR_MSG "-m32 is not supported by your compiler" + #define M64_ERR_MSG "-m64 is not supported by your compiler" - instrument_mode = INSTRUMENT_AFL; + if (aflcc->compiler_mode != GCC && aflcc->compiler_mode != CLANG) { - } else { + switch (aflcc->bit_mode) { - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_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 (strncasecmp(ptr2, "pc-guard", strlen("pc-guard")) == 0 || - strncasecmp(ptr2, "pcguard", strlen("pcguard")) == 0) { + } - if (!instrument_mode || instrument_mode == INSTRUMENT_PCGUARD) - instrument_mode = INSTRUMENT_PCGUARD; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); + #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) { - if (strncasecmp(ptr2, "llvmnative", strlen("llvmnative")) == 0 || - strncasecmp(ptr2, "llvm-native", strlen("llvm-native")) == 0 || - strncasecmp(ptr2, "native", strlen("native")) == 0) { + 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"); - if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) - instrument_mode = INSTRUMENT_LLVMNATIVE; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); + } - } + #endif - if (strncasecmp(ptr2, "llvmcodecov", strlen("llvmcodecov")) == 0 || - strncasecmp(ptr2, "llvm-codecov", strlen("llvm-codecov")) == 0) { + } - if (!instrument_mode || instrument_mode == INSTRUMENT_LLVMNATIVE) { +#endif - instrument_mode = INSTRUMENT_LLVMNATIVE; - instrument_opt_mode |= INSTRUMENT_OPT_CODECOV; + add_aflpplib(aflcc); - } else { +#if defined(USEMMAP) && !defined(__HAIKU__) && !__APPLE__ + insert_param(aflcc, "-Wl,-lrt"); +#endif - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); +} - } +/* Misc */ - } +void add_assembler(aflcc_state_t *aflcc) { - if (strncasecmp(ptr2, "cfg", strlen("cfg")) == 0 || - strncasecmp(ptr2, "instrim", strlen("instrim")) == 0) { + u8 *afl_as = find_object(aflcc, "as"); - FATAL( - "InsTrim instrumentation was removed. Use a modern LLVM and " - "PCGUARD (default in afl-cc).\n"); + if (!afl_as) FATAL("Cannot find 'as' (symlink to 'afl-as')."); - } + u8 *slash = strrchr(afl_as, '/'); + if (slash) *slash = 0; - if (strncasecmp(ptr2, "lto", strlen("lto")) == 0) { + insert_param(aflcc, "-B"); + insert_param(aflcc, afl_as); - lto_mode = 1; - if (!instrument_mode || instrument_mode == INSTRUMENT_LTO) - instrument_mode = INSTRUMENT_LTO; - else - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); + if (aflcc->compiler_mode == CLANG) insert_param(aflcc, "-no-integrated-as"); - } +} - if (strcasecmp(ptr2, "gcc") == 0) { +void add_gcc_plugin(aflcc_state_t *aflcc) { - if (!instrument_mode || instrument_mode == INSTRUMENT_GCC) - instrument_mode = INSTRUMENT_GCC; - else if (instrument_mode != INSTRUMENT_GCC) - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - compiler_mode = GCC; + 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); - if (strcasecmp(ptr2, "clang") == 0) { + } - if (!instrument_mode || instrument_mode == INSTRUMENT_CLANG) - instrument_mode = INSTRUMENT_CLANG; - else if (instrument_mode != INSTRUMENT_CLANG) - FATAL("main instrumentation mode already set with %s", - instrument_mode_string[instrument_mode]); - compiler_mode = CLANG; + insert_object(aflcc, "afl-gcc-pass.so", "-fplugin=%s", 0); - } + insert_param(aflcc, "-fno-if-conversion"); + insert_param(aflcc, "-fno-if-conversion2"); - 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++; +void add_misc_params(aflcc_state_t *aflcc) { - if (!*ptr3) { + if (getenv("AFL_NO_BUILTIN") || getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES") || + getenv("AFL_LLVM_LAF_ALL") || getenv("AFL_LLVM_CMPLOG") || + aflcc->lto_mode) { - 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"); + 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"); - } + } - ctx_k = atoi(ptr3); - if (ctx_k < 1 || ctx_k > CTX_MAX_K) - FATAL( - "K-CTX instrumentation option must be between 1 and CTX_MAX_K " - "(%u)", - CTX_MAX_K); + if (!aflcc->have_pic) { insert_param(aflcc, "-fPIC"); } - if (ctx_k == 1) { + if (getenv("AFL_HARDEN")) { - instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - setenv("AFL_LLVM_CALLER", "1", 1); - unsetenv("AFL_LLVM_CTX_K"); + insert_param(aflcc, "-fstack-protector-all"); - } else { + if (!aflcc->fortify_set) add_defs_fortify(aflcc, 2); - instrument_opt_mode |= (INSTRUMENT_OPT_CTX_K); - u8 *ptr4 = alloc_printf("%u", ctx_k); - setenv("AFL_LLVM_CTX_K", ptr4, 1); + } - } + 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 (strcasecmp(ptr2, "ctx") == 0) { + } - instrument_opt_mode |= INSTRUMENT_OPT_CTX; - setenv("AFL_LLVM_CTX", "1", 1); + if (aflcc->x_set) { - } + insert_param(aflcc, "-x"); + insert_param(aflcc, "none"); - if (strncasecmp(ptr2, "caller", strlen("caller")) == 0) { + } - instrument_opt_mode |= INSTRUMENT_OPT_CALLER; - setenv("AFL_LLVM_CALLER", "1", 1); +} - } +param_st parse_misc_params(aflcc_state_t *aflcc, u8 *cur_argv, u8 scan) { - if (strncasecmp(ptr2, "ngram", strlen("ngram")) == 0) { + param_st final_ = PARAM_MISS; - u8 *ptr3 = ptr2 + strlen("ngram"); - while (*ptr3 && (*ptr3 < '0' || *ptr3 > '9')) - ptr3++; +// MACRO START +#define SCAN_KEEP(dst, src) \ + do { \ + \ + if (scan) { \ + \ + dst = src; \ + final_ = PARAM_SCAN; \ + \ + } else { \ + \ + final_ = PARAM_KEEP; \ + \ + } \ + \ + } while (0) - if (!*ptr3) { + // MACRO END - 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"); + if (!strncasecmp(cur_argv, "-fpic", 5)) { - } + SCAN_KEEP(aflcc->have_pic, 1); - ngram_size = atoi(ptr3); - if (ngram_size < 2 || ngram_size > NGRAM_SIZE_MAX) - FATAL( - "NGRAM instrumentation option must be between 2 and " - "NGRAM_SIZE_MAX (%u)", - NGRAM_SIZE_MAX); - instrument_opt_mode |= (INSTRUMENT_OPT_NGRAM); - u8 *ptr4 = alloc_printf("%u", ngram_size); - setenv("AFL_LLVM_NGRAM_SIZE", ptr4, 1); + } else if (!strcmp(cur_argv, "-m32") || - } + !strcmp(cur_argv, "armv7a-linux-androideabi")) { - ptr2 = strtok(NULL, ":,;"); + SCAN_KEEP(aflcc->bit_mode, 32); - } + } else if (!strcmp(cur_argv, "-m64")) { - } + SCAN_KEEP(aflcc->bit_mode, 64); - if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && - (instrument_opt_mode & INSTRUMENT_OPT_CALLER)) { + } else if (strstr(cur_argv, "FORTIFY_SOURCE")) { - FATAL("you cannot set CTX and CALLER together"); + SCAN_KEEP(aflcc->fortify_set, 1); - } + } else if (!strcmp(cur_argv, "-x")) { - if ((instrument_opt_mode & INSTRUMENT_OPT_CTX) && - (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + SCAN_KEEP(aflcc->x_set, 1); - FATAL("you cannot set CTX and K-CTX together"); + } else if (!strcmp(cur_argv, "-E")) { - } + SCAN_KEEP(aflcc->preprocessor_only, 1); - if ((instrument_opt_mode & INSTRUMENT_OPT_CALLER) && - (instrument_opt_mode & INSTRUMENT_OPT_CTX_K)) { + } else if (!strcmp(cur_argv, "--target=wasm32-wasi")) { - FATAL("you cannot set CALLER and K-CTX together"); + SCAN_KEEP(aflcc->passthrough, 1); - } + } else if (!strcmp(cur_argv, "-c")) { - if (instrument_opt_mode && instrument_mode == INSTRUMENT_DEFAULT && - (compiler_mode == LLVM || compiler_mode == UNSET)) { + SCAN_KEEP(aflcc->have_c, 1); - instrument_mode = INSTRUMENT_CLASSIC; - compiler_mode = LLVM; + } else if (!strncmp(cur_argv, "-O", 2)) { - } + SCAN_KEEP(aflcc->have_o, 1); - if (!compiler_mode) { + } else if (!strncmp(cur_argv, "-funroll-loop", 13)) { - // 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 (have_llvm) - compiler_mode = LLVM; - else if (have_gcc_plugin) - compiler_mode = GCC_PLUGIN; - else if (have_gcc) -#ifdef __APPLE__ - // on OSX clang masquerades as GCC - compiler_mode = CLANG; -#else - compiler_mode = GCC; -#endif - else if (have_lto) - compiler_mode = LTO; + SCAN_KEEP(aflcc->have_unroll, 1); + + } else if (!strncmp(cur_argv, "--afl", 5)) { + + if (scan) + final_ = PARAM_SCAN; else - FATAL("no compiler mode available"); + final_ = PARAM_DROP; - } + } else if (!strncmp(cur_argv, "-fno-unroll", 11)) { - /* if our PCGUARD implementation is not available then silently switch to - native LLVM PCGUARD */ - if (compiler_mode == CLANG && - (instrument_mode == INSTRUMENT_DEFAULT || - instrument_mode == INSTRUMENT_PCGUARD) && - find_object("SanitizerCoveragePCGUARD.so", argv[0]) == NULL) { + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; - instrument_mode = INSTRUMENT_LLVMNATIVE; + } else if (!strcmp(cur_argv, "-pipe") && aflcc->compiler_mode == GCC_PLUGIN) { - } + if (scan) + final_ = PARAM_SCAN; + else + final_ = PARAM_DROP; - if (compiler_mode == GCC) { + } else if (!strncmp(cur_argv, "-stdlib=", 8) && - if (clang_mode) { + (aflcc->compiler_mode == GCC || + aflcc->compiler_mode == GCC_PLUGIN)) { - instrument_mode = INSTRUMENT_CLANG; + if (scan) { + + final_ = PARAM_SCAN; } else { - instrument_mode = INSTRUMENT_GCC; + if (!be_quiet) WARNF("Found '%s' - stripping!", cur_argv); + final_ = PARAM_DROP; } - } + } else if (cur_argv[0] != '-') { - if (compiler_mode == CLANG) { + /* It's a weak, loose pattern, with very different purpose + than others. We handle it at last, cautiously and robustly. */ - instrument_mode = INSTRUMENT_CLANG; - setenv(CLANG_ENV_VAR, "1", 1); // used by afl-as + 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_; + +} + +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 @@ -2168,16 +2559,22 @@ int main(int argc, char **argv, char **envp) { " [GCC/CLANG] simple gcc/clang: %s%s\n" " CLASSIC DEFAULT no no no no no " "no\n\n", - have_llvm ? "AVAILABLE" : "unavailable!", - compiler_mode == LLVM ? " [SELECTED]" : "", - have_llvm ? "AVAILABLE" : "unavailable!", - have_llvm ? "AVAILABLE" : "unavailable!", - have_lto ? "AVAILABLE" : "unavailable!", - compiler_mode == LTO ? " [SELECTED]" : "", - have_gcc_plugin ? "AVAILABLE" : "unavailable!", - compiler_mode == GCC_PLUGIN ? " [SELECTED]" : "", - have_gcc ? "AVAILABLE" : "unavailable!", - (compiler_mode == GCC || compiler_mode == CLANG) ? " [SELECTED]" : ""); + 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" @@ -2266,7 +2663,7 @@ int main(int argc, char **argv, char **envp) { " AFL_USE_TSAN: activate thread sanitizer\n" " AFL_USE_LSAN: activate leak-checker sanitizer\n"); - if (have_gcc_plugin) + if (aflcc->have_gcc_plugin) SAYF( "\nGCC Plugin-specific environment variables:\n" " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" @@ -2282,7 +2679,7 @@ int main(int argc, char **argv, char **envp) { #define COUNTER_BEHAVIOUR \ " AFL_LLVM_NOT_ZERO: use cycling trace counters that skip zero\n" #endif - if (have_llvm) + if (aflcc->have_llvm) SAYF( "\nLLVM/LTO/afl-clang-fast/afl-clang-lto specific environment " "variables:\n" @@ -2310,7 +2707,7 @@ int main(int argc, char **argv, char **envp) { "instrument allow/\n" " deny listing (selective instrumentation)\n"); - if (have_llvm) + if (aflcc->have_llvm) SAYF( " AFL_LLVM_CMPLOG: log operands of comparisons (RedQueen " "mutator)\n" @@ -2329,7 +2726,7 @@ int main(int argc, char **argv, char **envp) { "locations\n"); #ifdef AFL_CLANG_FLTO - if (have_lto) + if (aflcc->have_lto) SAYF( "\nLTO/afl-clang-lto specific environment variables:\n" " AFL_LLVM_MAP_ADDR: use a fixed coverage map address (speed), " @@ -2365,9 +2762,9 @@ int main(int argc, char **argv, char **envp) { "targets.\n\n"); #if (LLVM_MAJOR >= 3) - if (have_lto) + if (aflcc->have_lto) SAYF("afl-cc LTO with ld=%s %s\n", AFL_REAL_LD, AFL_CLANG_FLTO); - if (have_llvm) + if (aflcc->have_llvm) SAYF("afl-cc LLVM version %d using the binary path \"%s\".\n", LLVM_MAJOR, LLVM_BINDIR); #endif @@ -2406,205 +2803,356 @@ int main(int argc, char **argv, char **envp) { } - if (compiler_mode == LTO) { - - if (instrument_mode == 0 || instrument_mode == INSTRUMENT_LTO || - instrument_mode == INSTRUMENT_CFG || - instrument_mode == INSTRUMENT_PCGUARD) { +} - lto_mode = 1; - // force CFG - // if (!instrument_mode) { +static void process_params(aflcc_state_t *aflcc, u8 scan, u32 argc, + char **argv) { - instrument_mode = INSTRUMENT_PCGUARD; - // ptr = instrument_mode_string[instrument_mode]; - // } + limit_params(aflcc, argc); - } else if (instrument_mode == INSTRUMENT_CLASSIC) { + // for (u32 x = 0; x < argc; ++x) fprintf(stderr, "[%u] %s\n", x, argv[x]); - lto_mode = 1; + /* Process the argument list. */ - } else { + u8 skip_next = 0; + while (--argc) { - if (!be_quiet) { + u8 *cur = *(++argv); - WARNF("afl-clang-lto called with mode %s, using that mode instead", - instrument_mode_string[instrument_mode]); + if (skip_next > 0) { - } + skip_next--; + continue; } - } + if (PARAM_MISS != parse_misc_params(aflcc, cur, scan)) continue; - if (instrument_mode == 0 && compiler_mode < GCC_PLUGIN) { + if (PARAM_MISS != parse_fsanitize(aflcc, cur, scan)) continue; -#if LLVM_MAJOR >= 7 - #if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) - if (have_instr_env) { + if (PARAM_MISS != parse_linking_params(aflcc, cur, scan, &skip_next, argv)) + continue; - instrument_mode = INSTRUMENT_AFL; - if (!be_quiet) { + if (*cur == '@') { - WARNF( - "Switching to classic instrumentation because " - "AFL_LLVM_ALLOWLIST/DENYLIST does not work with PCGUARD < 10.0.1."); + // response file support. + // 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. + // We do *not* support quotes in the rsp files to cope with spaces in + // filenames etc! If you need that then send a patch! + u8 *filename = cur + 1; + if (aflcc->debug) { DEBUGF("response file=%s\n", filename); } + FILE *f = fopen(filename, "r"); + struct stat st; + + // Check not found or empty? let the compiler complain if so. + if (!f || fstat(fileno(f), &st) < 0 || st.st_size < 1) { + + if (!scan) insert_param(aflcc, cur); + continue; } - } else + u8 *tmpbuf = malloc(st.st_size + 2), *ptr; + char **args = malloc(sizeof(char *) * (st.st_size >> 1)); + int count = 1, cont = 0, cont_act = 0; - #endif - instrument_mode = INSTRUMENT_PCGUARD; + while (fgets(tmpbuf, st.st_size + 1, f)) { -#else - instrument_mode = INSTRUMENT_AFL; -#endif + ptr = tmpbuf; + // fprintf(stderr, "1: %s\n", ptr); + // no leading whitespace + while (isspace(*ptr)) { - } + ++ptr; + cont_act = 0; - if (instrument_opt_mode && compiler_mode != LLVM) - FATAL("CTX, CALLER and NGRAM can only be used in LLVM mode"); + } - if (!instrument_opt_mode) { + // no comments, no empty lines + if (*ptr == '#' || *ptr == '\n' || !*ptr) { continue; } + // remove LF + if (ptr[strlen(ptr) - 1] == '\n') { ptr[strlen(ptr) - 1] = 0; } + // remove CR + if (*ptr && ptr[strlen(ptr) - 1] == '\r') { ptr[strlen(ptr) - 1] = 0; } + // handle \ at end of line + if (*ptr && ptr[strlen(ptr) - 1] == '\\') { - if (lto_mode && instrument_mode == INSTRUMENT_CFG) - instrument_mode = INSTRUMENT_PCGUARD; - ptr = instrument_mode_string[instrument_mode]; + cont = 1; + ptr[strlen(ptr) - 1] = 0; - } else { + } - char *ptr2 = alloc_printf(" + NGRAM-%u", ngram_size); - char *ptr3 = alloc_printf(" + K-CTX-%u", ctx_k); + // fprintf(stderr, "2: %s\n", ptr); - ptr = alloc_printf( - "%s%s%s%s%s", instrument_mode_string[instrument_mode], - (instrument_opt_mode & INSTRUMENT_OPT_CTX) ? " + CTX" : "", - (instrument_opt_mode & INSTRUMENT_OPT_CALLER) ? " + CALLER" : "", - (instrument_opt_mode & INSTRUMENT_OPT_NGRAM) ? ptr2 : "", - (instrument_opt_mode & INSTRUMENT_OPT_CTX_K) ? ptr3 : ""); + // remove whitespace at end + while (*ptr && isspace(ptr[strlen(ptr) - 1])) { - ck_free(ptr2); - ck_free(ptr3); + ptr[strlen(ptr) - 1] = 0; + cont = 0; - } + } -#ifndef AFL_CLANG_FLTO - if (lto_mode) - FATAL( - "instrumentation mode LTO specified but LLVM support not available " - "(requires LLVM 11 or higher)"); -#endif + // fprintf(stderr, "3: %s\n", ptr); + if (*ptr) { - if (instrument_opt_mode && instrument_opt_mode != INSTRUMENT_OPT_CODECOV && - instrument_mode != INSTRUMENT_CLASSIC) - FATAL( - "CALLER, CTX and NGRAM instrumentation options can only be used with " - "the LLVM CLASSIC instrumentation mode."); + do { - 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"); + u8 *value = ptr; + while (*ptr && !isspace(*ptr)) { -#if LLVM_MAJOR < 11 && (LLVM_MAJOR < 10 || LLVM_MINOR < 1) - if (instrument_mode == INSTRUMENT_PCGUARD && have_instr_env) { + ++ptr; - FATAL( - "Instrumentation type PCGUARD does not support " - "AFL_LLVM_ALLOWLIST/DENYLIST! Use LLVM 10.0.1+ instead."); + } - } + while (*ptr && isspace(*ptr)) { -#endif + *ptr++ = 0; - u8 *ptr2; + } - if ((ptr2 = getenv("AFL_LLVM_DICT2FILE")) != NULL && *ptr2 != '/') - FATAL("AFL_LLVM_DICT2FILE must be set to an absolute file path"); + if (cont_act) { - if ((isatty(2) && !be_quiet) || debug) { + u32 len = strlen(args[count - 1]) + strlen(value) + 1; + u8 *tmp = malloc(len); + snprintf(tmp, len, "%s%s", args[count - 1], value); + free(args[count - 1]); + args[count - 1] = tmp; + cont_act = 0; - SAYF(cCYA - "afl-cc" VERSION cRST - " by Michal Zalewski, Laszlo Szekeres, Marc Heuse - mode: %s-%s\n", - compiler_mode_string[compiler_mode], ptr); + } else { - } + args[count++] = strdup(value); - if (!be_quiet && (compiler_mode == GCC || 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!"); + } while (*ptr); + + } + + if (cont) { + + cont_act = 1; + cont = 0; + + } + + } + + if (count) { process_params(aflcc, scan, count, args); } + + // we cannot free args[] unless we don't need + // to keep any reference in cc_params + if (scan) { + + if (count) do { + + free(args[--count]); + + } while (count); + + free(args); + + } + + free(tmpbuf); + + continue; + + } + + if (!scan) insert_param(aflcc, cur); } - if (debug) { +} - DEBUGF("cd '%s';", getthecwd()); - for (i = 0; i < argc; i++) - SAYF(" '%s'", argv[i]); - SAYF("\n"); - fflush(stdout); - fflush(stderr); +/* Copy argv to cc_params, making the necessary edits. */ + +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 (getenv("AFL_LLVM_LAF_ALL")) { + if (aflcc->compiler_mode == GCC || aflcc->compiler_mode == CLANG) { - 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); + add_assembler(aflcc); } - cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || - getenv("AFL_GCC_CMPLOG"); + if (aflcc->compiler_mode == GCC_PLUGIN) { add_gcc_plugin(aflcc); } -#if !defined(__ANDROID__) && !defined(ANDROID) - ptr = find_object("afl-compiler-rt.o", argv[0]); + if (aflcc->compiler_mode == LLVM || aflcc->compiler_mode == LTO) { - if (!ptr) { + if (aflcc->lto_mode && aflcc->have_instr_env) { - FATAL( - "Unable to find 'afl-compiler-rt.o'. Please set the AFL_PATH " - "environment variable."); + load_llvm_pass(aflcc, "afl-llvm-lto-instrumentlist.so"); - } + } - if (debug) { DEBUGF("rt=%s obj_path=%s\n", ptr, obj_path); } + if (getenv("AFL_LLVM_DICT2FILE")) { - ck_free(ptr); -#endif + load_llvm_pass(aflcc, "afl-llvm-dict2file.so"); - edit_params(argc, argv, envp); + } + + // laf + if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) { - if (debug) { + load_llvm_pass(aflcc, "split-switches-pass.so"); - DEBUGF("cd '%s';", getthecwd()); - for (i = 0; i < (s32)cc_par_cnt; i++) - SAYF(" '%s'", cc_params[i]); - SAYF("\n"); - fflush(stdout); - fflush(stderr); + } + + 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"); } - if (passthrough) { + /* 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] = cc_params[0]; - execvp(cc_params[0], (char **)argv); + argv[0] = aflcc->cc_params[0]; + execvp(aflcc->cc_params[0], (char **)argv); } else { - execvp(cc_params[0], (char **)cc_params); + execvp(aflcc->cc_params[0], (char **)aflcc->cc_params); } - FATAL("Oops, failed to execute '%s' - check your PATH", cc_params[0]); + FATAL("Oops, failed to execute '%s' - check your PATH", aflcc->cc_params[0]); return 0; diff --git a/src/afl-fuzz-run.c b/src/afl-fuzz-run.c index 34a5ff81..1ee8ebe7 100644 --- a/src/afl-fuzz-run.c +++ b/src/afl-fuzz-run.c @@ -169,20 +169,16 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { } - if (unlikely(afl->custom_mutators_count)) { - - LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - - if (el->afl_custom_fuzz_send) { + LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - el->afl_custom_fuzz_send(el->data, *mem, new_size); - sent = 1; + if (el->afl_custom_fuzz_send) { - } + el->afl_custom_fuzz_send(el->data, *mem, new_size); + sent = 1; - }); + } - } + }); if (likely(!sent)) { @@ -203,7 +199,7 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { } - } else { + } else { /* !afl->custom_mutators_count */ if (unlikely(len < afl->min_length && !fix)) { @@ -215,27 +211,8 @@ write_to_testcase(afl_state_t *afl, void **mem, u32 len, u32 fix) { } - if (unlikely(afl->custom_mutators_count)) { - - LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { - - if (el->afl_custom_fuzz_send) { - - el->afl_custom_fuzz_send(el->data, *mem, len); - sent = 1; - - } - - }); - - } - - if (likely(!sent)) { - - /* boring uncustom. */ - afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len); - - } + /* boring uncustom. */ + afl_fsrv_write_to_testcase(&afl->fsrv, *mem, len); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 17949fd7..2d5787e8 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -1812,6 +1812,10 @@ int main(int argc, char **argv_orig, char **envp) { check_cpu_governor(afl); #endif + #ifdef __APPLE__ + setenv("DYLD_NO_PIE", "1", 0); + #endif + if (getenv("LD_PRELOAD")) { WARNF( |