This commit is contained in:
Ruben ten Hove 2022-07-07 17:07:57 +00:00
commit b883faa942
38 changed files with 1537 additions and 1212 deletions

View File

@ -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

1
.gitignore vendored
View File

@ -97,3 +97,4 @@ utils/persistent_mode/persistent_demo_new
utils/persistent_mode/test-instr
!coresight_mode
!coresight_mode/coresight-trace
vuln_prog

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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!"

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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) { \

View File

@ -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)

View File

@ -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

View File

@ -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).

View File

@ -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) | | | | | | |

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -26,7 +26,7 @@
/* Version string: */
// c = release, a = volatile github dev, e = experimental branch
#define VERSION "++4.01a"
#define VERSION "++4.02a"
/******************************************************
* *

View File

@ -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).
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.

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
};
}

View File

@ -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,16 +298,25 @@ 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);
} else {
/* 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);
}
/* Add the increment (1 or the overflow bit) to count. */
auto incr_cntr = gimple_build_assign(cntr, PLUS_EXPR, cntr, incrv);
gimple_seq_add_stmt(&seq, incr_cntr);
/* Store count in the map entry. */
auto store_cntr = gimple_build_assign(unshare_expr(memref), cntr);
gimple_seq_add_stmt(&seq, store_cntr);
/* Store bid >> 1 in __afl_prev_loc. */
auto shift_loc =
gimple_build_assign(ploc, build_int_cst(TREE_TYPE(ploc), bid >> 1));
@ -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\

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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]);

View File

@ -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;

View File

@ -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)) {
*out_buf = afl_realloc(BUF_PARAMS(fuzz), mutated_size);
if (unlikely(!*out_buf)) { PFATAL("alloc"); }
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);
}
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;
}

View File

@ -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]);
}

View File

@ -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;

View File

@ -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