Merge branch 'debug' into dev

This commit is contained in:
van Hauser
2020-08-14 13:23:14 +02:00
committed by GitHub
21 changed files with 1147 additions and 236 deletions

View File

@ -521,6 +521,7 @@ clean:
$(MAKE) -C examples/argv_fuzzing clean
$(MAKE) -C qemu_mode/unsigaction clean
$(MAKE) -C qemu_mode/libcompcov clean
test -d qemu_taint/qemu && { cd qemu_taint ; ./clean.sh ; }
rm -rf qemu_mode/qemu-3.1.1
ifeq "$(IN_REPO)" "1"
test -d unicorn_mode/unicornafl && $(MAKE) -C unicorn_mode/unicornafl clean || true
@ -531,6 +532,7 @@ endif
deepclean: clean
rm -rf qemu_mode/qemu-3.1.1.tar.xz
rm -rf qemu_taint/qemu
rm -rf unicorn_mode/unicornafl
git reset --hard >/dev/null 2>&1 || true
@ -588,6 +590,7 @@ install: all $(MANPAGES)
install -m 755 $(PROGS) $(SH_PROGS) $${DESTDIR}$(BIN_PATH)
rm -f $${DESTDIR}$(BIN_PATH)/afl-as
if [ -f afl-qemu-trace ]; then install -m 755 afl-qemu-trace $${DESTDIR}$(BIN_PATH); fi
if [ -f afl-qemu-taint ]; then install -m 755 afl-qemu-taint $${DESTDIR}$(BIN_PATH); fi
if [ -f afl-gcc-fast ]; then set e; install -m 755 afl-gcc-fast $${DESTDIR}$(BIN_PATH); ln -sf afl-gcc-fast $${DESTDIR}$(BIN_PATH)/afl-g++-fast; install -m 755 afl-gcc-pass.so afl-gcc-rt.o $${DESTDIR}$(HELPER_PATH); fi
if [ -f afl-clang-fast ]; then $(MAKE) -C llvm_mode install; fi
if [ -f libdislocator.so ]; then set -e; install -m 755 libdislocator.so $${DESTDIR}$(HELPER_PATH); fi

View File

@ -1,3 +1,36 @@
# qemu_taint variant.
UPDATE: **WORKS NOW** **PLEASE TEST** **:-)**
## HOWTO
cd qemu_taint && ./build_qemu_taint.sh
afl-fuzz -A ...
## CAVEATS
* llvm shmem persistent mode does not and can not not work
* MOpt works but totally ignores the taint information, so disabled here
* custom mutators? dunno if they work or not. depends on how they work.
* not tested with qemu_mode
* there are several debug checks to ensure the data is fine which slows down
fuzzing, if the beta experiment runs fine these will be improved and it
will result in quite a speed gain.
## THE TAINT
taint can be seen in out/taint/
the id:000 mirrors the out/queue entry, except the content it 0x00 for
untainted bytes and '!' for tainted bytes.
If a file has new tainted bytes compared to from which previous entry it
was created then there is a id:000[...].new file where the new bytes are
marked '!'.
the mutation switches between fuzzing all tainted bytes in one cycle and
only new bytes in the other cycle.
# American Fuzzy Lop plus plus (afl++)
<img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo">

191
afl_driver.cpp Normal file
View File

@ -0,0 +1,191 @@
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// Platform detection. Copied from FuzzerInternal.h
#ifdef __linux__
#define LIBFUZZER_LINUX 1
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __APPLE__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 1
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __NetBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 1
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 0
#elif __FreeBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 1
#define LIBFUZZER_OPENBSD 0
#elif __OpenBSD__
#define LIBFUZZER_LINUX 0
#define LIBFUZZER_APPLE 0
#define LIBFUZZER_NETBSD 0
#define LIBFUZZER_FREEBSD 0
#define LIBFUZZER_OPENBSD 1
#else
#error "Support for your platform has not been implemented"
#endif
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
// Notify AFL about persistent mode.
static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
int __afl_persistent_loop(unsigned int);
static volatile char suppress_warning2 = AFL_PERSISTENT[0];
// Notify AFL about deferred forkserver.
static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
void __afl_manual_init();
static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
// Input buffer.
static const size_t kMaxAflInputSize = 1024000;
static uint8_t AflInputBuf[kMaxAflInputSize];
// Use this optionally defined function to output sanitizer messages even if
// user asks to close stderr.
__attribute__((weak)) void __sanitizer_set_report_fd(void *);
// Keep track of where stderr content is being written to, so that
// dup_and_close_stderr can use the correct one.
static FILE *output_file = stderr;
// Experimental feature to use afl_driver without AFL's deferred mode.
// Needs to run before __afl_auto_init.
__attribute__((constructor(0))) static void __decide_deferred_forkserver(void) {
if (getenv("AFL_DRIVER_DONT_DEFER")) {
if (unsetenv("__AFL_DEFER_FORKSRV")) {
perror("Failed to unset __AFL_DEFER_FORKSRV");
abort();
}
}
}
// If the user asks us to duplicate stderr, then do it.
static void maybe_duplicate_stderr() {
char *stderr_duplicate_filename =
getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
if (!stderr_duplicate_filename)
return;
FILE *stderr_duplicate_stream =
freopen(stderr_duplicate_filename, "a+", stderr);
if (!stderr_duplicate_stream) {
fprintf(
stderr,
"Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
abort();
}
output_file = stderr_duplicate_stream;
}
// Most of these I/O functions were inspired by/copied from libFuzzer's code.
static void discard_output(int fd) {
FILE *temp = fopen("/dev/null", "w");
if (!temp)
abort();
dup2(fileno(temp), fd);
fclose(temp);
}
static void close_stdout() { discard_output(STDOUT_FILENO); }
// Prevent the targeted code from writing to "stderr" but allow sanitizers and
// this driver to do so.
static void dup_and_close_stderr() {
int output_fileno = fileno(output_file);
int output_fd = dup(output_fileno);
if (output_fd <= 0)
abort();
FILE *new_output_file = fdopen(output_fd, "w");
if (!new_output_file)
abort();
if (!__sanitizer_set_report_fd)
return;
__sanitizer_set_report_fd(reinterpret_cast<void *>(output_fd));
discard_output(output_fileno);
}
// Close stdout and/or stderr if user asks for it.
static void maybe_close_fd_mask() {
char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK");
if (!fd_mask_str)
return;
int fd_mask = atoi(fd_mask_str);
if (fd_mask & 2)
dup_and_close_stderr();
if (fd_mask & 1)
close_stdout();
}
// Define LLVMFuzzerMutate to avoid link failures for targets that use it
// with libFuzzer's LLVMFuzzerCustomMutator.
size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
assert(false && "LLVMFuzzerMutate should not be called from afl_driver");
return 0;
}
int main(int argc, char **argv) {
printf(
"======================= INFO =========================\n"
"This binary is built for AFL-fuzz.\n"
"To run the target function on individual input(s) execute this:\n"
" %s < INPUT_FILE\n"
"To fuzz with afl-fuzz execute this:\n"
" afl-fuzz [afl-flags] %s [-N]\n"
"afl-fuzz will run N iterations before "
"re-spawning the process (default: 1000)\n"
"======================================================\n",
argv[0], argv[0]);
maybe_duplicate_stderr();
maybe_close_fd_mask();
if (LLVMFuzzerInitialize)
LLVMFuzzerInitialize(&argc, &argv);
// Do any other expensive one-time initialization here.
int N = 100000;
if (argc == 2 && argv[1][0] == '-')
N = atoi(argv[1] + 1);
else if(argc == 2 && (N = atoi(argv[1])) > 0)
printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
assert(N > 0);
if (!getenv("AFL_DRIVER_DONT_DEFER"))
__afl_manual_init();
// Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
// on the first execution of LLVMFuzzerTestOneInput is ignored.
uint8_t dummy_input[1] = {0};
LLVMFuzzerTestOneInput(dummy_input, 1);
while (__afl_persistent_loop(N)) {
ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
if (n_read > 0) {
LLVMFuzzerTestOneInput(AflInputBuf, n_read);
}
}
printf("%s: successfully executed input(s)\n", argv[0]);
}

View File

@ -106,10 +106,7 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both.
#error "Support for your platform has not been implemented"
#endif
int __afl_sharedmem_fuzzing = 1;
extern unsigned int * __afl_fuzz_len;
extern unsigned char *__afl_fuzz_ptr;
// extern struct cmp_map *__afl_cmp_map;
int __afl_sharedmem_fuzzing = 0;
// libFuzzer interface is thin, so we don't include any libFuzzer headers.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
@ -275,6 +272,7 @@ int main(int argc, char **argv) {
// Do any other expensive one-time initialization here.
uint8_t dummy_input[64] = {0};
uint8_t buf[1024000];
memcpy(dummy_input, (void *)AFL_PERSISTENT, sizeof(AFL_PERSISTENT));
memcpy(dummy_input + 32, (void *)AFL_DEFER_FORKSVR,
sizeof(AFL_DEFER_FORKSVR));
@ -285,17 +283,25 @@ int main(int argc, char **argv) {
printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N);
else if (argc > 1) {
__afl_sharedmem_fuzzing = 0;
if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) {
__afl_manual_init();
}
return ExecuteFilesOnyByOne(argc, argv);
}
assert(N > 0);
// if (!getenv("AFL_DRIVER_DONT_DEFER"))
if (!getenv("AFL_DISABLE_LLVM_INSTRUMENTATION")) {
fprintf(stderr, "performing manual init\n");
__afl_manual_init();
}
// Call LLVMFuzzerTestOneInput here so that coverage caused by initialization
// on the first execution of LLVMFuzzerTestOneInput is ignored.
LLVMFuzzerTestOneInput(dummy_input, 1);
@ -303,25 +309,13 @@ int main(int argc, char **argv) {
int num_runs = 0;
while (__afl_persistent_loop(N)) {
#ifdef _DEBUG
fprintf(stderr, "CLIENT crc: %016llx len: %u\n",
hash64(__afl_fuzz_ptr, *__afl_fuzz_len, 0xa5b35705),
*__afl_fuzz_len);
fprintf(stderr, "RECV:");
for (int i = 0; i < *__afl_fuzz_len; i++)
fprintf(stderr, "%02x", __afl_fuzz_ptr[i]);
fprintf(stderr, "\n");
#endif
if (*__afl_fuzz_len) {
ssize_t r = read(0, buf, sizeof(buf));
num_runs++;
LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
if (r > 0) { LLVMFuzzerTestOneInput(buf, r); }
}
}
printf("%s: successfully executed %d input(s)\n", argv[0], num_runs);
printf("%s: successfully executed input(s)\n", argv[0]);
}

View File

@ -134,7 +134,9 @@ extern s32
struct queue_entry {
u8 * fname; /* File name for the test case */
u8 * fname_taint; /* File name for taint data */
u32 len; /* Input length */
struct queue_entry *prev; /* previous queue entry, if any */
u8 cal_failed, /* Calibration failed? */
trim_done, /* Trimmed? */
@ -148,7 +150,10 @@ struct queue_entry {
is_ascii; /* Is the input just ascii text? */
u32 bitmap_size, /* Number of bits set in bitmap */
fuzz_level; /* Number of fuzzing iterations */
fuzz_level, /* Number of fuzzing iterations */
taint_bytes_all, /* Number of tainted bytes */
taint_bytes_new, /* Number of new tainted bytes */
taint_bytes_highest; /* highest offset in input */
u64 exec_us, /* Execution time (us) */
handicap, /* Number of queue cycles behind */
@ -380,6 +385,8 @@ typedef struct afl_state {
char **argv; /* argv if needed */
char **argv_taint; /* argv for taint mode */
/* MOpt:
Lots of globals, but mostly for the status UI and other things where it
really makes no sense to haul them around as function parameters. */
@ -431,7 +438,9 @@ typedef struct afl_state {
*in_bitmap, /* Input bitmap */
*file_extension, /* File extension */
*orig_cmdline, /* Original command line */
*infoexec; /* Command to execute on a new crash */
*infoexec, /* Command to execute on a new crash */
*taint_input_file, /* fuzz_input_one input file */
*taint_src, *taint_map;
u32 hang_tmout; /* Timeout used for hang det (ms) */
@ -442,7 +451,9 @@ typedef struct afl_state {
custom_only, /* Custom mutator only mode */
python_only, /* Python-only mode */
is_main_node, /* if this is the main node */
is_secondary_node; /* if this is a secondary instance */
is_secondary_node, /* if this is a secondary instance */
taint_needs_splode, /* explode fuzz input */
taint_mode;
u32 stats_update_freq; /* Stats update frequency (execs) */
@ -503,7 +514,8 @@ typedef struct afl_state {
useless_at_start, /* Number of useless starting paths */
var_byte_count, /* Bitmap bytes with var behavior */
current_entry, /* Current queue entry ID */
havoc_div; /* Cycle count divisor for havoc */
havoc_div, /* Cycle count divisor for havoc */
taint_len, taint_count;
u64 total_crashes, /* Total number of crashes */
unique_crashes, /* Crashes with unique signatures */
@ -590,6 +602,9 @@ typedef struct afl_state {
char * cmplog_binary;
afl_forkserver_t cmplog_fsrv; /* cmplog has its own little forkserver */
/* Taint mode */
afl_forkserver_t taint_fsrv; /* taint mode has its own little forkserver */
/* Custom mutators */
struct custom_mutator *mutator;
@ -841,7 +856,8 @@ struct custom_mutator {
};
void afl_state_init(afl_state_t *, uint32_t map_size);
void afl_state_init_1(afl_state_t *, uint32_t map_size);
void afl_state_init_2(afl_state_t *, uint32_t map_size);
void afl_state_deinit(afl_state_t *);
/* Set stop_soon flag on all childs, kill all childs */
@ -887,7 +903,7 @@ void deinit_py(void *);
void mark_as_det_done(afl_state_t *, struct queue_entry *);
void mark_as_variable(afl_state_t *, struct queue_entry *);
void mark_as_redundant(afl_state_t *, struct queue_entry *, u8);
void add_to_queue(afl_state_t *, u8 *, u32, u8);
void add_to_queue(afl_state_t *, u8 *, u8 *, u32, struct queue_entry *, u8);
void destroy_queue(afl_state_t *);
void update_bitmap_score(afl_state_t *, struct queue_entry *);
void cull_queue(afl_state_t *);
@ -897,7 +913,9 @@ u32 calculate_score(afl_state_t *, struct queue_entry *);
void write_bitmap(afl_state_t *);
u32 count_bits(afl_state_t *, u8 *);
u32 count_bits_len(afl_state_t *, u8 *, u32);
u32 count_bytes(afl_state_t *, u8 *);
u32 count_bytes_len(afl_state_t *, u8 *, u32);
u32 count_non_255_bytes(afl_state_t *, u8 *);
#ifdef WORD_SIZE_64
void simplify_trace(afl_state_t *, u64 *);
@ -975,6 +993,8 @@ void check_if_tty(afl_state_t *);
void setup_signal_handlers(void);
void save_cmdline(afl_state_t *, u32, char **);
void read_foreign_testcases(afl_state_t *, int);
void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname,
u8 *mem, u32 len);
/* CmpLog */

View File

@ -55,6 +55,7 @@ extern u8 *doc_path; /* path to documentation dir */
@returns the path, allocating the string */
u8 *find_binary(u8 *fname);
u8 *find_afl_binary(u8 *fname, u8 *own_loc);
/* Read a bitmap from file fname to memory
This is for the -B option again. */

View File

@ -123,6 +123,7 @@ static char *afl_environment_variables[] = {
"AFL_SKIP_BIN_CHECK",
"AFL_SKIP_CPUFREQ",
"AFL_SKIP_CRASHES",
"AFL_TAINT_INPUT",
"AFL_TMIN_EXACT",
"AFL_TMPDIR",
"AFL_TOKEN_FILE",

View File

@ -80,6 +80,8 @@ typedef struct afl_forkserver {
u8 qemu_mode; /* if running in qemu mode or not */
u8 taint_mode; /* if running taint analysis or not */
u32 *shmem_fuzz_len; /* length of the fuzzing test case */
u8 *shmem_fuzz; /* allocated memory for fuzzing */

42
qemu_taint/README.md Normal file
View File

@ -0,0 +1,42 @@
# qemu_taint
First level taint implementation with qemu for linux user mode
**THIS IS NOT WORKING YET** **WIP**
## What is this for
On new queue entries (newly discovered paths into the target) this tainter
is run with the new input and the data gathered which bytes in the input
file are actually touched.
Only touched bytes are then fuzzed by afl-fuzz
## How to build
./build_qemu_taint.sh
## How to use
Add the -A flag to afl-fuzz
## Caveats
For some targets this is amazing and improves fuzzing a lot, but if a target
copies all input bytes first (e.g. for creating a crc checksum or just to
safely work with the data), then this is not helping at all.
## Future
Two fuzz modes for a queue entry which will be switched back and forth:
1. fuzz all touched bytes
2. fuzz only bytes that are newly touched (compared to the one this queue
entry is based on)
## TODO
* Direct trim: trim to highest touched byte, that is all we need to do
* add 5-25% dummy bytes to the queue entries? (maybe create a 2nd one?)
* Disable trim?

7
qemu_taint/build_qemu_taint.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
test -d qemu || git clone https://github.com/vanhauser-thc/qemu_taint qemu || exit 1
cd qemu || exit 1
test -d .git && { git stash ; git pull ; }
cp -fv ../../include/config.h ../../include/types.h . || exit 1
./build.sh || exit 1
cp -fv ./afl-qemu-taint ../..

3
qemu_taint/clean.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
rm -f afl-qemu-taint qemu/afl-qemu-taint ../afl-qemu-taint
test -d qemu && { cd qemu ; ./clean.sh ; }

View File

@ -138,12 +138,59 @@ void argv_cpy_free(char **argv) {
}
u8 *find_afl_binary(u8 *fname, u8 *own_loc) {
u8 *tmp, *rsl, *own_copy, *cp;
tmp = getenv("AFL_PATH");
if (tmp) {
cp = alloc_printf("%s/%s", tmp, fname);
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
return cp;
}
if (own_loc) {
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
if (rsl) {
*rsl = 0;
cp = alloc_printf("%s/%s", own_copy, fname);
ck_free(own_copy);
if (!access(cp, X_OK)) { return cp; }
} else {
ck_free(own_copy);
}
}
cp = alloc_printf("%s/%s", BIN_PATH, fname);
if (!access(cp, X_OK)) { return cp; }
ck_free(cp);
return NULL;
}
/* Rewrite argv for QEMU. */
char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
char **new_argv = ck_alloc(sizeof(char *) * (argc + 4));
u8 * tmp, *cp = NULL, *rsl, *own_copy;
u8 * cp = NULL;
memcpy(&new_argv[3], &argv[1], (int)(sizeof(char *)) * (argc - 1));
new_argv[argc - 1] = NULL;
@ -153,51 +200,15 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
/* Now we need to actually find the QEMU binary to put in argv[0]. */
tmp = getenv("AFL_PATH");
cp = find_afl_binary("afl-qemu-trace", own_loc);
if (tmp) {
cp = alloc_printf("%s/afl-qemu-trace", tmp);
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
if (cp) {
*target_path_p = new_argv[0] = cp;
return new_argv;
}
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
if (rsl) {
*rsl = 0;
cp = alloc_printf("%s/afl-qemu-trace", own_copy);
ck_free(own_copy);
if (!access(cp, X_OK)) {
*target_path_p = new_argv[0] = cp;
return new_argv;
}
} else {
ck_free(own_copy);
}
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
if (cp) { ck_free(cp); }
*target_path_p = new_argv[0] = ck_strdup(BIN_PATH "/afl-qemu-trace");
return new_argv;
}
SAYF("\n" cLRD "[-] " cRST
"Oops, unable to find the 'afl-qemu-trace' binary. The binary must be "
"built\n"
@ -225,7 +236,7 @@ char **get_qemu_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
char **new_argv = ck_alloc(sizeof(char *) * (argc + 3));
u8 * tmp, *cp = NULL, *rsl, *own_copy;
u8 * cp = NULL;
memcpy(&new_argv[2], &argv[1], (int)(sizeof(char *)) * (argc - 1));
new_argv[argc - 1] = NULL;
@ -234,92 +245,38 @@ char **get_wine_argv(u8 *own_loc, u8 **target_path_p, int argc, char **argv) {
/* Now we need to actually find the QEMU binary to put in argv[0]. */
tmp = getenv("AFL_PATH");
cp = find_afl_binary("afl-qemu-trace", own_loc);
if (tmp) {
cp = alloc_printf("%s/afl-qemu-trace", tmp);
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
if (cp) {
ck_free(cp);
cp = find_afl_binary("afl-wine-trace", own_loc);
cp = alloc_printf("%s/afl-wine-trace", tmp);
if (access(cp, X_OK)) { FATAL("Unable to find '%s'", tmp); }
if (cp) {
*target_path_p = new_argv[0] = cp;
return new_argv;
}
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
if (rsl) {
*rsl = 0;
cp = alloc_printf("%s/afl-qemu-trace", own_copy);
if (cp && !access(cp, X_OK)) {
ck_free(cp);
cp = alloc_printf("%s/afl-wine-trace", own_copy);
if (!access(cp, X_OK)) {
*target_path_p = new_argv[0] = cp;
return new_argv;
}
}
ck_free(own_copy);
} else {
ck_free(own_copy);
}
u8 *ncp = BIN_PATH "/afl-qemu-trace";
if (!access(ncp, X_OK)) {
ncp = BIN_PATH "/afl-wine-trace";
if (!access(ncp, X_OK)) {
*target_path_p = new_argv[0] = ck_strdup(ncp);
return new_argv;
}
}
SAYF("\n" cLRD "[-] " cRST
"Oops, unable to find the '%s' binary. The binary must be "
"built\n"
" separately by following the instructions in "
"qemu_mode/README.md. "
"If you\n"
" already have the binary installed, you may need to specify "
"AFL_PATH in the\n"
" environment.\n\n"
"Oops, unable to find the afl-qemu-trace and afl-wine-trace binaries.\n"
"The afl-qemu-trace binary must be built separately by following the "
"instructions\n"
"in qemu_mode/README.md. If you already have the binary installed, you "
"may need\n"
"to specify the location via AFL_PATH in the environment.\n\n"
" Of course, even without QEMU, afl-fuzz can still work with "
"binaries that are\n"
" instrumented at compile time with afl-gcc. It is also possible to "
"use it as a\n"
" traditional non-instrumented fuzzer by specifying '-n' in the "
"command "
"line.\n",
ncp);
"line.\n");
FATAL("Failed to locate '%s'.", ncp);
FATAL("Failed to locate 'afl-qemu-trace' and 'afl-wine-trace'.");
}

View File

@ -498,11 +498,21 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv,
char pid_buf[16];
sprintf(pid_buf, "%d", fsrv->fsrv_pid);
if (fsrv->cmplog_binary)
if (fsrv->taint_mode) {
setenv("__AFL_TARGET_PID3", pid_buf, 1);
} else if (fsrv->cmplog_binary) {
setenv("__AFL_TARGET_PID2", pid_buf, 1);
else
} else {
setenv("__AFL_TARGET_PID1", pid_buf, 1);
}
/* Close the unneeded endpoints. */
close(ctl_pipe[0]);
@ -937,7 +947,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
} else {
s32 fd = fsrv->out_fd;
s32 fd;
if (fsrv->out_file) {
@ -956,6 +966,7 @@ void afl_fsrv_write_to_testcase(afl_forkserver_t *fsrv, u8 *buf, size_t len) {
} else {
fd = fsrv->out_fd;
lseek(fd, 0, SEEK_SET);
}

View File

@ -177,6 +177,40 @@ u32 count_bits(afl_state_t *afl, u8 *mem) {
}
u32 count_bits_len(afl_state_t *afl, u8 *mem, u32 len) {
u32 *ptr = (u32 *)mem;
u32 i = (len >> 2);
u32 ret = 0;
(void)(afl);
if (len % 4) i++;
while (i--) {
u32 v = *(ptr++);
/* This gets called on the inverse, virgin bitmap; optimize for sparse
data. */
if (v == 0xffffffff) {
ret += 32;
continue;
}
v -= ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
ret += (((v + (v >> 4)) & 0xF0F0F0F) * 0x01010101) >> 24;
}
return ret;
}
/* Count the number of bytes set in the bitmap. Called fairly sporadically,
mostly to update the status screen or calibrate and examine confirmed
new paths. */
@ -203,6 +237,32 @@ u32 count_bytes(afl_state_t *afl, u8 *mem) {
}
u32 count_bytes_len(afl_state_t *afl, u8 *mem, u32 len) {
u32 *ptr = (u32 *)mem;
u32 i = (len >> 2);
u32 ret = 0;
(void)(afl);
if (len % 4) i++;
while (i--) {
u32 v = *(ptr++);
if (!v) { continue; }
if (v & 0x000000ff) { ++ret; }
if (v & 0x0000ff00) { ++ret; }
if (v & 0x00ff0000) { ++ret; }
if (v & 0xff000000) { ++ret; }
}
return ret;
}
/* Count the number of non-255 bytes set in the bitmap. Used strictly for the
status screen, several calls per second or so. */
@ -595,7 +655,7 @@ save_if_interesting(afl_state_t *afl, void *mem, u32 len, u8 fault) {
#endif /* ^!SIMPLE_FILES */
add_to_queue(afl, queue_fn, len, 0);
add_to_queue(afl, queue_fn, mem, len, afl->queue_top, 0);
if (hnb == 2) {

View File

@ -712,7 +712,7 @@ void read_testcases(afl_state_t *afl) {
if (!access(dfn, F_OK)) { passed_det = 1; }
add_to_queue(afl, fn2, st.st_size, passed_det);
add_to_queue(afl, fn2, NULL, st.st_size, NULL, passed_det);
}
@ -771,9 +771,13 @@ void perform_dry_run(afl_state_t *afl) {
close(fd);
res = calibrate_case(afl, q, use_mem, 0, 1);
ck_free(use_mem);
if (afl->stop_soon) { return; }
if (afl->stop_soon) {
ck_free(use_mem);
return;
}
if (res == afl->crash_mode || res == FSRV_RUN_NOBITS) {
@ -960,6 +964,10 @@ void perform_dry_run(afl_state_t *afl) {
}
/* perform taint gathering on the input seed */
if (afl->taint_mode) perform_taint_run(afl, q, q->fname, use_mem, q->len);
ck_free(use_mem);
q = q->next;
}
@ -1438,6 +1446,10 @@ static void handle_existing_out_dir(afl_state_t *afl) {
u8 *orig_q = alloc_printf("%s/queue", afl->out_dir);
u8 *fnt = alloc_printf("%s/taint", afl->out_dir);
mkdir(fnt, 0755); // ignore errors
ck_free(fnt);
afl->in_dir = alloc_printf("%s/_resume", afl->out_dir);
rename(orig_q, afl->in_dir); /* Ignore errors */
@ -1494,6 +1506,20 @@ static void handle_existing_out_dir(afl_state_t *afl) {
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
ck_free(fn);
if (afl->taint_mode) {
fn = alloc_printf("%s/taint", afl->out_dir);
mkdir(fn, 0755); // ignore errors
u8 *fn2 = alloc_printf("%s/taint/.input", afl->out_dir);
unlink(fn2); // ignore errors
ck_free(fn2);
if (delete_files(fn, CASE_PREFIX)) { goto dir_cleanup_failed; }
ck_free(fn);
}
/* All right, let's do <afl->out_dir>/crashes/id:* and
* <afl->out_dir>/hangs/id:*. */
@ -1721,6 +1747,16 @@ void setup_dirs_fds(afl_state_t *afl) {
if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); }
ck_free(tmp);
/* Taint directory if taint_mode. */
if (afl->taint_mode) {
tmp = alloc_printf("%s/taint", afl->out_dir);
if (mkdir(tmp, 0700)) { PFATAL("Unable to create '%s'", tmp); }
ck_free(tmp);
}
/* Top-level directory for queue metadata used for session
resume and related tasks. */

View File

@ -458,6 +458,147 @@ u8 fuzz_one_original(afl_state_t *afl) {
}
u32 tmp_val = 0;
if (unlikely(afl->taint_mode)) {
tmp_val = afl->queue_cycle % 2; // starts with 1
ret_val = 0;
if (unlikely(afl->queue_cur->cal_failed && !tmp_val)) goto abandon_entry;
if (unlikely(!afl->skip_deterministic && !afl->queue_cur->passed_det &&
!tmp_val))
goto abandon_entry;
if ((!afl->queue_cur->taint_bytes_new ||
afl->queue_cur->taint_bytes_new == afl->queue_cur->len) &&
!tmp_val)
goto abandon_entry;
ret_val = 1;
s32 dst = 0, i;
temp_len = len = afl->queue_cur->len;
s32 j = 0; // tmp
fd = open(afl->queue_cur->fname, O_RDONLY);
afl->taint_src = mmap(0, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (fd < 0 || (ssize_t)afl->taint_src == -1)
FATAL("unable to open '%s'", afl->queue_cur->fname);
close(fd);
afl->taint_needs_splode = 1;
switch (tmp_val) {
case 1: // fuzz only tainted bytes
// special case: all or nothing tainted. in this case we act like
// nothing is special. this is not the taint you are looking for ...
if (!afl->queue_cur->taint_bytes_all ||
afl->queue_cur->taint_bytes_all == (u32)len) {
orig_in = in_buf = afl->taint_src;
afl->taint_needs_splode = 0;
break;
}
fd = open(afl->taint_input_file, O_RDONLY);
temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_all;
orig_in = in_buf =
mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536,
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (fd < 0 || (ssize_t)in_buf == -1)
FATAL("unable to open '%s'", afl->taint_input_file);
close(fd);
fd = open(afl->queue_cur->fname_taint, O_RDONLY);
afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (fd < 0 || (ssize_t)in_buf == -1)
FATAL("unable to open '%s'", afl->queue_cur->fname_taint);
close(fd);
for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++)
if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i];
// FIXME DEBUG TODO XXX
for (i = 0; i < (s32)afl->queue_cur->len; i++) {
switch (afl->taint_map[i]) {
case 0x0:
break;
case '!':
j++;
break;
default:
FATAL(
"invalid taint map entry byte 0x%02x at position %d "
"(passed_det:%d)\n",
afl->taint_map[i], i, afl->queue_cur->passed_det);
}
}
if (j != len)
FATAL("different taint values in map vs in queue (%d != %d)", j, len);
break;
case 0: // fuzz only newly tainted bytes
fd = open(afl->taint_input_file, O_RDONLY);
temp_len = len = afl->taint_len = afl->queue_cur->taint_bytes_new;
orig_in = in_buf =
mmap(0, len >= MAX_FILE - 65536 ? MAX_FILE : len + 65536,
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (fd < 0 || (ssize_t)in_buf == -1)
FATAL("unable to open '%s'", afl->taint_input_file);
close(fd);
u8 *fn = alloc_printf("%s.new", afl->queue_cur->fname_taint);
if (!fn) FATAL("OOM");
fd = open(fn, O_RDWR);
afl->taint_map = mmap(0, afl->queue_cur->len, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (fd < 0 || (ssize_t)in_buf == -1)
FATAL("unable to open '%s' for %u bytes", fn, len);
close(fd);
ck_free(fn);
for (i = 0; i < (s32)afl->queue_cur->len && dst < len; i++)
if (afl->taint_map[i]) in_buf[dst++] = afl->taint_src[i];
// FIXME DEBUG TODO XXX
for (i = 0; i < (s32)afl->queue_cur->len; i++) {
switch (afl->taint_map[i]) {
case 0x0:
break;
case '!':
j++;
break;
default:
FATAL(
"invalid taint map entry byte 0x%02x at position %d "
"(passed_det:%d)\n",
afl->taint_map[i], i, afl->queue_cur->passed_det);
}
}
if (j != len)
FATAL("different taint values in map vs in queue (%d != %d)", j, len);
break;
}
} else {
/* Map the test case into memory. */
fd = open(afl->queue_cur->fname, O_RDONLY);
@ -480,6 +621,8 @@ u8 fuzz_one_original(afl_state_t *afl) {
close(fd);
}
/* We could mmap() out_buf as MAP_PRIVATE, but we end up clobbering every
single byte anyway, so it wouldn't give us any performance or memory usage
benefits. */
@ -502,8 +645,12 @@ u8 fuzz_one_original(afl_state_t *afl) {
afl->queue_cur->exec_cksum = 0;
res =
calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1, 0);
if (unlikely(afl->taint_needs_splode))
res = calibrate_case(afl, afl->queue_cur, afl->taint_src,
afl->queue_cycle - 1, 0);
else
res = calibrate_case(afl, afl->queue_cur, in_buf, afl->queue_cycle - 1,
0);
if (unlikely(res == FSRV_RUN_ERROR)) {
@ -526,8 +673,8 @@ u8 fuzz_one_original(afl_state_t *afl) {
* TRIMMING *
************/
if (!afl->non_instrumented_mode && !afl->queue_cur->trim_done &&
!afl->disable_trim) {
if (unlikely(!afl->non_instrumented_mode && !afl->queue_cur->trim_done &&
!afl->disable_trim && !afl->taint_needs_splode)) {
u8 res = trim_case(afl, afl->queue_cur, in_buf);
@ -564,13 +711,26 @@ u8 fuzz_one_original(afl_state_t *afl) {
if (afl->shm.cmplog_mode && !afl->queue_cur->fully_colorized) {
if (input_to_state_stage(afl, in_buf, out_buf, len,
afl->queue_cur->exec_cksum)) {
int res;
if (unlikely(afl->taint_needs_splode)) {
goto abandon_entry;
len = afl->queue_cur->len;
memcpy(out_buf, afl->taint_src, len);
res = input_to_state_stage(afl, afl->taint_src, out_buf, len,
afl->queue_cur->exec_cksum);
// just abandon as success
ret_val = 0;
res = 1;
} else {
res = input_to_state_stage(afl, in_buf, out_buf, len,
afl->queue_cur->exec_cksum);
}
if (unlikely(res)) { goto abandon_entry; }
}
/* Skip right away if -d is given, if it has not been chosen sufficiently
@ -2133,9 +2293,19 @@ havoc_stage:
if (actually_clone) {
if (unlikely(afl->taint_needs_splode)) {
clone_len = choose_block_len(afl, afl->queue_cur->len);
clone_from =
rand_below(afl, afl->queue_cur->len - clone_len + 1);
} else {
clone_len = choose_block_len(afl, temp_len);
clone_from = rand_below(afl, temp_len - clone_len + 1);
}
} else {
clone_len = choose_block_len(afl, HAVOC_BLK_XL);
@ -2156,6 +2326,10 @@ havoc_stage:
if (actually_clone) {
if (unlikely(afl->taint_needs_splode))
memcpy(new_buf + clone_to, afl->taint_src + clone_from,
clone_len);
else
memcpy(new_buf + clone_to, out_buf + clone_from, clone_len);
} else {
@ -2168,7 +2342,7 @@ havoc_stage:
}
/* Tail */
memcpy(new_buf + clone_to + clone_len, out_buf + clone_to,
memmove(new_buf + clone_to + clone_len, out_buf + clone_to,
temp_len - clone_to);
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
@ -2189,19 +2363,52 @@ havoc_stage:
if (temp_len < 2) { break; }
copy_len = choose_block_len(afl, temp_len - 1);
if (unlikely(afl->taint_needs_splode)) {
copy_len = choose_block_len(afl, afl->queue_cur->len - 1);
copy_from = rand_below(afl, afl->queue_cur->len - copy_len + 1);
copy_to = rand_below(afl, temp_len + 1);
} else {
copy_len = choose_block_len(afl, temp_len - 1);
copy_from = rand_below(afl, temp_len - copy_len + 1);
copy_to = rand_below(afl, temp_len - copy_len + 1);
}
if (rand_below(afl, 4)) {
if (copy_from != copy_to) {
if (unlikely(afl->taint_needs_splode)) {
if (temp_len >= (s32)(copy_to + copy_len)) {
memcpy(out_buf + copy_to, afl->taint_src + copy_from,
copy_len);
} else {
u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
copy_to + copy_len);
memcpy(new_buf, in_buf, copy_to);
memcpy(new_buf + copy_to, afl->taint_src + copy_from,
copy_len);
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
out_buf = new_buf;
temp_len = copy_to + copy_len;
}
} else {
memmove(out_buf + copy_to, out_buf + copy_from, copy_len);
}
}
} else {
memset(out_buf + copy_to,
@ -2464,10 +2671,17 @@ havoc_stage:
splices them together at some offset, then relies on the havoc
code to mutate that blob. */
u32 saved_len;
if (unlikely(afl->taint_needs_splode))
saved_len = afl->taint_len;
else
saved_len = afl->queue_cur->len;
retry_splicing:
if (afl->use_splicing && splice_cycle++ < SPLICE_CYCLES &&
afl->queued_paths > 1 && afl->queue_cur->len > 1) {
afl->queued_paths > 1 && saved_len > 1) {
struct queue_entry *target;
u32 tid, split_at;
@ -2480,7 +2694,7 @@ retry_splicing:
if (in_buf != orig_in) {
in_buf = orig_in;
len = afl->queue_cur->len;
len = saved_len;
}
@ -2551,6 +2765,8 @@ retry_splicing:
ret_val = 0;
goto abandon_entry;
/* we are through with this queue entry - for this iteration */
abandon_entry:
@ -2570,8 +2786,18 @@ abandon_entry:
++afl->queue_cur->fuzz_level;
if (unlikely(afl->taint_needs_splode)) {
munmap(afl->taint_src, afl->queue_cur->len);
munmap(orig_in, afl->taint_len);
munmap(afl->taint_map, afl->queue_cur->len);
} else {
munmap(orig_in, afl->queue_cur->len);
}
return ret_val;
#undef FLIP_BIT

View File

@ -103,6 +103,169 @@ void mark_as_redundant(afl_state_t *afl, struct queue_entry *q, u8 state) {
}
void perform_taint_run(afl_state_t *afl, struct queue_entry *q, u8 *fname,
u8 *mem, u32 len) {
u8 * ptr, *fn = fname;
u32 bytes = 0, plen = len;
struct queue_entry *prev = q->prev;
if (plen % 4) plen = plen + 4 - (len % 4);
if ((ptr = strrchr(fname, '/')) != NULL) fn = ptr + 1;
q->fname_taint = alloc_printf("%s/taint/%s", afl->out_dir, fn);
if (q->fname_taint) {
u8 *save = ck_maybe_grow(BUF_PARAMS(out_scratch), afl->fsrv.map_size);
memcpy(save, afl->taint_fsrv.trace_bits, afl->fsrv.map_size);
afl->taint_fsrv.map_size = plen; // speed :)
write_to_testcase(afl, mem, len);
if (afl_fsrv_run_target(&afl->taint_fsrv, afl->fsrv.exec_tmout * 4,
&afl->stop_soon) == 0) {
bytes = q->taint_bytes_all =
count_bytes_len(afl, afl->taint_fsrv.trace_bits, plen);
if (afl->debug)
fprintf(stderr, "Debug: tainted %u out of %u bytes\n", bytes, len);
/* DEBUG FIXME TODO XXX */
u32 i;
for (i = 0; i < len; i++) {
if (afl->taint_fsrv.trace_bits[i] &&
afl->taint_fsrv.trace_bits[i] != '!')
FATAL("invalid taint map value %02x at pos %d",
afl->taint_fsrv.trace_bits[i], i);
}
if (len < plen)
for (i = len; i < plen; i++) {
if (afl->taint_fsrv.trace_bits[i])
FATAL("invalid taint map value %02x in padding at pos %d",
afl->taint_fsrv.trace_bits[i], i);
}
}
// if all is tainted we do not need to write taint data away
if (bytes && bytes < len) {
// save the bytes away
int w = open(q->fname_taint, O_CREAT | O_WRONLY, 0644);
if (w >= 0) {
ck_write(w, afl->taint_fsrv.trace_bits, len, q->fname_taint);
close(w);
// find the highest tainted offset in the input (for trim opt)
s32 i = len;
while (i > 0 && !afl->taint_fsrv.trace_bits[i - 1])
i--;
q->taint_bytes_highest = i;
afl->taint_count++;
} else {
FATAL("could not create %s", q->fname_taint);
q->taint_bytes_all = bytes = 0;
}
// it is possible that there is no main taint file - if the whole file
// is tainted - but a .new taint file if it had new tainted bytes
// check if there is a previous queue entry and if it had taint
if (bytes && prev && prev->taint_bytes_all &&
prev->taint_bytes_all < prev->len) {
// check if there are new bytes in the taint vs the previous
int r = open(prev->fname_taint, O_RDONLY);
if (r >= 0) {
u8 *bufr = mmap(0, prev->len, PROT_READ, MAP_PRIVATE, r, 0);
if ((ssize_t)bufr != -1) {
u32 i;
u8 *tmp = ck_maybe_grow(BUF_PARAMS(in_scratch), plen);
memset(tmp, 0, plen);
for (i = 0; i < len; i++)
if (afl->taint_fsrv.trace_bits[i] && (i >= prev->len || !bufr[i]))
tmp[i] = '!';
q->taint_bytes_new = count_bytes_len(afl, tmp, plen);
if (afl->debug)
fprintf(stderr, "Debug: %u new taint out of %u bytes\n", bytes,
len);
if (q->taint_bytes_new) {
u8 *fnw = alloc_printf("%s.new", q->fname_taint);
if (fnw) {
int w = open(fnw, O_CREAT | O_WRONLY, 0644);
if (w >= 0) {
ck_write(w, tmp, plen, fnw);
close(w);
} else {
FATAL("count not create '%s'", fnw);
q->taint_bytes_new = 0;
}
ck_free(fnw);
} else {
q->taint_bytes_new = 0;
}
}
munmap(bufr, prev->len);
}
close(r);
}
}
}
memcpy(afl->taint_fsrv.trace_bits, save, afl->fsrv.map_size);
}
if (!bytes) {
q->taint_bytes_highest = q->taint_bytes_all = q->taint_bytes_new = 0;
if (q->fname_taint) {
ck_free(q->fname_taint);
q->fname_taint = NULL;
}
}
}
/* check if ascii or UTF-8 */
static u8 check_if_text(struct queue_entry *q) {
@ -212,10 +375,12 @@ static u8 check_if_text(struct queue_entry *q) {
/* Append new test case to the queue. */
void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
void add_to_queue(afl_state_t *afl, u8 *fname, u8 *mem, u32 len,
struct queue_entry *prev_q, u8 passed_det) {
struct queue_entry *q = ck_alloc(sizeof(struct queue_entry));
q->prev = prev_q;
q->fname = fname;
q->len = len;
q->depth = afl->cur_depth + 1;
@ -254,6 +419,13 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
afl->last_path_time = get_cur_time();
/* trigger the tain gathering if this is not a dry run */
if (afl->taint_mode && mem) { perform_taint_run(afl, q, fname, mem, len); }
/* only redqueen currently uses is_ascii */
if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q);
/* run custom mutators afl_custom_queue_new_entry() */
if (afl->custom_mutators_count) {
LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, {
@ -273,9 +445,6 @@ void add_to_queue(afl_state_t *afl, u8 *fname, u32 len, u8 passed_det) {
}
/* only redqueen currently uses is_ascii */
if (afl->shm.cmplog_mode) q->is_ascii = check_if_text(q);
}
/* Destroy the entire queue. */

View File

@ -350,7 +350,9 @@ u8 calibrate_case(afl_state_t *afl, struct queue_entry *q, u8 *use_mem,
}
if (q->exec_cksum) {
if (unlikely(afl->taint_mode))
q->exec_cksum = 0;
else if (q->exec_cksum) {
memcpy(afl->first_trace, afl->fsrv.trace_bits, afl->fsrv.map_size);
hnb = has_new_bits(afl, afl->virgin_bits);
@ -753,6 +755,10 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
while (remove_pos < q->len) {
u32 trim_avail = MIN(remove_len, q->len - remove_pos);
if (likely((!q->taint_bytes_highest) ||
(q->len - trim_avail > q->taint_bytes_highest))) {
u64 cksum;
write_with_gap(afl, in_buf, q->len, remove_pos, trim_avail);
@ -799,10 +805,15 @@ u8 trim_case(afl_state_t *afl, struct queue_entry *q, u8 *in_buf) {
}
/* Since this can be slow, update the screen every now and then. */
if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); }
++afl->stage_cur;
} else {
remove_pos += remove_len;
}
}
remove_len >>= 1;
@ -855,6 +866,8 @@ abort_trimming:
}
#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
/* Write a modified test case, run program, process results. Handle
error conditions, returning 1 if it's time to bail out. This is
a helper function for fuzz_one(). */
@ -864,6 +877,32 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
u8 fault;
if (unlikely(afl->taint_needs_splode)) {
s32 new_len = afl->queue_cur->len + len - afl->taint_len;
if (new_len < 4)
new_len = 4;
else if (new_len > MAX_FILE)
new_len = MAX_FILE;
u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch), new_len);
u32 i, taint = 0;
for (i = 0; i < (u32)new_len; i++) {
if (i >= afl->taint_len || i >= afl->queue_cur->len || afl->taint_map[i])
new_buf[i] = out_buf[taint++];
else
new_buf[i] = afl->taint_src[i];
}
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
out_buf = new_buf;
len = new_len;
}
write_to_testcase(afl, out_buf, len);
fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout);
@ -911,3 +950,5 @@ common_fuzz_stuff(afl_state_t *afl, u8 *out_buf, u32 len) {
}
#undef BUF_PARAMS

View File

@ -75,7 +75,7 @@ static list_t afl_states = {.element_prealloc_count = 0};
/* Initializes an afl_state_t. */
void afl_state_init(afl_state_t *afl, uint32_t map_size) {
void afl_state_init_1(afl_state_t *afl, uint32_t map_size) {
/* thanks to this memset, growing vars like out_buf
and out_size are NULL/0 by default. */
@ -100,16 +100,6 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
afl->cpu_aff = -1; /* Selected CPU core */
#endif /* HAVE_AFFINITY */
afl->virgin_bits = ck_alloc(map_size);
afl->virgin_tmout = ck_alloc(map_size);
afl->virgin_crash = ck_alloc(map_size);
afl->var_bytes = ck_alloc(map_size);
afl->top_rated = ck_alloc(map_size * sizeof(void *));
afl->clean_trace = ck_alloc(map_size);
afl->clean_trace_custom = ck_alloc(map_size);
afl->first_trace = ck_alloc(map_size);
afl->map_tmp_buf = ck_alloc(map_size);
afl->fsrv.use_stdin = 1;
afl->fsrv.map_size = map_size;
// afl_state_t is not available in forkserver.c
@ -161,6 +151,24 @@ void afl_state_init(afl_state_t *afl, uint32_t map_size) {
}
void afl_state_init_2(afl_state_t *afl, uint32_t map_size) {
afl->shm.map_size = map_size ? map_size : MAP_SIZE;
afl->virgin_bits = ck_alloc(map_size);
afl->virgin_tmout = ck_alloc(map_size);
afl->virgin_crash = ck_alloc(map_size);
afl->var_bytes = ck_alloc(map_size);
afl->top_rated = ck_alloc(map_size * sizeof(void *));
afl->clean_trace = ck_alloc(map_size);
afl->clean_trace_custom = ck_alloc(map_size);
afl->first_trace = ck_alloc(map_size);
afl->map_tmp_buf = ck_alloc(map_size);
afl->fsrv.map_size = map_size;
}
/*This sets up the environment variables for afl-fuzz into the afl_state
* struct*/

View File

@ -116,6 +116,7 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
"edges_found : %u\n"
"var_byte_count : %u\n"
"havoc_expansion : %u\n"
"tainted_inputs : %u\n"
"afl_banner : %s\n"
"afl_version : " VERSION
"\n"
@ -149,8 +150,8 @@ void write_stats_file(afl_state_t *afl, double bitmap_cvg, double stability,
#else
-1,
#endif
t_bytes, afl->var_byte_count, afl->expand_havoc, afl->use_banner,
afl->unicorn_mode ? "unicorn" : "",
t_bytes, afl->var_byte_count, afl->expand_havoc, afl->taint_count,
afl->use_banner, afl->unicorn_mode ? "unicorn" : "",
afl->fsrv.qemu_mode ? "qemu " : "",
afl->non_instrumented_mode ? " non_instrumented " : "",
afl->no_forkserver ? "no_fsrv " : "", afl->crash_mode ? "crash " : "",

View File

@ -53,6 +53,9 @@ static void at_exit() {
ptr = getenv("__AFL_TARGET_PID2");
if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL);
ptr = getenv("__AFL_TARGET_PID3");
if (ptr && *ptr && (i = atoi(ptr)) > 0) kill(i, SIGKILL);
i = 0;
while (list[i] != NULL) {
@ -89,6 +92,8 @@ static void usage(u8 *argv0, int more_help) {
" -o dir - output directory for fuzzer findings\n\n"
"Execution control settings:\n"
" -A - use first level taint analysis (see "
"qemu_taint/README.md)\n"
" -p schedule - power schedules compute a seed's performance score. "
"<explore\n"
" (default), fast, coe, lin, quad, exploit, mmopt, "
@ -241,7 +246,8 @@ int main(int argc, char **argv_orig, char **envp) {
s32 opt;
u64 prev_queued = 0;
u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE;
u32 sync_interval_cnt = 0, seek_to, show_help = 0, map_size = MAP_SIZE,
real_map_size = 0;
u8 * extras_dir = 0;
u8 mem_limit_given = 0, exit_1 = 0, debug = 0;
char **use_argv;
@ -257,7 +263,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (get_afl_env("AFL_DEBUG")) { debug = afl->debug = 1; }
map_size = get_map_size();
afl_state_init(afl, map_size);
afl_state_init_1(afl, map_size);
afl->debug = debug;
afl_fsrv_init(&afl->fsrv);
@ -277,10 +283,15 @@ int main(int argc, char **argv_orig, char **envp) {
while ((opt = getopt(
argc, argv,
"+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QNUWe:p:s:V:E:L:hRP:")) > 0) {
"+b:c:i:I:o:f:F:m:t:T:dDnCB:S:M:x:QANUWe:p:s:V:E:L:hRP:")) > 0) {
switch (opt) {
case 'A':
afl->taint_mode = 1;
if (!mem_limit_given) { afl->fsrv.mem_limit = MEM_LIMIT_QEMU; }
break;
case 'I':
afl->infoexec = optarg;
break;
@ -488,7 +499,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (!optarg) { FATAL("Wrong usage of -m"); }
if (!strcmp(optarg, "none")) {
if (!strcmp(optarg, "none") || !strcmp(optarg, "0")) {
afl->fsrv.mem_limit = 0;
break;
@ -818,6 +829,15 @@ int main(int argc, char **argv_orig, char **envp) {
}
if (afl->taint_mode && afl->fsrv.map_size < MAX_FILE) {
real_map_size = map_size;
map_size = MAX_FILE;
}
afl_state_init_2(afl, map_size);
if (!mem_limit_given && afl->shm.cmplog_mode) afl->fsrv.mem_limit += 260;
OKF("afl++ is maintained by Marc \"van Hauser\" Heuse, Heiko \"hexcoder\" "
@ -825,8 +845,7 @@ int main(int argc, char **argv_orig, char **envp) {
OKF("afl++ is open source, get it at "
"https://github.com/AFLplusplus/AFLplusplus");
OKF("Power schedules from github.com/mboehme/aflfast");
OKF("Python Mutator and llvm_mode instrument file list from "
"github.com/choller/afl");
OKF("Python Mutator from github.com/choller/afl");
OKF("MOpt Mutator from github.com/puppet-meteor/MOpt-AFL");
if (afl->sync_id && afl->is_main_node &&
@ -872,6 +891,19 @@ int main(int argc, char **argv_orig, char **envp) {
if (afl->crash_mode) { FATAL("-C and -n are mutually exclusive"); }
if (afl->fsrv.qemu_mode) { FATAL("-Q and -n are mutually exclusive"); }
if (afl->unicorn_mode) { FATAL("-U and -n are mutually exclusive"); }
if (afl->taint_mode) { FATAL("-A and -n are mutually exclusive"); }
}
if (afl->limit_time_sig != 0 && afl->taint_mode) {
FATAL("-A and -L are mutually exclusive");
}
if (afl->unicorn_mode != 0 && afl->taint_mode) {
FATAL("-A and -U are mutually exclusive");
}
@ -972,7 +1004,7 @@ int main(int argc, char **argv_orig, char **envp) {
if (afl->afl_env.afl_preload) {
if (afl->fsrv.qemu_mode) {
if (afl->fsrv.qemu_mode || afl->taint_mode) {
u8 *qemu_preload = getenv("QEMU_SET_ENV");
u8 *afl_preload = getenv("AFL_PRELOAD");
@ -1068,6 +1100,13 @@ int main(int argc, char **argv_orig, char **envp) {
afl->fsrv.trace_bits =
afl_shm_init(&afl->shm, afl->fsrv.map_size, afl->non_instrumented_mode);
if (real_map_size && map_size != real_map_size) {
afl->fsrv.map_size = real_map_size;
if (afl->cmplog_binary) afl->cmplog_fsrv.map_size = real_map_size;
}
if (!afl->in_bitmap) { memset(afl->virgin_bits, 255, afl->fsrv.map_size); }
memset(afl->virgin_tmout, 255, afl->fsrv.map_size);
memset(afl->virgin_crash, 255, afl->fsrv.map_size);
@ -1223,7 +1262,6 @@ int main(int argc, char **argv_orig, char **envp) {
ACTF("Spawning cmplog forkserver");
afl_fsrv_init_dup(&afl->cmplog_fsrv, &afl->fsrv);
// TODO: this is semi-nice
afl->cmplog_fsrv.trace_bits = afl->fsrv.trace_bits;
afl->cmplog_fsrv.qemu_mode = afl->fsrv.qemu_mode;
afl->cmplog_fsrv.cmplog_binary = afl->cmplog_binary;
@ -1234,6 +1272,70 @@ int main(int argc, char **argv_orig, char **envp) {
}
if (afl->taint_mode) {
ACTF("Spawning qemu_taint forkserver");
u8 *disable = getenv("AFL_DISABLE_LLVM_INSTRUMENTATION");
setenv("AFL_DISABLE_LLVM_INSTRUMENTATION", "1", 0);
afl_fsrv_init_dup(&afl->taint_fsrv, &afl->fsrv);
afl->taint_fsrv.taint_mode = 1;
afl->taint_fsrv.trace_bits = afl->fsrv.trace_bits;
ck_free(afl->taint_fsrv.target_path);
afl->argv_taint = ck_alloc(sizeof(char *) * (argc + 4 - optind));
afl->taint_fsrv.target_path = find_afl_binary("afl-qemu-taint", argv[0]);
afl->argv_taint[0] = find_afl_binary("afl-qemu-taint", argv[0]);
if (!afl->argv_taint[0])
FATAL(
"Cannot find 'afl-qemu-taint', read qemu_taint/README.md on how to "
"build it.");
u32 idx = optind - 1, offset = 0;
do {
idx++;
offset++;
afl->argv_taint[offset] = argv[idx];
} while (argv[idx] != NULL);
if (afl->fsrv.use_stdin)
unsetenv("AFL_TAINT_INPUT");
else
setenv("AFL_TAINT_INPUT", afl->fsrv.out_file, 1);
afl_fsrv_start(&afl->taint_fsrv, afl->argv_taint, &afl->stop_soon,
afl->afl_env.afl_debug_child_output);
afl->taint_input_file = alloc_printf("%s/taint/.input", afl->out_dir);
int fd = open(afl->taint_input_file, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (fd < 0)
FATAL("Cannot create taint inpu file '%s'", afl->taint_input_file);
lseek(fd, MAX_FILE, SEEK_SET);
ck_write(fd, "\0", 1, afl->taint_input_file);
if (!disable) unsetenv("AFL_DISABLE_LLVM_INSTRUMENTATION");
OKF("Taint forkserver successfully started");
const rlim_t kStackSize = 128L * 1024L * 1024L; // min stack size = 128 Mb
struct rlimit rl;
rl.rlim_cur = kStackSize;
if (getrlimit(RLIMIT_STACK, &rl) != 0)
WARNF("Setting a higher stack size failed!");
#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
u8 *tmp1 = ck_maybe_grow(BUF_PARAMS(eff), MAX_FILE + 4096);
u8 *tmp2 = ck_maybe_grow(BUF_PARAMS(ex), MAX_FILE + 4096);
u8 *tmp3 = ck_maybe_grow(BUF_PARAMS(in_scratch), MAX_FILE + 4096);
u8 *tmp4 = ck_maybe_grow(BUF_PARAMS(out), MAX_FILE + 4096);
u8 *tmp5 = ck_maybe_grow(BUF_PARAMS(out_scratch), MAX_FILE + 4096);
#undef BUF_PARAMS
if (!tmp1 || !tmp2 || !tmp3 || !tmp4 || !tmp5)
FATAL("memory issues. me hungry, feed me!");
}
perform_dry_run(afl);
cull_queue(afl);
@ -1308,7 +1410,7 @@ int main(int argc, char **argv_orig, char **envp) {
break;
case 1:
if (afl->limit_time_sig == 0 && !afl->custom_only &&
!afl->python_only) {
!afl->python_only && !afl->taint_mode) {
afl->limit_time_sig = -1;
afl->limit_time_puppet = 0;
@ -1496,8 +1598,11 @@ stop_fuzzing:
}
if (afl->cmplog_binary) afl_fsrv_deinit(&afl->cmplog_fsrv);
if (afl->taint_mode) afl_fsrv_deinit(&afl->taint_fsrv);
afl_fsrv_deinit(&afl->fsrv);
if (afl->orig_cmdline) { ck_free(afl->orig_cmdline); }
if (afl->argv_taint) { ck_free(afl->argv_taint); }
ck_free(afl->fsrv.target_path);
ck_free(afl->fsrv.out_file);
ck_free(afl->sync_id);