diff options
38 files changed, 1536 insertions, 1211 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ca76f2d..04cbaca8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: install - run: brew install make gcc + run: brew install make gcc llvm - name: fix install run: cd /usr/local/bin; ln -s gcc-11 gcc; ln -s g++-11 g++; which gcc; gcc -v - name: build diff --git a/.gitignore b/.gitignore index 22ee6bf1..8b0f0a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,4 @@ utils/persistent_mode/persistent_demo_new utils/persistent_mode/test-instr !coresight_mode !coresight_mode/coresight-trace +vuln_prog \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a82fffdc..f1b2fc01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ # # This Dockerfile for AFLplusplus uses Ubuntu 22.04 jammy and -# installs LLVM 14 for afl-clang-lto support :-) +# installs LLVM 14 for afl-clang-lto support. +# +# GCC 11 is used instead of 12 because genhtml for afl-cov doesn't like it. # FROM ubuntu:22.04 AS aflplusplus @@ -17,7 +19,7 @@ RUN apt-get update && apt-get full-upgrade -y && \ rm -rf /var/lib/apt/lists/* ENV LLVM_VERSION=14 -ENV GCC_VERSION=12 +ENV GCC_VERSION=11 RUN echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg.key] http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list && \ wget -qO /etc/apt/keyrings/llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key @@ -25,7 +27,7 @@ RUN echo "deb [signed-by=/etc/apt/keyrings/llvm-snapshot.gpg.key] http://apt.llv RUN apt-get update && \ apt-get -y install --no-install-recommends \ make cmake automake meson ninja-build bison flex \ - git xz-utils bzip2 wget vim jupp nano bash-completion less \ + git xz-utils bzip2 wget jupp nano bash-completion less vim joe ssh psmisc \ python3 python3-dev python3-setuptools python-is-python3 \ libtool libtool-bin libglib2.0-dev \ apt-utils apt-transport-https gnupg dialog \ @@ -62,9 +64,6 @@ ENV AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 RUN git clone --depth=1 https://github.com/vanhauser-thc/afl-cov && \ (cd afl-cov && make install) && rm -rf afl-cov -# Until gcc v12.1 is released for ubuntu https://bugs.launchpad.net/ubuntu/+source/gcc-11/+bug/1940029 -ENV NO_NYX=1 - # Build currently broken ENV NO_CORESIGHT=1 ENV NO_UNICORN_ARM64=1 diff --git a/GNUmakefile.gcc_plugin b/GNUmakefile.gcc_plugin index e21203ae..17bd825d 100644 --- a/GNUmakefile.gcc_plugin +++ b/GNUmakefile.gcc_plugin @@ -100,7 +100,9 @@ ifeq "$(SYS)" "SunOS" endif -PROGS = ./afl-gcc-pass.so ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o +PASSES = ./afl-gcc-pass.so ./afl-gcc-cmplog-pass.so ./afl-gcc-cmptrs-pass.so + +PROGS = $(PASSES) ./afl-compiler-rt.o ./afl-compiler-rt-32.o ./afl-compiler-rt-64.o .PHONY: all all: test_shm test_deps $(PROGS) test_build all_done @@ -141,6 +143,8 @@ afl-common.o: ./src/afl-common.c @printf "[*] Building 64-bit variant of the runtime (-m64)... " @$(CC) $(CFLAGS_SAFE) $(CPPFLAGS) -O3 -Wno-unused-result -m64 -fPIC -c $< -o $@ 2>/dev/null; if [ "$$?" = "0" ]; then echo "success!"; else echo "failed (that's fine)"; fi +$(PASSES): instrumentation/afl-gcc-common.h + ./afl-gcc-pass.so: instrumentation/afl-gcc-pass.so.cc | test_deps $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ ln -sf afl-cc afl-gcc-fast @@ -148,6 +152,12 @@ afl-common.o: ./src/afl-common.c ln -sf afl-cc.8 afl-gcc-fast.8 ln -sf afl-cc.8 afl-g++-fast.8 +./afl-gcc-cmplog-pass.so: instrumentation/afl-gcc-cmplog-pass.so.cc | test_deps + $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ + +./afl-gcc-cmptrs-pass.so: instrumentation/afl-gcc-cmptrs-pass.so.cc | test_deps + $(CXX) $(CXXEFLAGS) $(PLUGIN_FLAGS) -shared $< -o $@ + .PHONY: test_build test_build: $(PROGS) @echo "[*] Testing the CC wrapper and instrumentation output..." @@ -190,6 +200,8 @@ install: all ln -sf afl-c++ $${DESTDIR}$(BIN_PATH)/afl-g++-fast ln -sf afl-compiler-rt.o $${DESTDIR}$(HELPER_PATH)/afl-gcc-rt.o install -m 755 ./afl-gcc-pass.so $${DESTDIR}$(HELPER_PATH) + install -m 755 ./afl-gcc-cmplog-pass.so $${DESTDIR}$(HELPER_PATH) + install -m 755 ./afl-gcc-cmptrs-pass.so $${DESTDIR}$(HELPER_PATH) install -m 644 -T instrumentation/README.gcc_plugin.md $${DESTDIR}$(DOC_PATH)/README.gcc_plugin.md .PHONY: clean diff --git a/README.md b/README.md index 91345d0c..53b2b8d0 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ <img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/master/static/aflpp_bg.svg" alt="AFL++ logo" width="250" heigh="250"> -Release version: [4.00c](https://github.com/AFLplusplus/AFLplusplus/releases) +Release version: [4.01c](https://github.com/AFLplusplus/AFLplusplus/releases) -GitHub version: 4.01a +GitHub version: 4.02a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -53,8 +53,8 @@ To have AFL++ easily available with everything compiled, pull the image directly from the Docker Hub (available for both x86_64 and arm64): ```shell -docker pull docker.io/aflplusplus/aflplusplus:stable -docker run -ti -v /location/of/your/target:/src docker.io/aflplusplus/aflplusplus:stable +docker pull aflplusplus/aflplusplus +docker run -ti -v /location/of/your/target:/src aflplusplus/aflplusplus ``` This image is automatically published when a push to the stable branch happens diff --git a/TODO.md b/TODO.md index 99d2c419..c64c1236 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,7 @@ ## Should + - makefiles should show provide a build summary success/failure - better documentation for custom mutators - better autodetection of shifting runtime timeout values - Update afl->pending_not_fuzzed for MOpt diff --git a/afl-system-config b/afl-system-config index ef343704..f482e4fb 100755 --- a/afl-system-config +++ b/afl-system-config @@ -118,7 +118,7 @@ if [ "$PLATFORM" = "Darwin" ] ; then sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist >/dev/null 2>&1 echo fi - echo It is recommended to disable System Integration Protection for increased performance. + echo It is recommended to disable System Integrity Protection for increased performance. echo DONE=1 fi diff --git a/custom_mutators/gramatron/automaton-parser.c b/custom_mutators/gramatron/automaton-parser.c deleted file mode 100644 index 266f5a07..00000000 --- a/custom_mutators/gramatron/automaton-parser.c +++ /dev/null @@ -1,515 +0,0 @@ -#include "afl-fuzz.h" -#include "automaton-parser.h" - -int free_terminal_arr(any_t placeholder, any_t item) { - - struct terminal_arr *tmp = item; - free(tmp->start); - free(tmp); - return MAP_OK; - -} - -int compare_two_symbols(const void *a, const void *b) { - - char * a_char = *(char **)a; - char * b_char = *(char **)b; - size_t fa = strlen(a_char); - size_t fb = strlen(b_char); - if (fa > fb) - return -1; - else if (fa == fb) - return 0; - else - return 1; - -} - -// TODO: create a map -// key: first character of a symbol, value: a list of symbols that starts with -// key, the list is sorted in descending order of the symbol lengths -map_t create_first_char_to_symbols_hashmap(struct symbols_arr *symbols, - struct symbols_arr *first_chars) { - - map_t char_to_symbols = hashmap_new(); - // TODO: free the allocated map - // sort the symbol_dict in descending order of the symbol lengths - qsort(symbols->symbols_arr, symbols->len, sizeof(char *), - compare_two_symbols); -#ifdef DEBUG - printf("------ print after sort ------\n"); - print_symbols_arr(symbols); -#endif - size_t i; - int r; // response from hashmap get and put - for (i = 0; i < symbols->len; i++) { - - char *symbol_curr = symbols->symbols_arr[i]; - // get first character from symbol_curr - char first_character[2]; - first_character[0] = symbol_curr[0]; - first_character[1] = '\0'; -#ifdef DEBUG - printf("****** Current symbol is %s, its first character is %s ******\n", - symbol_curr, first_character); -#endif - // key would be the first character of symbol_curr - // the value would be an array of chars - struct symbols_arr *associated_symbols; - r = hashmap_get(char_to_symbols, first_character, - (any_t *)&associated_symbols); - if (!r) { - -// append current symbol to existing array -#ifdef DEBUG - printf("****** First character %s is already in hashmap ******\n", - first_character); -#endif - if (!add_element_to_symbols_arr(associated_symbols, symbol_curr, - strlen(symbol_curr) + 1)) { - - free_hashmap(char_to_symbols, &free_array_of_chars); - return NULL; - - } - - } else { - -// start a new symbols_arr -#ifdef DEBUG - printf("****** First character %s is not in hashmap ******\n", - first_character); -#endif - struct symbols_arr *new_associated_symbols = create_array_of_chars(); - strncpy(first_chars->symbols_arr[first_chars->len], first_character, - 2); // 2 because one character plus the NULL byte - add_element_to_symbols_arr(new_associated_symbols, symbol_curr, - strlen(symbol_curr) + 1); - r = hashmap_put(char_to_symbols, - first_chars->symbols_arr[first_chars->len], - new_associated_symbols); - first_chars->len++; -#ifdef DEBUG - if (r) { - - printf("hashmap put failed\n"); - - } else { - - printf("hashmap put succeeded\n"); - - } - -#endif - - } - - } - - printf("****** Testing ******\n"); - struct symbols_arr *tmp_arr; - char str[] = "i"; - int t = hashmap_get(char_to_symbols, str, (any_t *)&tmp_arr); - if (!t) print_symbols_arr(tmp_arr); - return char_to_symbols; - -} - -struct symbols_arr *create_array_of_chars() { - - struct symbols_arr *ret = - (struct symbols_arr *)malloc(sizeof(struct symbols_arr)); - ret->len = 0; - ret->symbols_arr = (char **)malloc(MAX_TERMINAL_NUMS * sizeof(char *)); - size_t i; - for (i = 0; i < MAX_TERMINAL_NUMS; i++) { - - ret->symbols_arr[i] = (char *)calloc(MAX_TERMINAL_LENGTH, sizeof(char)); - - } - - return ret; - -} - -// map a symbol to a list of (state, trigger_idx) -map_t create_pda_hashmap(state *pda, struct symbols_arr *symbols_arr) { - - int state_idx, trigger_idx, - r; // r is the return result for hashmap operation - map_t m = hashmap_new(); - // iterate over pda - for (state_idx = 0; state_idx < numstates; state_idx++) { - -#ifdef DEBUG - printf("------ The state idx is %d ------\n", state_idx); -#endif - if (state_idx == final_state) continue; - state *state_curr = pda + state_idx; - for (trigger_idx = 0; trigger_idx < state_curr->trigger_len; - trigger_idx++) { - -#ifdef DEBUG - printf("------ The trigger idx is %d ------\n", trigger_idx); -#endif - trigger * trigger_curr = state_curr->ptr + trigger_idx; - char * symbol_curr = trigger_curr->term; - size_t symbol_len = trigger_curr->term_len; - struct terminal_arr *terminal_arr_curr; - r = hashmap_get(m, symbol_curr, (any_t *)&terminal_arr_curr); - if (r) { - - // the symbol is not in the map - if (!add_element_to_symbols_arr(symbols_arr, symbol_curr, - symbol_len + 1)) { - - // the number of symbols exceed maximual number - free_hashmap(m, &free_terminal_arr); - return NULL; - - } - -#ifdef DEBUG - printf("Symbol %s is not in map\n", symbol_curr); -#endif - struct terminal_arr *new_terminal_arr = - (struct terminal_arr *)malloc(sizeof(struct terminal_arr)); - new_terminal_arr->start = (struct terminal_meta *)calloc( - numstates, sizeof(struct terminal_meta)); -#ifdef DEBUG - printf("allocate new memory address %p\n", new_terminal_arr->start); -#endif - new_terminal_arr->start->state_name = state_idx; - new_terminal_arr->start->dest = trigger_curr->dest; - new_terminal_arr->start->trigger_idx = trigger_idx; - new_terminal_arr->len = 1; -#ifdef DEBUG - printf("Symbol %s is included in %zu edges\n", symbol_curr, - new_terminal_arr->len); -#endif - r = hashmap_put(m, symbol_curr, new_terminal_arr); -#ifdef DEBUG - if (r) { - - printf("hashmap put failed\n"); - - } else { - - printf("hashmap put succeeded\n"); - - } - -#endif - // if symbol not already in map, it's not in symbol_dict, simply add the - // symbol to the array - // TODO: need to initialize symbol dict (calloc) - - } else { - -// the symbol is already in map -// append to terminal array -// no need to touch start -#ifdef DEBUG - printf("Symbol %s is in map\n", symbol_curr); -#endif - struct terminal_meta *modify = - terminal_arr_curr->start + terminal_arr_curr->len; - modify->state_name = state_idx; - modify->trigger_idx = trigger_idx; - modify->dest = trigger_curr->dest; - terminal_arr_curr->len++; -#ifdef DEBUG - printf("Symbol %s is included in %zu edges\n", symbol_curr, - terminal_arr_curr->len); -#endif - // if symbol already in map, it's already in symbol_dict as well, no - // work needs to be done - - } - - } - - } - - return m; - -} - -void print_symbols_arr(struct symbols_arr *arr) { - - size_t i; - printf("("); - for (i = 0; i < arr->len; i++) { - - printf("%s", arr->symbols_arr[i]); - if (i != arr->len - 1) printf(","); - - } - - printf(")\n"); - -} - -void free_hashmap(map_t m, int (*f)(any_t, any_t)) { - - if (!m) { - - printf("m map is empty\n"); - return; - - } - - int r = hashmap_iterate(m, f, NULL); -#ifdef DEBUG - if (!r) - printf("free hashmap items successfully!\n"); - else - printf("free hashmap items failed"); -#endif - hashmap_free(m); - -} - -int free_array_of_chars(any_t placeholder, any_t item) { - - if (!item) { - - printf("item is empty\n"); - return MAP_MISSING; - - } - - struct symbols_arr *arr = item; - size_t i; - for (i = 0; i < MAX_TERMINAL_NUMS; i++) { - - free(arr->symbols_arr[i]); - - } - - free(arr->symbols_arr); - free(arr); - return MAP_OK; - -} - -void free_pda(state *pda) { - - if (!pda) { - - printf("pda is null\n"); - return; - - } - - size_t i, j; - for (i = 0; i < numstates; i++) { - - state *state_curr = pda + i; - for (j = 0; j < state_curr->trigger_len; j++) { - - trigger *trigger_curr = state_curr->ptr + j; - free(trigger_curr->id); - free(trigger_curr->term); - - } - - free(state_curr->ptr); - - } - - free(pda); - -} - -int dfs(struct terminal_arr **tmp, const char *program, - const size_t program_length, struct terminal_arr **res, size_t idx, - int curr_state) { - - if (*res) return 1; // 1 means successfully found a path - if (idx == program_length) { - - // test if the last terminal points to the final state - if (curr_state != final_state) return 0; - *res = *tmp; - return 1; - - } - - if ((*tmp)->len == MAX_PROGRAM_WALK_LENGTH) { - - printf("Reached maximum program walk length\n"); - return 0; - - } - - char first_char[2]; - first_char[0] = program[idx]; // first character of program - first_char[1] = '\0'; - int r; - struct symbols_arr *matching_symbols; - r = hashmap_get(first_char_to_symbols_map, first_char, - (any_t *)&matching_symbols); - if (r) { - - printf( - "No symbols match the current character, abort!"); // hopefully won't - // reach this state - return 0; - - } - - size_t i; - bool matched = false; - for (i = 0; i < matching_symbols->len; i++) { - - if (matched) break; - char *matching_symbol = matching_symbols->symbols_arr[i]; - if (!strncmp(matching_symbol, program + idx, strlen(matching_symbol))) { - - // there is a match - matched = true; - // find the possible paths of that symbol - struct terminal_arr *ta; - int r2 = hashmap_get(pda_map, matching_symbol, (any_t *)&ta); - if (!r2) { - - // the terminal is found in the dictionary - size_t j; - for (j = 0; j < ta->len; j++) { - - int state_name = (ta->start + j)->state_name; - if (state_name != curr_state) continue; - size_t trigger_idx = (ta->start + j)->trigger_idx; - int dest = (ta->start + j)->dest; - (*tmp)->start[(*tmp)->len].state_name = state_name; - (*tmp)->start[(*tmp)->len].trigger_idx = trigger_idx; - (*tmp)->start[(*tmp)->len].dest = dest; - (*tmp)->len++; - if (dfs(tmp, program, program_length, res, - idx + strlen(matching_symbol), dest)) - return 1; - (*tmp)->len--; - - } - - } else { - - printf("No path goes out of this symbol, abort!"); // hopefully won't - // reach this state - return 0; - - } - - } - - } - - return 0; - /* - 1. First extract the first character of the current program - 2. Match the possible symbols of that program - 3. Find the possible paths of that symbol - 4. Add to temporary terminal array - 5. Recursion - 6. Pop the path from the terminal array - 7. - If idx reaches end of program, set tmp to res - - If idx is not at the end and nothing matches, the current path is not - working, simply return 0 - */ - -} - -Array *constructArray(struct terminal_arr *terminal_arr, state *pda) { - - Array *res = (Array *)calloc(1, sizeof(Array)); - initArray(res, INIT_SIZE); - size_t i; - for (i = 0; i < terminal_arr->len; i++) { - - struct terminal_meta *curr = terminal_arr->start + i; - int state_name = curr->state_name; - int trigger_idx = curr->trigger_idx; - // get the symbol from pda - state * state_curr = pda + state_name; - trigger *trigger_curr = state_curr->ptr + trigger_idx; - char * symbol_curr = trigger_curr->term; - size_t symbol_curr_len = trigger_curr->term_len; - insertArray(res, state_name, symbol_curr, symbol_curr_len, trigger_idx); - - } - - return res; - -} - -Array *automaton_parser(const uint8_t *seed_fn) { - - Array *parsed_res = NULL; - FILE * ptr; - ptr = fopen(seed_fn, "r"); - if (ptr == NULL) { - - printf("file can't be opened \n"); - fclose(ptr); - return NULL; - - } - - char ch; - char program[MAX_PROGRAM_LENGTH]; - int i = 0; - bool program_too_long = false; - do { - - if (i == MAX_PROGRAM_LENGTH) { - - // the maximum program length is reached - printf("maximum program length is reached, give up the current seed\n"); - program_too_long = true; - break; - - } - - ch = fgetc(ptr); - program[i] = ch; - i++; - - } while (ch != EOF); - - program[i - 1] = '\0'; - fclose(ptr); - if ((i == 1 && program[0] == '\0') || program_too_long) return NULL; - struct terminal_arr *arr_holder; - struct terminal_arr *dfs_res = NULL; - arr_holder = (struct terminal_arr *)calloc(1, sizeof(struct terminal_arr)); - arr_holder->start = (struct terminal_meta *)calloc( - MAX_PROGRAM_WALK_LENGTH, sizeof(struct terminal_meta)); - int dfs_success = - dfs(&arr_holder, program, strlen(program), &dfs_res, 0, init_state); - // printf("*** return value %d *** \n", dfs_success); - if (dfs_success) { parsed_res = constructArray(dfs_res, pda); } - free(arr_holder->start); - free(arr_holder); - return parsed_res; - -} - -// return 0 if fails -// return 1 if succeeds -int add_element_to_symbols_arr(struct symbols_arr *symbols_arr, char *symbol, - size_t symbol_len) { - - if (symbols_arr->len >= MAX_TERMINAL_NUMS || - symbol_len >= MAX_TERMINAL_LENGTH) { - - return 0; - - } - - strncpy(symbols_arr->symbols_arr[symbols_arr->len], symbol, symbol_len); - symbols_arr->len++; - return 1; - -} - diff --git a/custom_mutators/gramatron/automaton-parser.h b/custom_mutators/gramatron/automaton-parser.h deleted file mode 100644 index 762415af..00000000 --- a/custom_mutators/gramatron/automaton-parser.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _AUTOMATON_PARSER_H -#define _AUTOMATON_PARSER_H - -#define NUMINPUTS 500 -#define MAX_PROGRAM_LENGTH 20000 -#define MAX_PROGRAM_WALK_LENGTH 5000 -#define MAX_TERMINAL_NUMS 5000 -#define MAX_TERMINAL_LENGTH 1000 -#define MAX_PROGRAM_NAME_LENGTH 5000 - -#include "gramfuzz.h" - -// represents an edge in the FSA -struct terminal_meta { - - int state_name; - int trigger_idx; - int dest; - -}; - -// represents a set of edges -struct terminal_arr { - - struct terminal_meta *start; - size_t len; - -}; - -// essentially a string array -struct symbols_arr { - - char **symbols_arr; - size_t len; - -}; - -struct symbols_arr *symbols; // symbols contain all the symbols in the language -map_t pda_map; // a map that maps each symbol in the language to a set of edges -struct symbols_arr - * first_chars; // an array of first characters, only temperary array -map_t first_char_to_symbols_map; // a map that maps each first character to a - // set of symbols (the symbols are sorted in - // descending order) - -// freeing terminal arrays -int free_terminal_arr(any_t placeholder, any_t item); - -// return a map that maps each symbol in the language to a set of edges -// populate symbols_arr with all the symbols in the language -map_t create_pda_hashmap(state *pda, struct symbols_arr *symbols_arr); - -// print the string array -void print_symbols_arr(struct symbols_arr *arr); - -// free hashmap -// the function pointer contains function to free the values in the hashmap -void free_hashmap(map_t m, int (*f)(any_t, any_t)); - -// free string array -int free_array_of_chars(any_t placeholder, any_t item); - -// free the pda -void free_pda(state *pda); - -// create a string array -struct symbols_arr *create_array_of_chars(); - -map_t create_first_char_to_symbols_hashmap(struct symbols_arr *symbols, - struct symbols_arr *first_chars); - -// return the automaton represented by the seed -Array *automaton_parser(const uint8_t *seed_fn); - -int add_element_to_symbols_arr(struct symbols_arr *symbols_arr, char *symbol, - size_t symbol_len); - -#endif - diff --git a/custom_mutators/gramatron/build_gramatron_mutator.sh b/custom_mutators/gramatron/build_gramatron_mutator.sh index 0638e3b2..9952e7f5 100755 --- a/custom_mutators/gramatron/build_gramatron_mutator.sh +++ b/custom_mutators/gramatron/build_gramatron_mutator.sh @@ -125,7 +125,7 @@ else } fi -test -f json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } +test -d json-c/.git || { echo "[-] not checked out, please install git or check your internet connection." ; exit 1 ; } echo "[+] Got json-c." test -e json-c/.libs/libjson-c.a || { @@ -144,6 +144,6 @@ echo echo echo "[+] Json-c successfully prepared!" echo "[+] Builing gramatron now." -$CC -O3 -g -fPIC -Wno-unused-result -Wl,--allow-multiple-definition -I../../include -o gramatron.so -shared -I. -I/prg/dev/include gramfuzz.c gramfuzz-helpers.c gramfuzz-mutators.c gramfuzz-util.c hashmap.c automaton-parser.c ../../src/afl-performance.o json-c/.libs/libjson-c.a || exit 1 +$CC -O3 -g -fPIC -Wno-unused-result -Wl,--allow-multiple-definition -I../../include -o gramatron.so -shared -I. -I/prg/dev/include gramfuzz.c gramfuzz-helpers.c gramfuzz-mutators.c gramfuzz-util.c hashmap.c ../../src/afl-performance.o json-c/.libs/libjson-c.a || exit 1 echo echo "[+] gramatron successfully built!" diff --git a/custom_mutators/gramatron/gramfuzz-mutators.c b/custom_mutators/gramatron/gramfuzz-mutators.c index 789a36fd..0fc9c307 100644 --- a/custom_mutators/gramatron/gramfuzz-mutators.c +++ b/custom_mutators/gramatron/gramfuzz-mutators.c @@ -58,8 +58,7 @@ Array *performSpliceOne(Array *originput, IdxMap_new *statemap_orig, int length = utarray_len(stateptr); if (length) { - int *splice_idx = - (int *)utarray_eltptr(stateptr, rand_below(global_afl, length)); + int *splice_idx = (int *)utarray_eltptr(stateptr, rand_below(global_afl, length)); ip.orig_idx = *splice_idx; ip.splice_idx = x; utarray_push_back(pairs, &ip); diff --git a/custom_mutators/gramatron/gramfuzz.c b/custom_mutators/gramatron/gramfuzz.c index f25dfead..9c9dbb43 100644 --- a/custom_mutators/gramatron/gramfuzz.c +++ b/custom_mutators/gramatron/gramfuzz.c @@ -9,7 +9,6 @@ #include "afl-fuzz.h" #include "gramfuzz.h" -#include "automaton-parser.h" #define MUTATORS 4 // Specify the total number of mutators @@ -164,12 +163,6 @@ my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) { if (automaton_file) { pda = create_pda(automaton_file); - symbols = create_array_of_chars(); - pda_map = create_pda_hashmap((struct state *)pda, symbols); - print_symbols_arr(symbols); - first_chars = create_array_of_chars(); - first_char_to_symbols_map = - create_first_char_to_symbols_hashmap(symbols, first_chars); } else { @@ -289,27 +282,11 @@ u8 afl_custom_queue_new_entry(my_mutator_t * data, if (filename_orig_queue) { - if (data->mutated_walk) { - - write_input(data->mutated_walk, automaton_fn); - - } else { - - Array *parsed_walk = automaton_parser(filename_new_queue); - if (!parsed_walk) PFATAL("Parser unsuccessful on %s", filename_new_queue); - write_input(parsed_walk, automaton_fn); - free(parsed_walk->start); - free(parsed_walk); - - } + write_input(data->mutated_walk, automaton_fn); } else { - // TODO: try to parse the input seeds here, if they can be parsed, then - // generate the corresponding automaton file if not, then generate a new - // input - new_input = automaton_parser(filename_new_queue); - if (new_input == NULL) { new_input = gen_input(pda, NULL); } + new_input = gen_input(pda, NULL); write_input(new_input, automaton_fn); // Update the placeholder file @@ -350,21 +327,7 @@ u8 afl_custom_queue_new_entry(my_mutator_t * data, uint8_t afl_custom_queue_get(my_mutator_t *data, const uint8_t *filename) { // get the filename - u8 *automaton_fn = alloc_printf("%s.aut", filename); - // find the automaton file, if the automaton file cannot be found, do not fuzz - // the current entry on the queue - FILE *fp; - fp = fopen(automaton_fn, "rb"); - if (fp == NULL) { - - printf( - "File '%s' does not exist, exiting. Would not fuzz current entry on " - "the queue\n", - automaton_fn); - return 0; - - } - + u8 * automaton_fn = alloc_printf("%s.aut", filename); IdxMap_new *statemap_ptr; terminal * term_ptr; int state; @@ -461,11 +424,6 @@ void afl_custom_deinit(my_mutator_t *data) { free(data->mutator_buf); free(data); - free_hashmap(pda_map, &free_terminal_arr); - free_hashmap(first_char_to_symbols_map, &free_array_of_chars); - free_pda(pda); - free_array_of_chars(NULL, symbols); // free the array of symbols - free_array_of_chars(NULL, first_chars); } diff --git a/custom_mutators/gramatron/hashmap.c b/custom_mutators/gramatron/hashmap.c index db4f9f98..09715b87 100644 --- a/custom_mutators/gramatron/hashmap.c +++ b/custom_mutators/gramatron/hashmap.c @@ -151,7 +151,7 @@ static unsigned long crc32_tab[] = { /* Return a 32-bit CRC of the contents of the buffer. */ -unsigned long custom_crc32(const unsigned char *s, unsigned int len) { +unsigned long crc32(const unsigned char *s, unsigned int len) { unsigned int i; unsigned long crc32val; @@ -172,9 +172,7 @@ unsigned long custom_crc32(const unsigned char *s, unsigned int len) { */ unsigned int hashmap_hash_int(hashmap_map *m, char *keystring) { - unsigned int keystring_len = strlen(keystring); - - unsigned long key = custom_crc32((unsigned char *)(keystring), keystring_len); + unsigned long key = crc32((unsigned char *)(keystring), strlen(keystring)); /* Robert Jenkins' 32 bit Mix Function */ key += (key << 12); diff --git a/custom_mutators/gramatron/testMakefile.mk b/custom_mutators/gramatron/testMakefile.mk deleted file mode 100644 index ff19826b..00000000 --- a/custom_mutators/gramatron/testMakefile.mk +++ /dev/null @@ -1,3 +0,0 @@ -test: test.c - gcc -g -fPIC -Wno-unused-result -Wl,--allow-multiple-definition -I../../include -o test -I. -I/prg/dev/include test.c gramfuzz-helpers.c gramfuzz-mutators.c gramfuzz-util.c hashmap.c ../../src/afl-performance.o json-c/.libs/libjson-c.a - diff --git a/custom_mutators/gramatron/uthash.h b/custom_mutators/gramatron/uthash.h index 93322d5b..05c8abe6 100644 --- a/custom_mutators/gramatron/uthash.h +++ b/custom_mutators/gramatron/uthash.h @@ -127,8 +127,6 @@ typedef unsigned char uint8_t; #if HASH_NONFATAL_OOM /* malloc failures can be recovered from */ - #define IF_HASH_NONFATAL_OOM(x) x - #ifndef uthash_nonfatal_oom #define uthash_nonfatal_oom(obj) \ do { \ @@ -142,6 +140,8 @@ typedef unsigned char uint8_t; (oomed) = 1; \ \ } while (0) +\ + #define IF_HASH_NONFATAL_OOM(x) x #else /* malloc failures result in lost memory, hash tables are unusable */ @@ -156,10 +156,11 @@ typedef unsigned char uint8_t; #endif /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets \ - */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 \ + 5U /* lg2 of initial number of buckets \ + */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhp */ #define ELMT_FROM_HH(tbl, hhp) ((void *)(((char *)(hhp)) - ((tbl)->hho))) @@ -646,7 +647,7 @@ typedef unsigned char uint8_t; HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ \ } while (0) - +\ #define HASH_ADD_STR(head, strfield, add) \ do { \ \ @@ -654,7 +655,7 @@ typedef unsigned char uint8_t; HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ \ } while (0) - +\ #define HASH_REPLACE_STR(head, strfield, add, replaced) \ do { \ \ @@ -662,7 +663,7 @@ typedef unsigned char uint8_t; HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ \ } while (0) - +\ #define HASH_FIND_INT(head, findint, out) \ HASH_FIND(hh, head, findint, sizeof(int), out) #define HASH_ADD_INT(head, intfield, add) \ @@ -682,17 +683,17 @@ typedef unsigned char uint8_t; * isn't defined. */ #ifdef HASH_DEBUG - #define HASH_OOPS(...) \ + #define HASH_OOPS(...) \ + do { \ + \ + fprintf(stderr, __VA_ARGS__); \ + exit(-1); \ + \ + } while (0) +\ + #define HASH_FSCK(hh, head, where) \ do { \ \ - fprintf(stderr, __VA_ARGS__); \ - exit(-1); \ - \ - } while (0) \ - \ - \ - #define HASH_FSCK(hh, head, where) do { \ - \ struct UT_hash_handle *_thh; \ if (head) { \ \ @@ -758,8 +759,7 @@ typedef unsigned char uint8_t; \ } \ \ - } \ - while (0) + } while (0) #else #define HASH_FSCK(hh, head, where) @@ -1352,7 +1352,6 @@ typedef unsigned char uint8_t; \ } else if ((cmpfcn(DECLTYPE(head)( \ \ - \ ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, \ _hs_q)))) <= 0) { \ diff --git a/docs/Changelog.md b/docs/Changelog.md index 44939b16..7284500e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,8 +8,13 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to <afl-users+subscribe@googlegroups.com>. -### Version ++4.01a (dev) - - fix */build_...sh scripts to work outside of git +### Version ++4.02a (dev) + - gcc_plugin: + - Adacore submitted CMPLOG support to the gcc_plugin! :-) + + +### Version ++4.01c (release) + - fixed */build_...sh scripts to work outside of git - new custom_mutator: libafl with token fuzzing :) - afl-fuzz: - when you just want to compile once and set CMPLOG, then just @@ -17,6 +22,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. CMPLOG. - new commandline options -g/G to set min/max length of generated fuzz inputs + - you can set the time for syncing to other fuzzer now with + AFL_SYNC_TIME - reintroduced AFL_PERSISTENT and AFL_DEFER_FORKSRV to allow persistent mode and manual forkserver support if these are not in the target binary (e.g. are in a shared library) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index e29fca96..9036a1f1 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -8,11 +8,11 @@ hence afl-clang-lto is available) or just pull directly from the Docker Hub (for x86_64 and arm64): ```shell -docker pull docker.io/aflplusplus/aflplusplus:stable -docker run -ti -v /location/of/your/target:/src docker.io/aflplusplus/aflplusplus:stable +docker pull aflplusplus/aflplusplus: +docker run -ti -v /location/of/your/target:/src aflplusplus/aflplusplus ``` -This image is automatically generated when a push to the stable repo happens. +This image is automatically generated when a push to the stable branch happens. You will find your target source code in `/src` in the container. Note: you can also pull `aflplusplus/aflplusplus:dev` which is the most current @@ -21,9 +21,12 @@ development state of AFL++. If you want to build AFL++ yourself, you have many options. The easiest choice is to build and install everything: +NOTE: depending on your Debian/Ubuntu/Kali/... version replease `-12` with +whatever llvm version is available! + ```shell sudo apt-get update -sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools +sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev # try to install llvm 12 and install the distro default if that fails sudo apt-get install -y lld-12 llvm-12 llvm-12-dev clang-12 || sudo apt-get install -y lld llvm llvm-dev clang sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev diff --git a/docs/env_variables.md b/docs/env_variables.md index 0598a809..c836a929 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -160,6 +160,8 @@ Available options: Setting `AFL_LLVM_CMPLOG=1` during compilation will tell afl-clang-fast to produce a CmpLog binary. +For afl-gcc-fast set `AFL_GCC_CMPLOG=1` instead. + For more information, see [instrumentation/README.cmplog.md](../instrumentation/README.cmplog.md). diff --git a/docs/features.md b/docs/features.md index dd3d2bcb..212302f8 100644 --- a/docs/features.md +++ b/docs/features.md @@ -12,7 +12,7 @@ QEMU 5.1 with laf-intel and Redqueen, FRIDA mode, unicorn mode, gcc plugin, full | NeverZero [B] | x86[_64] | x(1) | x | x | x | x | | | | Persistent Mode [C] | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | x | | | | LAF-Intel / CompCov [D] | | x | | | x86[_64]/arm[64] | x86[_64]/arm[64] | x86[_64] | | -| CmpLog [E] | | x | | x86[_64]/arm64 | x86[_64]/arm[64] | | | | +| CmpLog [E] | | x | x | x86[_64]/arm64 | x86[_64]/arm[64] | | | | | Selective Instrumentation [F] | | x | x | x | x | | | | | Non-Colliding Coverage [G] | | x(4) | | | (x)(5) | | | | | Ngram prev_loc Coverage [H] | | x(6) | | | | | | | diff --git a/docs/fuzzing_in_depth.md b/docs/fuzzing_in_depth.md index 8963c635..2c27dfe1 100644 --- a/docs/fuzzing_in_depth.md +++ b/docs/fuzzing_in_depth.md @@ -47,7 +47,7 @@ tasks, fuzzing may put a strain on your hardware and on the OS. In particular: example, the following line will run a Docker container with all this preset: ```shell - # docker run -ti --mount type=tmpfs,destination=/ramdisk -e AFL_TMPDIR=/ramdisk docker.io/aflplusplus/aflplusplus:stable + # docker run -ti --mount type=tmpfs,destination=/ramdisk -e AFL_TMPDIR=/ramdisk aflplusplus/aflplusplus ``` ## 1. Instrumenting the target diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c index 098e7269..ec421861 100644 --- a/frida_mode/src/instrument/instrument_coverage.c +++ b/frida_mode/src/instrument/instrument_coverage.c @@ -289,9 +289,9 @@ static void coverage_write_modules(int fd, GArray *coverage_modules) { coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->base_address); coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->limit); /* entry */ - coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0); + coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0UL); /* checksum */ - coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0); + coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0UL); /* timestamp */ coverage_format(fd, "%08" G_GINT32_MODIFIER "X, ", 0); coverage_format(fd, "%s\n", module->path); diff --git a/frida_mode/src/instrument/instrument_debug.c b/frida_mode/src/instrument/instrument_debug.c index 592ab673..d26f9cec 100644 --- a/frida_mode/src/instrument/instrument_debug.c +++ b/frida_mode/src/instrument/instrument_debug.c @@ -66,7 +66,7 @@ static void instrument_disasm(guint8 *start, guint8 *end, instrument_debug("\t0x%" G_GINT64_MODIFIER "x\t* 0x%016" G_GSIZE_MODIFIER "x\n", - curr, *(size_t *)curr); + (uint64_t)curr, *(size_t *)curr); len += sizeof(size_t); continue; diff --git a/include/config.h b/include/config.h index 9fc92b06..0ae6c271 100644 --- a/include/config.h +++ b/include/config.h @@ -26,7 +26,7 @@ /* Version string: */ // c = release, a = volatile github dev, e = experimental branch -#define VERSION "++4.01a" +#define VERSION "++4.02a" /****************************************************** * * diff --git a/instrumentation/README.gcc_plugin.md b/instrumentation/README.gcc_plugin.md index ed39af9d..011a574a 100644 --- a/instrumentation/README.gcc_plugin.md +++ b/instrumentation/README.gcc_plugin.md @@ -99,4 +99,11 @@ See ## 6) Bonus feature #3: selective instrumentation It can be more effective to fuzzing to only instrument parts of the code. For -details, see [README.instrument_list.md](README.instrument_list.md). \ No newline at end of file +details, see [README.instrument_list.md](README.instrument_list.md). + +## 7) Bonus feature #4: CMPLOG + +The gcc_plugin also support CMPLOG/Redqueen, just set `AFL_GCC_CMPLOG` before +instrumenting the target. +Read more about this in the llvm document. + diff --git a/instrumentation/SanitizerCoverageLTO.so.cc b/instrumentation/SanitizerCoverageLTO.so.cc index 9a48ae6d..f976f48a 100644 --- a/instrumentation/SanitizerCoverageLTO.so.cc +++ b/instrumentation/SanitizerCoverageLTO.so.cc @@ -921,7 +921,7 @@ bool ModuleSanitizerCoverageLTO::instrumentModule( std::string outstring; fprintf(stderr, "%s: length %zu/%zu \"", FuncName.c_str(), optLen, thestring.length()); - for (uint8_t i = 0; i < thestring.length(); i++) { + for (uint16_t i = 0; i < (uint16_t)thestring.length(); i++) { uint8_t c = thestring[i]; if (c <= 32 || c >= 127) diff --git a/instrumentation/afl-gcc-cmplog-pass.so.cc b/instrumentation/afl-gcc-cmplog-pass.so.cc new file mode 100644 index 00000000..c2910498 --- /dev/null +++ b/instrumentation/afl-gcc-cmplog-pass.so.cc @@ -0,0 +1,404 @@ +/* GCC plugin for cmplog instrumentation of code for AFL++. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Copyright 2019-2022 AdaCore + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + LLVM CmpLog pass by Andrea Fioraldi <andreafioraldi@gmail.com>, and + on the AFL GCC pass. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "afl-gcc-common.h" + +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; + +namespace { + +static const struct pass_data afl_cmplog_pass_data = { + + .type = GIMPLE_PASS, + .name = "aflcmplog", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il | + TODO_rebuild_cgraph_edges), + +}; + +struct afl_cmplog_pass : afl_base_pass { + + afl_cmplog_pass(bool quiet) + : afl_base_pass(quiet, /*debug=*/false, afl_cmplog_pass_data), + t8u(), + cmplog_hooks() { + + } + + /* An unsigned 8-bit integral type. */ + tree t8u; + + /* Declarations for the various cmplog hook functions, allocated on demand.. + [0] is for __cmplog_ins_hookN, that accepts non-power-of-2 sizes. + [n in 1..5] are for unsigned ints of 2^{n-1} bytes. */ + tree cmplog_hooks[6]; + + tree cmplog_hook(unsigned i) { + + tree t, fnt; + + if (!t8u) { + + if (BITS_PER_UNIT == 8) + t8u = unsigned_char_type_node; + else + t8u = build_nonstandard_integer_type(8, 1); + + } + + if (i <= ARRAY_SIZE(cmplog_hooks) && cmplog_hooks[i]) + return cmplog_hooks[i]; + + switch (i) { + + case 0: +#ifdef uint128_type_node + t = uint128_type_node; +#else + t = build_nonstandard_integer_type(128, 1); +#endif + fnt = + build_function_type_list(void_type_node, t, t, t8u, t8u, NULL_TREE); + t = cmplog_hooks[0] = build_fn_decl("__cmplog_ins_hookN", fnt); + break; + + case 1: + t = t8u; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[1] = build_fn_decl("__cmplog_ins_hook1", fnt); + break; + + case 2: + t = uint16_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[2] = build_fn_decl("__cmplog_ins_hook2", fnt); + break; + + case 3: + t = uint32_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[3] = build_fn_decl("__cmplog_ins_hook4", fnt); + break; + + case 4: + t = uint64_type_node; + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[4] = build_fn_decl("__cmplog_ins_hook8", fnt); + break; + + case 5: +#ifdef uint128_type_node + t = uint128_type_node; +#else + t = build_nonstandard_integer_type(128, 1); +#endif + fnt = build_function_type_list(void_type_node, t, t, t8u, NULL_TREE); + t = cmplog_hooks[5] = build_fn_decl("__cmplog_ins_hook16", fnt); + break; + + default: + gcc_unreachable(); + + } + + /* Mark the newly-created decl as non-throwing, so that we can + insert call within basic blocks. */ + TREE_NOTHROW(t) = 1; + + return t; + + } + + /* Insert a cmplog hook call before GSI for a CODE compare between + LHS and RHS. */ + void insert_cmplog_call(gimple_stmt_iterator gsi, tree_code code, tree lhs, + tree rhs) { + + gcc_checking_assert(TYPE_MAIN_VARIANT(TREE_TYPE(lhs)) == + TYPE_MAIN_VARIANT(TREE_TYPE(rhs))); + + tree fn; + bool pass_n = false; + + /* Obtain the compare operand size as a constant. */ + tree st = TREE_TYPE(lhs); + tree szt = TYPE_SIZE(st); + + if (!tree_fits_uhwi_p(szt)) return; + + unsigned HOST_WIDE_INT sz = tree_to_uhwi(szt); + + /* Round it up. */ + if (sz % 8) sz = (((sz - 1) / 8) + 1) * 8; + + /* Select the hook function to call, based on the size. */ + switch (sz) { + + default: + fn = cmplog_hook(0); + pass_n = true; + break; + + case 8: + fn = cmplog_hook(1); + break; + + case 16: + fn = cmplog_hook(2); + break; + + case 32: + fn = cmplog_hook(3); + break; + + case 64: + fn = cmplog_hook(4); + break; + + case 128: + fn = cmplog_hook(5); + break; + + } + + /* Set attr according to the compare operation. */ + unsigned char attr = 0; + + switch (code) { + + case UNORDERED_EXPR: + case ORDERED_EXPR: + /* ??? */ + /* Fallthrough. */ + case NE_EXPR: + case LTGT_EXPR: + break; + + case EQ_EXPR: + case UNEQ_EXPR: + attr += 1; + break; + + case GT_EXPR: + case UNGT_EXPR: + attr += 2; + break; + + case GE_EXPR: + case UNGE_EXPR: + attr += 3; + break; + + case LT_EXPR: + case UNLT_EXPR: + attr += 4; + break; + + case LE_EXPR: + case UNLE_EXPR: + attr += 5; + break; + + default: + gcc_unreachable(); + + } + + if (FLOAT_TYPE_P(TREE_TYPE(lhs))) { + + attr += 8; + + tree t = build_nonstandard_integer_type(sz, 1); + + tree s = make_ssa_name(t); + gimple *g = gimple_build_assign(s, VIEW_CONVERT_EXPR, + build1(VIEW_CONVERT_EXPR, t, lhs)); + lhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + s = make_ssa_name(t); + g = gimple_build_assign(s, VIEW_CONVERT_EXPR, + build1(VIEW_CONVERT_EXPR, t, rhs)); + rhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + /* Convert the operands to the hook arg type, if needed. */ + tree t = TREE_VALUE(TYPE_ARG_TYPES(TREE_TYPE(fn))); + + lhs = fold_convert_loc(UNKNOWN_LOCATION, t, lhs); + if (!is_gimple_val(lhs)) { + + tree s = make_ssa_name(t); + gimple *g = gimple_build_assign(s, lhs); + lhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + rhs = fold_convert_loc(UNKNOWN_LOCATION, t, rhs); + if (!is_gimple_val(rhs)) { + + tree s = make_ssa_name(t); + gimple *g = gimple_build_assign(s, rhs); + rhs = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + /* Insert the call. */ + tree att = build_int_cst(t8u, attr); + gimple *call; + if (pass_n) + call = gimple_build_call(fn, 4, lhs, rhs, att, + build_int_cst(t8u, sz / 8 - 1)); + else + call = gimple_build_call(fn, 3, lhs, rhs, att); + + gsi_insert_before(&gsi, call, GSI_SAME_STMT); + + } + + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; + + basic_block bb; + FOR_EACH_BB_FN(bb, fn) { + + /* A GIMPLE_COND or GIMPLE_SWITCH will always be the last stmt + in a BB. */ + gimple_stmt_iterator gsi = gsi_last_bb(bb); + if (gsi_end_p(gsi)) continue; + + gimple *stmt = gsi_stmt(gsi); + + if (gimple_code(stmt) == GIMPLE_COND) { + + tree_code code = gimple_cond_code(stmt); + tree lhs = gimple_cond_lhs(stmt); + tree rhs = gimple_cond_rhs(stmt); + + insert_cmplog_call(gsi, code, lhs, rhs); + + } else if (gimple_code(stmt) == GIMPLE_SWITCH) { + + gswitch *sw = as_a<gswitch *>(stmt); + tree lhs = gimple_switch_index(sw); + + for (int i = 0, e = gimple_switch_num_labels(sw); i < e; i++) { + + tree clx = gimple_switch_label(sw, i); + tree rhsl = CASE_LOW(clx); + /* Default case labels exprs don't have a CASE_LOW. */ + if (!rhsl) continue; + tree rhsh = CASE_HIGH(clx); + /* If there is a CASE_HIGH, issue range compares. */ + if (rhsh) { + + insert_cmplog_call(gsi, GE_EXPR, lhs, rhsl); + insert_cmplog_call(gsi, LE_EXPR, lhs, rhsh); + + } + + /* Otherwise, use a single equality compare. */ + else + insert_cmplog_call(gsi, EQ_EXPR, lhs, rhsl); + + } + + } else + + continue; + + } + + return 0; + + } + +}; + +static struct plugin_info afl_cmplog_plugin = { + + .version = "20220420", + .help = G_("AFL gcc cmplog plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +"), + +}; + +} // namespace + +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, " + "is %s"), + gcc_version.basever, version->basever); + + /* Show a banner. */ + bool quiet = false; + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-cmplog-pass " cBRI VERSION cRST + " by <oliva@adacore.com>\n"); + else + quiet = true; + + const char *name = info->base_name; + register_callback(name, PLUGIN_INFO, NULL, &afl_cmplog_plugin); + + afl_cmplog_pass * aflp = new afl_cmplog_pass(quiet); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + + return 0; + +} + diff --git a/instrumentation/afl-gcc-cmptrs-pass.so.cc b/instrumentation/afl-gcc-cmptrs-pass.so.cc new file mode 100644 index 00000000..31679b9b --- /dev/null +++ b/instrumentation/afl-gcc-cmptrs-pass.so.cc @@ -0,0 +1,366 @@ +/* GCC plugin for cmplog routines instrumentation of code for AFL++. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + Copyright 2019-2022 AdaCore + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + LLVM CmpLog Routines pass by Andrea Fioraldi + <andreafioraldi@gmail.com>, and on the AFL GCC CmpLog pass. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "afl-gcc-common.h" + +/* This plugin, being under the same license as GCC, satisfies the + "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY + EXCEPTION, so it can be part of an "Eligible" "Compilation + Process". */ +int plugin_is_GPL_compatible = 1; + +namespace { + +static const struct pass_data afl_cmptrs_pass_data = { + + .type = GIMPLE_PASS, + .name = "aflcmptrs", + .optinfo_flags = OPTGROUP_NONE, + .tv_id = TV_NONE, + .properties_required = 0, + .properties_provided = 0, + .properties_destroyed = 0, + .todo_flags_start = 0, + .todo_flags_finish = (TODO_update_ssa | TODO_cleanup_cfg | TODO_verify_il | + TODO_rebuild_cgraph_edges), + +}; + +struct afl_cmptrs_pass : afl_base_pass { + + afl_cmptrs_pass(bool quiet) + : afl_base_pass(quiet, /*debug=*/false, afl_cmptrs_pass_data), + tp8u(), + cmptrs_hooks() { + + } + + /* A pointer type to a unsigned 8-bit integral type. */ + tree tp8u; + + /* Declarations for the various cmptrs hook functions, allocated on + demand.. [0] is for compares between any pointers, [1] is for + compares between G++ std::string, [2] is for compares between G++ + std::string and GCC C strings, [3] and [4] are analogous to [1] + and [2] but for LLVM C++ strings. */ + tree cmptrs_hooks[5]; + + tree cmptrs_hook(unsigned i) { + + if (!tp8u) { + + tree t8u; + if (BITS_PER_UNIT == 8) + t8u = unsigned_char_type_node; + else + t8u = build_nonstandard_integer_type(8, 1); + tp8u = build_pointer_type(t8u); + + } + + if (i <= ARRAY_SIZE(cmptrs_hooks) && cmptrs_hooks[i]) + return cmptrs_hooks[i]; + + const char *n = NULL; + + switch (i) { + + case 0: + n = "__cmplog_rtn_hook"; + break; + + case 1: + n = "__cmplog_rtn_gcc_stdstring_stdstring"; + break; + + case 2: + n = "__cmplog_rtn_gcc_stdstring_cstring"; + break; + + case 3: + n = "__cmplog_rtn_llvm_stdstring_stdstring"; + break; + + case 4: + n = "__cmplog_rtn_llvm_stdstring_cstring"; + break; + + default: + gcc_unreachable(); + + } + + tree fnt = build_function_type_list(void_type_node, tp8u, tp8u, NULL_TREE); + tree t = cmptrs_hooks[i] = build_fn_decl(n, fnt); + + /* Mark the newly-created decl as non-throwing, so that we can + insert call within basic blocks. */ + TREE_NOTHROW(t) = 1; + + return t; + + } + + /* Return true if T is the char* type. */ + bool is_c_string(tree t) { + + return (POINTER_TYPE_P(t) && + TYPE_MAIN_VARIANT(TREE_TYPE(t)) == char_type_node); + + } + + /* Return true if T is an indirect std::string type. The LLVM pass + tests portions of the mangled name of the callee. We could do + that in GCC too, but computing the mangled name may cause + template instantiations and get symbols defined that could + otherwise be considered unused. We check for compatible layout, + and class, namespace, and field names. These have been unchanged + since at least GCC 7, probably longer, up to GCC 11. Odds are + that, if it were to change in significant ways, mangling would + also change to flag the incompatibility, and we'd have to use a + different hook anyway. */ + bool is_gxx_std_string(tree t) { + + /* We need a pointer or reference type. */ + if (!POINTER_TYPE_P(t)) return false; + + /* Get to the pointed-to type. */ + t = TREE_TYPE(t); + if (!t) return false; + + /* Select the main variant, so that can compare types with pointers. */ + t = TYPE_MAIN_VARIANT(t); + + /* We expect it to be a record type. */ + if (TREE_CODE(t) != RECORD_TYPE) return false; + + /* The type of the template is basic_string. */ + if (strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(t)), "basic_string") != 0) + return false; + + /* It's declared in an internal namespace named __cxx11. */ + tree c = DECL_CONTEXT(TYPE_NAME(t)); + if (!c || TREE_CODE(c) != NAMESPACE_DECL || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "__cxx11") != 0) + return false; + + /* The __cxx11 namespace is a member of namespace std. */ + c = DECL_CONTEXT(c); + if (!c || TREE_CODE(c) != NAMESPACE_DECL || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "std") != 0) + return false; + + /* And the std namespace is in the global namespace. */ + c = DECL_CONTEXT(c); + if (c && TREE_CODE(c) != TRANSLATION_UNIT_DECL) return false; + + /* Check that the first nonstatic data member of the record type + is named _M_dataplus. */ + for (c = TYPE_FIELDS(t); c; c = DECL_CHAIN(c)) + if (TREE_CODE(c) == FIELD_DECL) break; + if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_dataplus") != 0) + return false; + + /* Check that the second nonstatic data member of the record type + is named _M_string_length. */ + tree f2; + for (f2 = DECL_CHAIN(c); f2; f2 = DECL_CHAIN(f2)) + if (TREE_CODE(f2) == FIELD_DECL) break; + if (!f2 /* No need to check this field's offset. */ + || strcmp(IDENTIFIER_POINTER(DECL_NAME(f2)), "_M_string_length") != 0) + return false; + + /* The type of the second data member is size_t. */ + if (!TREE_TYPE(f2) || TYPE_MAIN_VARIANT(TREE_TYPE(f2)) != size_type_node) + return false; + + /* Now go back to the first data member. Its type should be a + record type named _Alloc_hider. */ + c = TREE_TYPE(c); + if (!c || TREE_CODE(c) != RECORD_TYPE || + strcmp(IDENTIFIER_POINTER(TYPE_IDENTIFIER(c)), "_Alloc_hider") != 0) + return false; + + /* And its first data member is named _M_p. */ + for (c = TYPE_FIELDS(c); c; c = DECL_CHAIN(c)) + if (TREE_CODE(c) == FIELD_DECL) break; + if (!c || !integer_zerop(DECL_FIELD_BIT_OFFSET(c)) || + strcmp(IDENTIFIER_POINTER(DECL_NAME(c)), "_M_p") != 0) + return false; + + /* For the basic_string<char> type we're interested in, the type + of the data member is the C string type. */ + if (!is_c_string(TREE_TYPE(c))) return false; + + /* This might not be the real thing, but the bits that matter for + the hook are there. */ + + return true; + + } + + /* ??? This is not implemented. What would the point be of + recognizing LLVM's string type in GCC? */ + bool is_llvm_std_string(tree t) { + + return false; + + } + + virtual unsigned int execute(function *fn) { + + if (!isInInstrumentList(fn)) return 0; + + basic_block bb; + FOR_EACH_BB_FN(bb, fn) { + + for (gimple_stmt_iterator gsi = gsi_after_labels(bb); !gsi_end_p(gsi); + gsi_next(&gsi)) { + + gimple *stmt = gsi_stmt(gsi); + + /* We're only interested in GIMPLE_CALLs. */ + if (gimple_code(stmt) != GIMPLE_CALL) continue; + + if (gimple_call_num_args(stmt) < 2) continue; + + gcall *c = as_a<gcall *>(stmt); + + tree callee_type = gimple_call_fntype(c); + + if (!callee_type || !TYPE_ARG_TYPES(callee_type) || + !TREE_CHAIN(TYPE_ARG_TYPES(callee_type))) + continue; + + tree arg_type[2] = { + + TYPE_MAIN_VARIANT(TREE_VALUE(TYPE_ARG_TYPES(callee_type))), + TYPE_MAIN_VARIANT( + TREE_VALUE(TREE_CHAIN(TYPE_ARG_TYPES(callee_type))))}; + + tree fn = NULL; + /* Callee arglist starts with two GCC std::string arguments. */ + if (arg_type[0] == arg_type[1] && is_gxx_std_string(arg_type[0])) + fn = cmptrs_hook(1); + /* Callee arglist starts with GCC std::string and C string. */ + else if (is_gxx_std_string(arg_type[0]) && is_c_string(arg_type[1])) + fn = cmptrs_hook(2); + /* Callee arglist starts with two LLVM std::string arguments. */ + else if (arg_type[0] == arg_type[1] && is_llvm_std_string(arg_type[0])) + fn = cmptrs_hook(3); + /* Callee arglist starts with LLVM std::string and C string. */ + else if (is_llvm_std_string(arg_type[0]) && is_c_string(arg_type[1])) + fn = cmptrs_hook(4); + /* Callee arglist starts with two pointers to the same type, + and callee returns a value. */ + else if (arg_type[0] == arg_type[1] && POINTER_TYPE_P(arg_type[0]) && + (TYPE_MAIN_VARIANT(gimple_call_return_type(c)) != + void_type_node)) + fn = cmptrs_hook(0); + else + continue; + + tree arg[2] = {gimple_call_arg(c, 0), gimple_call_arg(c, 1)}; + + for (unsigned i = 0; i < ARRAY_SIZE(arg); i++) { + + tree c = fold_convert_loc(UNKNOWN_LOCATION, tp8u, arg[i]); + if (!is_gimple_val(c)) { + + tree s = make_ssa_name(tp8u); + gimple *g = gimple_build_assign(s, c); + c = s; + gsi_insert_before(&gsi, g, GSI_SAME_STMT); + + } + + arg[i] = c; + + } + + gimple *call = gimple_build_call(fn, 2, arg[0], arg[1]); + gsi_insert_before(&gsi, call, GSI_SAME_STMT); + + } + + } + + return 0; + + } + +}; + +static struct plugin_info afl_cmptrs_plugin = { + + .version = "20220420", + .help = G_("AFL gcc cmptrs plugin\n\ +\n\ +Set AFL_QUIET in the environment to silence it.\n\ +"), + +}; + +} // namespace + +/* This is the function GCC calls when loading a plugin. Initialize + and register further callbacks. */ +int plugin_init(struct plugin_name_args * info, + struct plugin_gcc_version *version) { + + if (!plugin_default_version_check(version, &gcc_version)) + FATAL(G_("GCC and plugin have incompatible versions, expected GCC %s, " + "is %s"), + gcc_version.basever, version->basever); + + /* Show a banner. */ + bool quiet = false; + if (isatty(2) && !getenv("AFL_QUIET")) + SAYF(cCYA "afl-gcc-cmptrs-pass " cBRI VERSION cRST + " by <oliva@adacore.com>\n"); + else + quiet = true; + + const char *name = info->base_name; + register_callback(name, PLUGIN_INFO, NULL, &afl_cmptrs_plugin); + + afl_cmptrs_pass * aflp = new afl_cmptrs_pass(quiet); + struct register_pass_info pass_info = { + + .pass = aflp, + .reference_pass_name = "ssa", + .ref_pass_instance_number = 1, + .pos_op = PASS_POS_INSERT_AFTER, + + }; + + register_callback(name, PLUGIN_PASS_MANAGER_SETUP, NULL, &pass_info); + + return 0; + +} + diff --git a/instrumentation/afl-gcc-common.h b/instrumentation/afl-gcc-common.h new file mode 100644 index 00000000..806e7ac3 --- /dev/null +++ b/instrumentation/afl-gcc-common.h @@ -0,0 +1,498 @@ +/* GCC plugin common infrastructure for AFL++ instrumentation passes. + + Copyright 2014-2019 Free Software Foundation, Inc + Copyright 2015, 2016 Google Inc. All rights reserved. + Copyright 2019-2022 AdaCore + + Written by Alexandre Oliva <oliva@adacore.com>, based on the AFL++ + GCC plugin. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ + +#include "../include/config.h" +#include "../include/debug.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef likely + #undef likely +#endif +#ifdef unlikely + #undef unlikely +#endif + +#include <list> +#include <string> +#include <fstream> + +#include <algorithm> +#include <fnmatch.h> + +#include <gcc-plugin.h> +#include <plugin-version.h> +#include <toplev.h> +#include <tree-pass.h> +#include <context.h> +#include <tree.h> +#include <gimplify.h> +#include <basic-block.h> +#include <tree-ssa-alias.h> +#include <gimple-expr.h> +#include <gimple.h> +#include <gimple-iterator.h> +#include <stringpool.h> +#include <gimple-ssa.h> +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ + 60200 /* >= version 6.2.0 */ + #include <tree-vrp.h> +#endif +#include <tree-ssanames.h> +#include <tree-phinodes.h> +#include <ssa-iterators.h> + +#include <intl.h> + +namespace { + +struct afl_base_pass : gimple_opt_pass { + + afl_base_pass(bool quiet, bool debug, struct pass_data const &pd) + : gimple_opt_pass(pd, g), be_quiet(quiet), debug(debug) { + + initInstrumentList(); + + } + + /* Are we outputting to a non-terminal, or running with AFL_QUIET + set? */ + const bool be_quiet; + + /* Are we running with AFL_DEBUG set? */ + const bool debug; + +#define report_fatal_error(msg) BADF(msg) + + std::list<std::string> allowListFiles; + std::list<std::string> allowListFunctions; + std::list<std::string> denyListFiles; + std::list<std::string> denyListFunctions; + + /* Note: this ignore check is also called in isInInstrumentList() */ + bool isIgnoreFunction(function *F) { + + // Starting from "LLVMFuzzer" these are functions used in libfuzzer based + // fuzzing campaign installations, e.g. oss-fuzz + + static constexpr const char *ignoreList[] = { + + "asan.", + "llvm.", + "sancov.", + "__ubsan_", + "ign.", + "__afl_", + "_fini", + "__libc_csu", + "__asan", + "__msan", + "__cmplog", + "__sancov", + "msan.", + "LLVMFuzzerM", + "LLVMFuzzerC", + "LLVMFuzzerI", + "__decide_deferred", + "maybe_duplicate_stderr", + "discard_output", + "close_stdout", + "dup_and_close_stderr", + "maybe_close_fd_mask", + "ExecuteFilesOnyByOne" + + }; + + const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); + + for (auto const &ignoreListFunc : ignoreList) { + + if (strncmp(name, ignoreListFunc, len) == 0) { return true; } + + } + + return false; + + } + + void initInstrumentList() { + + char *allowlist = getenv("AFL_GCC_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); + if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); + if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); + char *denylist = getenv("AFL_GCC_DENYLIST"); + if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); + if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); + + if (allowlist && denylist) + FATAL( + "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " + "but not both!"); + + if (allowlist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(allowlist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + allowListFiles.push_back(line); + else + allowListFunctions.push_back(line); + + } + + getline(fileStream, line); + + } + + if (debug) + DEBUGF("loaded allowlist with %zu file and %zu function entries\n", + allowListFiles.size(), allowListFunctions.size()); + + } + + if (denylist) { + + std::string line; + std::ifstream fileStream; + fileStream.open(denylist); + if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); + getline(fileStream, line); + + while (fileStream) { + + int is_file = -1; + std::size_t npos; + std::string original_line = line; + + line.erase(std::remove_if(line.begin(), line.end(), ::isspace), + line.end()); + + // remove # and following + if ((npos = line.find("#")) != std::string::npos) + line = line.substr(0, npos); + + if (line.compare(0, 4, "fun:") == 0) { + + is_file = 0; + line = line.substr(4); + + } else if (line.compare(0, 9, "function:") == 0) { + + is_file = 0; + line = line.substr(9); + + } else if (line.compare(0, 4, "src:") == 0) { + + is_file = 1; + line = line.substr(4); + + } else if (line.compare(0, 7, "source:") == 0) { + + is_file = 1; + line = line.substr(7); + + } + + if (line.find(":") != std::string::npos) { + + FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); + + } + + if (line.length() > 0) { + + // if the entry contains / or . it must be a file + if (is_file == -1) + if (line.find("/") != std::string::npos || + line.find(".") != std::string::npos) + is_file = 1; + // otherwise it is a function + + if (is_file == 1) + denyListFiles.push_back(line); + else + denyListFunctions.push_back(line); + + } + + getline(fileStream, line); + + } + + if (debug) + DEBUGF("loaded denylist with %zu file and %zu function entries\n", + denyListFiles.size(), denyListFunctions.size()); + + } + + } + + /* Returns the source file name attached to the function declaration F. If + there is no source location information, returns an empty string. */ + std::string getSourceName(function *F) { + + return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : ""; + + } + + bool isInInstrumentList(function *F) { + + bool return_default = true; + + // is this a function with code? If it is external we don't instrument it + // anyway and it can't be in the instrument file list. Or if it is it is + // ignored. + if (isIgnoreFunction(F)) return false; + + if (!denyListFiles.empty() || !denyListFunctions.empty()) { + + if (!denyListFunctions.empty()) { + + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + + for (std::list<std::string>::iterator it = denyListFunctions.begin(); + it != denyListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the deny function list, not " + "instrumenting ... \n", + instFunction.c_str()); + return false; + + } + + } + + } + + } + + if (!denyListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = denyListFiles.begin(); + it != denyListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + return false; + + } + + } + + } + + } else { + + // we could not find out the location. in this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + + } + + } + + } + + // if we do not have a instrument file list return true + if (!allowListFiles.empty() || !allowListFunctions.empty()) { + + return_default = false; + + if (!allowListFunctions.empty()) { + + std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); + + for (std::list<std::string>::iterator it = allowListFunctions.begin(); + it != allowListFunctions.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (instFunction.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the allow function list, instrumenting " + "... \n", + instFunction.c_str()); + return true; + + } + + } + + } + + } + + if (!allowListFiles.empty()) { + + std::string source_file = getSourceName(F); + + if (!source_file.empty()) { + + for (std::list<std::string>::iterator it = allowListFiles.begin(); + it != allowListFiles.end(); ++it) { + + /* We don't check for filename equality here because + * filenames might actually be full paths. Instead we + * check that the actual filename ends in the filename + * specified in the list. We also allow UNIX-style pattern + * matching */ + + if (source_file.length() >= it->length()) { + + if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { + + if (debug) + DEBUGF( + "Function %s is in the allowlist (%s), instrumenting ... " + "\n", + IDENTIFIER_POINTER(DECL_NAME(F->decl)), + source_file.c_str()); + return true; + + } + + } + + } + + } else { + + // we could not find out the location. In this case we say it is not + // in the instrument file list + if (!be_quiet) + WARNF( + "No debug information found for function %s, will not be " + "instrumented (recompile with -g -O[1-3]).", + IDENTIFIER_POINTER(DECL_NAME(F->decl))); + return false; + + } + + } + + } + + return return_default; + + } +}; + +} diff --git a/instrumentation/afl-gcc-pass.so.cc b/instrumentation/afl-gcc-pass.so.cc index bb5483fc..795bbd8a 100644 --- a/instrumentation/afl-gcc-pass.so.cc +++ b/instrumentation/afl-gcc-pass.so.cc @@ -124,50 +124,8 @@ entry edge for the entry block. */ -#include "../include/config.h" -#include "../include/debug.h" - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#ifdef likely - #undef likely -#endif -#ifdef unlikely - #undef unlikely -#endif - -#include <list> -#include <string> -#include <fstream> - -#include <algorithm> -#include <fnmatch.h> - -#include <gcc-plugin.h> -#include <plugin-version.h> -#include <toplev.h> -#include <tree-pass.h> -#include <context.h> -#include <tree.h> -#include <gimplify.h> -#include <basic-block.h> -#include <tree-ssa-alias.h> -#include <gimple-expr.h> -#include <gimple.h> -#include <gimple-iterator.h> -#include <stringpool.h> -#include <gimple-ssa.h> -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= \ - 60200 /* >= version 6.2.0 */ - #include <tree-vrp.h> -#endif -#include <tree-ssanames.h> -#include <tree-phinodes.h> -#include <ssa-iterators.h> - -#include <intl.h> +#include "afl-gcc-common.h" +#include "memmodel.h" /* This plugin, being under the same license as GCC, satisfies the "GPL-compatible Software" definition in the GCC RUNTIME LIBRARY @@ -191,12 +149,10 @@ static constexpr struct pass_data afl_pass_data = { }; -struct afl_pass : gimple_opt_pass { +struct afl_pass : afl_base_pass { afl_pass(bool quiet, unsigned int ratio) - : gimple_opt_pass(afl_pass_data, g), - be_quiet(quiet), - debug(!!getenv("AFL_DEBUG")), + : afl_base_pass(quiet, !!getenv("AFL_DEBUG"), afl_pass_data), inst_ratio(ratio), #ifdef AFL_GCC_OUT_OF_LINE out_of_line(!!(AFL_GCC_OUT_OF_LINE)), @@ -210,13 +166,6 @@ struct afl_pass : gimple_opt_pass { } - /* Are we outputting to a non-terminal, or running with AFL_QUIET - set? */ - const bool be_quiet; - - /* Are we running with AFL_DEBUG set? */ - const bool debug; - /* How likely (%) is a block to be instrumented? */ const unsigned int inst_ratio; @@ -297,21 +246,22 @@ struct afl_pass : gimple_opt_pass { gimple_build_assign(ntry, POINTER_PLUS_EXPR, map_ptr, indx); gimple_seq_add_stmt(&seq, idx_map); - /* Increment the counter in idx_map. */ - tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, - build_zero_cst(TREE_TYPE(ntry))); - if (blocks == 0) - cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); - - /* Load the count from the entry. */ - auto load_cntr = gimple_build_assign(cntr, memref); - gimple_seq_add_stmt(&seq, load_cntr); - /* Prepare to add constant 1 to it. */ - tree incrv = build_one_cst(TREE_TYPE(cntr)); + tree incrv = build_one_cst(TREE_TYPE(TREE_TYPE(ntry))); if (neverZero) { + /* Increment the counter in idx_map. */ + tree memref = build2(MEM_REF, TREE_TYPE(TREE_TYPE(ntry)), ntry, + build_zero_cst(TREE_TYPE(ntry))); + + if (blocks == 0) + cntr = create_tmp_var(TREE_TYPE(memref), ".afl_edge_count"); + + /* Load the count from the entry. */ + auto load_cntr = gimple_build_assign(cntr, memref); + gimple_seq_add_stmt(&seq, load_cntr); + /* NeverZero: if count wrapped around to zero, advance to one. */ if (blocks == 0) { @@ -348,15 +298,24 @@ struct afl_pass : gimple_opt_pass { in xincr. */ incrv = xincr; - } + /* Add the increment (1 or the overflow bit) to count. */ + auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); + gimple_seq_add_stmt(&seq, incr_cntr); + + /* Store count in the map entry. */ + auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); + gimple_seq_add_stmt(&seq, store_cntr); - /* Add the increment (1 or the overflow bit) to count. */ - auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv); - gimple_seq_add_stmt(&seq, incr_cntr); + } else { - /* Store count in the map entry. */ - auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr); - gimple_seq_add_stmt(&seq, store_cntr); + /* Use a serialized memory model. */ + tree memmod = build_int_cst(integer_type_node, MEMMODEL_SEQ_CST); + + tree fadd = builtin_decl_explicit(BUILT_IN_ATOMIC_FETCH_ADD_1); + auto incr_cntr = gimple_build_call(fadd, 3, ntry, incrv, memmod); + gimple_seq_add_stmt(&seq, incr_cntr); + + } /* Store bid >> 1 in __afl_prev_loc. */ auto shift_loc = @@ -456,6 +415,8 @@ struct afl_pass : gimple_opt_pass { thread-local variable. */ static inline tree get_afl_area_ptr_decl() { + /* If type changes, the size N in FETCH_ADD_<N> must be adjusted + in builtin calls above. */ tree type = build_pointer_type(unsigned_char_type_node); tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, get_identifier("__afl_area_ptr"), type); @@ -490,420 +451,11 @@ struct afl_pass : gimple_opt_pass { } -#define report_fatal_error(msg) BADF(msg) - - std::list<std::string> allowListFiles; - std::list<std::string> allowListFunctions; - std::list<std::string> denyListFiles; - std::list<std::string> denyListFunctions; - - /* Note: this ignore check is also called in isInInstrumentList() */ - bool isIgnoreFunction(function *F) { - - // Starting from "LLVMFuzzer" these are functions used in libfuzzer based - // fuzzing campaign installations, e.g. oss-fuzz - - static constexpr const char *ignoreList[] = { - - "asan.", - "llvm.", - "sancov.", - "__ubsan_", - "ign.", - "__afl_", - "_fini", - "__libc_csu", - "__asan", - "__msan", - "__cmplog", - "__sancov", - "msan.", - "LLVMFuzzerM", - "LLVMFuzzerC", - "LLVMFuzzerI", - "__decide_deferred", - "maybe_duplicate_stderr", - "discard_output", - "close_stdout", - "dup_and_close_stderr", - "maybe_close_fd_mask", - "ExecuteFilesOnyByOne" - - }; - - const char *name = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - int len = IDENTIFIER_LENGTH(DECL_NAME(F->decl)); - - for (auto const &ignoreListFunc : ignoreList) { - - if (strncmp(name, ignoreListFunc, len) == 0) { return true; } - - } - - return false; - - } - - void initInstrumentList() { - - char *allowlist = getenv("AFL_GCC_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_GCC_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_GCC_WHITELIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_ALLOWLIST"); - if (!allowlist) allowlist = getenv("AFL_LLVM_INSTRUMENT_FILE"); - if (!allowlist) allowlist = getenv("AFL_LLVM_WHITELIST"); - char *denylist = getenv("AFL_GCC_DENYLIST"); - if (!denylist) denylist = getenv("AFL_GCC_BLOCKLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_DENYLIST"); - if (!denylist) denylist = getenv("AFL_LLVM_BLOCKLIST"); - - if (allowlist && denylist) - FATAL( - "You can only specify either AFL_GCC_ALLOWLIST or AFL_GCC_DENYLIST " - "but not both!"); - - if (allowlist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(allowlist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_ALLOWLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_GCC_ALLOWLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - allowListFiles.push_back(line); - else - allowListFunctions.push_back(line); - - } - - getline(fileStream, line); - - } - - if (debug) - DEBUGF("loaded allowlist with %zu file and %zu function entries\n", - allowListFiles.size(), allowListFunctions.size()); - - } - - if (denylist) { - - std::string line; - std::ifstream fileStream; - fileStream.open(denylist); - if (!fileStream) report_fatal_error("Unable to open AFL_GCC_DENYLIST"); - getline(fileStream, line); - - while (fileStream) { - - int is_file = -1; - std::size_t npos; - std::string original_line = line; - - line.erase(std::remove_if(line.begin(), line.end(), ::isspace), - line.end()); - - // remove # and following - if ((npos = line.find("#")) != std::string::npos) - line = line.substr(0, npos); - - if (line.compare(0, 4, "fun:") == 0) { - - is_file = 0; - line = line.substr(4); - - } else if (line.compare(0, 9, "function:") == 0) { - - is_file = 0; - line = line.substr(9); - - } else if (line.compare(0, 4, "src:") == 0) { - - is_file = 1; - line = line.substr(4); - - } else if (line.compare(0, 7, "source:") == 0) { - - is_file = 1; - line = line.substr(7); - - } - - if (line.find(":") != std::string::npos) { - - FATAL("invalid line in AFL_GCC_DENYLIST: %s", original_line.c_str()); - - } - - if (line.length() > 0) { - - // if the entry contains / or . it must be a file - if (is_file == -1) - if (line.find("/") != std::string::npos || - line.find(".") != std::string::npos) - is_file = 1; - // otherwise it is a function - - if (is_file == 1) - denyListFiles.push_back(line); - else - denyListFunctions.push_back(line); - - } - - getline(fileStream, line); - - } - - if (debug) - DEBUGF("loaded denylist with %zu file and %zu function entries\n", - denyListFiles.size(), denyListFunctions.size()); - - } - - } - - /* Returns the source file name attached to the function declaration F. If - there is no source location information, returns an empty string. */ - std::string getSourceName(function *F) { - - return DECL_SOURCE_FILE(F->decl) ? DECL_SOURCE_FILE(F->decl) : ""; - - } - - bool isInInstrumentList(function *F) { - - bool return_default = true; - - // is this a function with code? If it is external we don't instrument it - // anyway and it can't be in the instrument file list. Or if it is it is - // ignored. - if (isIgnoreFunction(F)) return false; - - if (!denyListFiles.empty() || !denyListFunctions.empty()) { - - if (!denyListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::iterator it = denyListFunctions.begin(); - it != denyListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the deny function list, not " - "instrumenting ... \n", - instFunction.c_str()); - return false; - - } - - } - - } - - } - - if (!denyListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::iterator it = denyListFiles.begin(); - it != denyListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - return false; - - } - - } - - } - - } else { - - // we could not find out the location. in this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - - } - - } - - } - - // if we do not have a instrument file list return true - if (!allowListFiles.empty() || !allowListFunctions.empty()) { - - return_default = false; - - if (!allowListFunctions.empty()) { - - std::string instFunction = IDENTIFIER_POINTER(DECL_NAME(F->decl)); - - for (std::list<std::string>::iterator it = allowListFunctions.begin(); - it != allowListFunctions.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (instFunction.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), instFunction.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allow function list, instrumenting " - "... \n", - instFunction.c_str()); - return true; - - } - - } - - } - - } - - if (!allowListFiles.empty()) { - - std::string source_file = getSourceName(F); - - if (!source_file.empty()) { - - for (std::list<std::string>::iterator it = allowListFiles.begin(); - it != allowListFiles.end(); ++it) { - - /* We don't check for filename equality here because - * filenames might actually be full paths. Instead we - * check that the actual filename ends in the filename - * specified in the list. We also allow UNIX-style pattern - * matching */ - - if (source_file.length() >= it->length()) { - - if (fnmatch(("*" + *it).c_str(), source_file.c_str(), 0) == 0) { - - if (debug) - DEBUGF( - "Function %s is in the allowlist (%s), instrumenting ... " - "\n", - IDENTIFIER_POINTER(DECL_NAME(F->decl)), - source_file.c_str()); - return true; - - } - - } - - } - - } else { - - // we could not find out the location. In this case we say it is not - // in the instrument file list - if (!be_quiet) - WARNF( - "No debug information found for function %s, will not be " - "instrumented (recompile with -g -O[1-3]).", - IDENTIFIER_POINTER(DECL_NAME(F->decl))); - return false; - - } - - } - - } - - return return_default; - - } - }; static struct plugin_info afl_plugin = { - .version = "20220907", + .version = "20220420", .help = G_("AFL gcc plugin\n\ \n\ Set AFL_QUIET in the environment to silence it.\n\ diff --git a/instrumentation/afl-llvm-common.cc b/instrumentation/afl-llvm-common.cc index 9483da83..5fcf27fb 100644 --- a/instrumentation/afl-llvm-common.cc +++ b/instrumentation/afl-llvm-common.cc @@ -291,7 +291,7 @@ void scanForDangerousFunctions(llvm::Module *M) { StringRef r_name = cast<Function>(r->getOperand(0))->getName(); if (!be_quiet) fprintf(stderr, - "Info: Found an ifunc with name %s that points to resolver " + "Note: Found an ifunc with name %s that points to resolver " "function %s, we will not instrument this, putting it into the " "block list.\n", ifunc_name.str().c_str(), r_name.str().c_str()); @@ -329,7 +329,7 @@ void scanForDangerousFunctions(llvm::Module *M) { if (!be_quiet) fprintf(stderr, - "Info: Found constructor function %s with prio " + "Note: Found constructor function %s with prio " "%u, we will not instrument this, putting it into a " "block list.\n", F->getName().str().c_str(), Priority); diff --git a/instrumentation/split-compares-pass.so.cc b/instrumentation/split-compares-pass.so.cc index d7bb7aba..375de065 100644 --- a/instrumentation/split-compares-pass.so.cc +++ b/instrumentation/split-compares-pass.so.cc @@ -566,8 +566,17 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, case CmpInst::ICMP_NE: case CmpInst::ICMP_UGT: case CmpInst::ICMP_ULT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLE: break; default: + if (!be_quiet) + fprintf(stderr, "Error: split-compare: Unsupported predicate (%u)\n", + pred); // unsupported predicate! return false; @@ -581,6 +590,7 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, if (!intTyOp0) { // not an integer type + fprintf(stderr, "Error: split-compare: not an integer type\n"); return false; } @@ -675,6 +685,12 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } + case CmpInst::ICMP_SGE: + case CmpInst::ICMP_SLE: + case CmpInst::ICMP_SGT: + case CmpInst::ICMP_SLT: + case CmpInst::ICMP_UGE: + case CmpInst::ICMP_ULE: case CmpInst::ICMP_UGT: case CmpInst::ICMP_ULT: { @@ -687,7 +703,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, CmpInst * icmp_inv_cmp = nullptr; BasicBlock * inv_cmp_bb = BasicBlock::Create(C, "inv_cmp", end_bb->getParent(), end_bb); - if (pred == CmpInst::ICMP_UGT) { + if (pred == CmpInst::ICMP_UGT || pred == CmpInst::ICMP_SGT || + pred == CmpInst::ICMP_UGE || pred == CmpInst::ICMP_SGE) { icmp_inv_cmp = CmpInst::Create(Instruction::ICmp, CmpInst::ICMP_ULT, op0_high, op1_high); @@ -729,6 +746,8 @@ bool SplitComparesTransform::splitCompare(CmpInst *cmp_inst, Module &M, } default: + if (!be_quiet) + fprintf(stderr, "Error: split-compare: should not happen\n"); return false; } diff --git a/qemu_mode/libqasan/patch.c b/qemu_mode/libqasan/patch.c index ee928ab3..8c5553c0 100644 --- a/qemu_mode/libqasan/patch.c +++ b/qemu_mode/libqasan/patch.c @@ -147,7 +147,7 @@ static void find_libc(void) { fields = sscanf(line, "%" PRIx64 "-%" PRIx64 " %c%c%c%c %" PRIx64 " %x:%x %d" - " %512s", + " %511s", &min, &max, &flag_r, &flag_w, &flag_x, &flag_p, &offset, &dev_maj, &dev_min, &inode, path); diff --git a/src/afl-cc.c b/src/afl-cc.c index 4a56169f..246e01cd 100644 --- a/src/afl-cc.c +++ b/src/afl-cc.c @@ -422,8 +422,24 @@ static void edit_params(u32 argc, char **argv, char **envp) { if (compiler_mode == GCC_PLUGIN) { - char *fplugin_arg = alloc_printf("-fplugin=%s/afl-gcc-pass.so", obj_path); - cc_params[cc_par_cnt++] = fplugin_arg; + char *fplugin_arg; + + if (cmplog_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; + + } else { + + 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"; @@ -1879,6 +1895,7 @@ int main(int argc, char **argv, char **envp) { if (have_gcc_plugin) SAYF( "\nGCC Plugin-specific environment variables:\n" + " AFL_GCC_CMPLOG: log operands of comparisons (RedQueen mutator)\n" " AFL_GCC_OUT_OF_LINE: disable inlined instrumentation\n" " AFL_GCC_SKIP_NEVERZERO: do not skip zero on trace counters\n" " AFL_GCC_INSTRUMENT_FILE: enable selective instrumentation by " @@ -2149,9 +2166,8 @@ int main(int argc, char **argv, char **envp) { } - cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG"); - if (!be_quiet && cmplog_mode) - printf("CmpLog mode by <andreafioraldi@gmail.com>\n"); + cmplog_mode = getenv("AFL_CMPLOG") || getenv("AFL_LLVM_CMPLOG") || + getenv("AFL_GCC_CMPLOG"); #if !defined(__ANDROID__) && !defined(ANDROID) ptr = find_object("afl-compiler-rt.o", argv[0]); diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 6a653a00..f4b2d908 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -146,6 +146,10 @@ void bind_to_free_cpu(afl_state_t *afl) { } + } else { + + OKF("CPU binding request using -b %d successful.", afl->cpu_to_bind); + } return; diff --git a/src/afl-fuzz-python.c b/src/afl-fuzz-python.c index 65501c8c..0231d2cd 100644 --- a/src/afl-fuzz-python.c +++ b/src/afl-fuzz-python.c @@ -28,6 +28,36 @@ /* Python stuff */ #ifdef USE_PYTHON +// Tries to cast a python bytearray or bytes to a char ptr +static inline bool py_bytes(PyObject *py_value, /* out */ char **bytes, + /* out */ size_t *size) { + + if (!py_value) { return false; } + + *bytes = PyByteArray_AsString(py_value); + if (*bytes) { + + // we got a bytearray + *size = PyByteArray_Size(py_value); + + } else { + + *bytes = PyBytes_AsString(py_value); + if (!*bytes) { + + // No valid type returned. + return false; + + } + + *size = PyBytes_Size(py_value); + + } + + return true; + +} + static void *unsupported(afl_state_t *afl, unsigned int seed) { (void)afl; @@ -93,12 +123,22 @@ static size_t fuzz_py(void *py_mutator, u8 *buf, size_t buf_size, u8 **out_buf, if (py_value != NULL) { - mutated_size = PyByteArray_Size(py_value); + char *bytes; + if (!py_bytes(py_value, &bytes, &mutated_size)) { + + FATAL("Python mutator fuzz() should return a bytearray or bytes"); + + } + + if (mutated_size) { + + *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); + if (unlikely(!*out_buf)) { PFATAL("alloc"); } + + memcpy(*out_buf, bytes, mutated_size); - *out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size); - if (unlikely(!*out_buf)) { PFATAL("alloc"); } + } - memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); Py_DECREF(py_value); return mutated_size; @@ -625,7 +665,7 @@ s32 post_trim_py(void *py_mutator, u8 success) { size_t trim_py(void *py_mutator, u8 **out_buf) { PyObject *py_args, *py_value; - size_t ret; + size_t trimmed_size; py_args = PyTuple_New(0); py_value = PyObject_CallObject( @@ -634,10 +674,21 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { if (py_value != NULL) { - ret = PyByteArray_Size(py_value); - *out_buf = afl_realloc(BUF_PARAMS(trim), ret); - if (unlikely(!*out_buf)) { PFATAL("alloc"); } - memcpy(*out_buf, PyByteArray_AsString(py_value), ret); + char *bytes; + if (!py_bytes(py_value, &bytes, &trimmed_size)) { + + FATAL("Python mutator fuzz() should return a bytearray"); + + } + + if (trimmed_size) { + + *out_buf = afl_realloc(BUF_PARAMS(trim), trimmed_size); + if (unlikely(!*out_buf)) { PFATAL("alloc"); } + memcpy(*out_buf, bytes, trimmed_size); + + } + Py_DECREF(py_value); } else { @@ -647,7 +698,7 @@ size_t trim_py(void *py_mutator, u8 **out_buf) { } - return ret; + return trimmed_size; } @@ -692,7 +743,13 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, if (py_value != NULL) { - mutated_size = PyByteArray_Size(py_value); + char *bytes; + if (!py_bytes(py_value, &bytes, &mutated_size)) { + + FATAL("Python mutator fuzz() should return a bytearray"); + + } + if (mutated_size <= buf_size) { /* We reuse the input buf here. */ @@ -706,7 +763,7 @@ size_t havoc_mutation_py(void *py_mutator, u8 *buf, size_t buf_size, } - memcpy(*out_buf, PyByteArray_AsString(py_value), mutated_size); + if (mutated_size) { memcpy(*out_buf, bytes, mutated_size); } Py_DECREF(py_value); return mutated_size; @@ -762,7 +819,17 @@ const char *introspection_py(void *py_mutator) { } else { - return PyByteArray_AsString(py_value); + char * ret; + size_t len; + if (!py_bytes(py_value, &ret, &len)) { + + FATAL( + "Python mutator introspection call returned illegal type (expected " + "bytes or bytearray)"); + + } + + return ret; } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 7c33ba29..18367cf2 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -296,6 +296,7 @@ static void usage(u8 *argv0, int more_help) { " Supported formats are: 'dogstatsd', 'librato',\n" " 'signalfx' and 'influxdb'\n" "AFL_SYNC_TIME: sync time between fuzzing instances (in minutes)\n" + "AFL_NO_CRASH_README: do not create a README in the crashes directory\n" "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" "AFL_EARLY_FORKSERVER: force an early forkserver in an afl-clang-fast/\n" @@ -1468,7 +1469,7 @@ int main(int argc, char **argv_orig, char **envp) { if (afl->shm.cmplog_mode && (!strcmp("-", afl->cmplog_binary) || !strcmp("0", afl->cmplog_binary))) { - afl->cmplog_binary = argv[optind]; + afl->cmplog_binary = strdup(argv[optind]); } diff --git a/test-instr.c b/test-instr.c index b2caa1fe..f304e208 100644 --- a/test-instr.c +++ b/test-instr.c @@ -58,12 +58,21 @@ int main(int argc, char **argv) { // we support three input cases (plus a 4th if stdin is used but there is no // input) - if (buf[0] == '0') - printf("Looks like a zero to me!\n"); - else if (buf[0] == '1') - printf("Pretty sure that is a one!\n"); - else - printf("Neither one or zero? How quaint!\n"); + switch (buf[0]) { + + case '0': + printf("Looks like a zero to me!\n"); + break; + + case '1': + printf("Pretty sure that is a one!\n"); + break; + + default: + printf("Neither one or zero? How quaint!\n"); + break; + + } return 0; diff --git a/test/test-frida-mode.sh b/test/test-frida-mode.sh index 59b8e307..9e1f756d 100755 --- a/test/test-frida-mode.sh +++ b/test/test-frida-mode.sh @@ -62,7 +62,7 @@ test -e ../afl-frida-trace.so && { #else #fi export AFL_FRIDA_PERSISTENT_ADDR=0x`nm test-instr | grep -Ei "T _main|T main" | awk '{print $1}'` - $ECHO "Info: AFL_FRIDA_PERSISTENT_ADDR=$AFL_FRIDA_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" + $ECHO "Note: AFL_FRIDA_PERSISTENT_ADDR=$AFL_FRIDA_PERSISTENT_ADDR <= $(nm test-instr | grep "T main" | awk '{print $1}')" env|grep AFL_|sort file test-instr export AFL_DEBUG_CHILD=1 |