From dd7476a9bdcf06ccc2d11f49d66ce9edf09612d7 Mon Sep 17 00:00:00 2001 From: lazymio Date: Mon, 25 Oct 2021 00:51:16 +0200 Subject: [PATCH] Initial import unicornafl --- CMakeLists.txt | 6 + include/afl/afl-common.h | 49 +++ include/afl/afl-cpu-inl.h | 521 ++++++++++++++++++++++++++++ include/afl/afl-cpu-translate-inl.h | 81 +++++ include/afl/afl-tcg-op-inl.h | 76 ++++ include/afl/afl-tcg-runtime-inl.h | 123 +++++++ include/afl/config.h | 452 ++++++++++++++++++++++++ include/afl/types.h | 193 +++++++++++ include/uc_priv.h | 44 ++- include/unicorn/unicorn.h | 81 +++++ qemu/aarch64.h | 5 + qemu/aarch64eb.h | 5 + qemu/accel/tcg/cpu-exec.c | 37 ++ qemu/accel/tcg/tcg-runtime.c | 4 + qemu/accel/tcg/tcg-runtime.h | 7 + qemu/accel/tcg/translator.c | 30 ++ qemu/arm.h | 5 + qemu/armeb.h | 5 + qemu/include/tcg/tcg-op.h | 4 + qemu/m68k.h | 5 + qemu/mips.h | 5 + qemu/mips64.h | 5 + qemu/mips64el.h | 5 + qemu/mipsel.h | 5 + qemu/ppc.h | 5 + qemu/ppc64.h | 5 + qemu/riscv32.h | 5 + qemu/riscv64.h | 5 + qemu/softmmu/cpus.c | 15 + qemu/sparc.h | 5 + qemu/sparc64.h | 5 + qemu/target/arm/translate-a64.c | 20 ++ qemu/target/arm/translate.c | 15 + qemu/target/i386/translate.c | 19 + qemu/target/m68k/translate.c | 15 + qemu/target/mips/translate.c | 16 + qemu/target/ppc/translate.c | 15 + qemu/target/riscv/translate.c | 16 + qemu/target/sparc/translate.c | 18 + qemu/x86_64.h | 5 + symbols.sh | 5 + uc.c | 328 +++++++++++++++++ 42 files changed, 2269 insertions(+), 1 deletion(-) create mode 100644 include/afl/afl-common.h create mode 100644 include/afl/afl-cpu-inl.h create mode 100644 include/afl/afl-cpu-translate-inl.h create mode 100644 include/afl/afl-tcg-op-inl.h create mode 100644 include/afl/afl-tcg-runtime-inl.h create mode 100644 include/afl/config.h create mode 100644 include/afl/types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eca217e..69af3b8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,8 @@ else() include_directories( ${CMAKE_BINARY_DIR} ) + # Try to build AFL support on all other platform. + set(UNICORN_HAS_AFL TRUE) endif() include_directories( @@ -326,6 +328,10 @@ else() OUTPUT_FILE ${CMAKE_BINARY_DIR}/riscv64-softmmu/config-target.h ) endif() + if (UNICORN_HAS_AFL) + # Make it globally + set (UNICORN_CFLAGS ${UNICORN_CFLAGS} -DUNICORN_HAS_AFL) + endif() add_compile_options( ${UNICORN_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/tcg/${UNICORN_TARGET_ARCH} diff --git a/include/afl/afl-common.h b/include/afl/afl-common.h new file mode 100644 index 00000000..eb342255 --- /dev/null +++ b/include/afl/afl-common.h @@ -0,0 +1,49 @@ +/* + american fuzzy lop++ - unicorn instrumentation + ---------------------------------------------- + + Originally written by Andrew Griffiths and + Michal Zalewski + + Adapted for afl-unicorn by Dominik Maier + + CompareCoverage and NeverZero counters by Andrea Fioraldi + + + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. + Copyright 2019 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This code is a shim patched into the separately-distributed source + code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality + to implement AFL-style instrumentation and to take care of the remaining + parts of the AFL fork server logic. + + The resulting libunicorn binary is essentially a standalone instrumentation + tool; for an example of how to leverage it for other purposes, you can + have a look at afl-showmap.c. + + */ + +#include "config.h" +#include "types.h" + +/* NeverZero */ + +#if (defined(__x86_64__) || defined(__i386__)) && defined(AFL_QEMU_NOT_ZERO) +#define INC_AFL_AREA(loc) \ + asm volatile( \ + "addb $1, (%0, %1, 1)\n" \ + "adcb $0, (%0, %1, 1)\n" \ + : /* no out */ \ + : "r"(afl_area_ptr), "r"(loc) \ + : "memory", "eax") +#else +#define INC_AFL_AREA(loc) afl_area_ptr[loc]++ +#endif + diff --git a/include/afl/afl-cpu-inl.h b/include/afl/afl-cpu-inl.h new file mode 100644 index 00000000..5a781e6f --- /dev/null +++ b/include/afl/afl-cpu-inl.h @@ -0,0 +1,521 @@ +/* + american fuzzy lop++ - unicorn instrumentation + ---------------------------------------------- + + Originally written by Andrew Griffiths and + Michal Zalewski + + Adapted for afl-unicorn by Dominik Maier + + CompareCoverage and NeverZero counters by Andrea Fioraldi + + + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. + Copyright 2019 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This code is a shim patched into the separately-distributed source + code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality + to implement AFL-style instrumentation and to take care of the remaining + parts of the AFL fork server logic. + + The resulting libunicorn binary is essentially a standalone instrumentation + tool; for an example of how to leverage it for other purposes, you can + have a look at afl-showmap.c. + + */ + +#include +#include +#include +#include +#include "config.h" +#include "types.h" +#include "afl-common.h" + +/* We use one additional file descriptor to relay "needs translation" + or "child done" messages between the child and the fork server. */ + +#define FF16 (0xFFFFFFFFFFFFFFFF) + +/* Copied from aflpp/types.h to talk to forkserver */ +#define FS_OPT_ENABLED 0x80000001 +#define FS_OPT_SHDMEM_FUZZ 0x01000000 + +/** + * The correct fds for reading and writing pipes + */ + +#define _R(pipe) ((pipe)[0]) +#define _W(pipe) ((pipe)[1]) + +/* Function declarations. */ + +static void afl_setup(struct uc_struct*); +static inline uc_afl_ret afl_forkserver(CPUState*); +static int afl_find_wifsignaled_id(void); + +static enum afl_child_ret afl_handle_child_requests(CPUState*); +static void afl_request_tsl(CPUState *cpu, target_ulong, target_ulong, uint64_t, uint32_t); +static uc_afl_ret afl_request_next(struct uc_struct* uc, bool found_crash); + +// static TranslationBlock* tb_find_slow(CPUArchState*, target_ulong, target_ulong, uint64_t); + +/* Data structure passed around by the translate handlers: */ + +struct afl_tsl { + + target_ulong pc; + target_ulong cs_base; + uint64_t flags; + uint32_t cf_mask; +#if defined(TARGET_MIPS) + TCGv_i32 hflags; + TCGv_i32 btarget; +#endif + +}; + +/* Current state, as forwarded from forkserver child to parent */ + +enum afl_child_ret { + + // Persistent + AFL_CHILD_NEXT, + // Crash discovered but still alive in persistent mode + AFL_CHILD_FOUND_CRASH, + // Read again, one afl_tsl struct. + AFL_CHILD_TSL_REQUEST, + // Child no longer there. Read status code. + AFL_CHILD_EXITED, + +}; + +static int wifsignaled; + +/************************* + * ACTUAL IMPLEMENTATION * + *************************/ + +/* Set up SHM region and initialize other stuff. */ + +static void afl_setup(struct uc_struct* uc) { + + char *id_str = getenv(SHM_ENV_VAR); + char *inst_r = getenv("AFL_INST_RATIO"); + + // A value we can use to tell AFL our persistent mode found a crash + wifsignaled = afl_find_wifsignaled_id(); + + int shm_id; + + if (inst_r) { + + unsigned int r; + + r = atoi(inst_r); + + if (r > 100) r = 100; + if (!r) r = 1; + + uc->afl_inst_rms = MAP_SIZE * r / 100; + + } else { + + uc->afl_inst_rms = MAP_SIZE; + + } + + if (id_str) { + + shm_id = atoi(id_str); + uc->afl_area_ptr = shmat(shm_id, NULL, 0); + uc->afl_prev_loc = 0; + uc->afl_area_ptr[0] = 1; + + if (uc->afl_area_ptr == (void*)-1) exit(1); + + } + + /* Maintain for compatibility */ + if (getenv("AFL_QEMU_COMPCOV")) { uc->afl_compcov_level = 1; } + if (getenv("AFL_COMPCOV_LEVEL")) { + + uc->afl_compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL")); + + } +#if defined(AFL_DEBUG) + if (uc->afl_compcov_level) { + printf("[d] USING AFL_COMPCOV_LEVEL %d\n", uc->afl_compcov_level); + } +#endif + +} + +// Some dirty hack to come up with a valid statuscode that AFL will just accept. + +static int afl_find_wifsignaled_id(void) { + + int ret = 0; // A faux status code that AFL will accept as signaled/crashed. 1 on linux. + while (!(WIFSIGNALED(ret))) ret++; + +#if defined(AFL_DEBUG) + printf("[d] wifsignaled is %d (WIFSIGNALED(x)=%d)\n", ret, WIFSIGNALED(ret)); +#endif + + return ret; + +} + +/* Fork server logic, invoked by calling uc_afl_forkserver_start. + Roughly follows https://github.com/vanhauser-thc/AFLplusplus/blob/c83e8e1e6255374b085292ba8673efdca7388d76/llvm_mode/afl-llvm-rt.o.c#L130 + */ + +static inline uc_afl_ret afl_forkserver(CPUState* cpu) { + + unsigned char tmp[4] = {0}; + pid_t child_pid; + enum afl_child_ret child_ret = AFL_CHILD_EXITED; + bool first_round = true; + u32 status = 0; + + if (!cpu->uc->afl_area_ptr) return UC_AFL_RET_NO_AFL; + + if (cpu->uc->afl_testcase_ptr) { + /* Parent supports testcases via shared map - and the user wants to use it. Tell AFL. */ + status = (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ); + } + + /* Phone home and tell the parent that we're OK. If parent isn't there, + assume we're not running in forkserver mode and just execute program. */ + + if (write(FORKSRV_FD + 1, &status, 4) != 4) return UC_AFL_RET_NO_AFL; + + /* afl tells us in an extra message if it accepted this option or not */ + if (cpu->uc->afl_testcase_ptr && getenv(SHM_FUZZ_ENV_VAR)) { + if (read(FORKSRV_FD, &status, 4) != 4) { + fprintf(stderr, "[!] AFL parent exited before forkserver was up\n"); + return UC_AFL_RET_ERROR; + } + if (status != (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { + fprintf(stderr, "[!] Unexpected response from AFL++ on forkserver setup\n"); + return UC_AFL_RET_ERROR; + } + } else { +#if defined(AFL_DEBUG) + printf("[d] AFL++ sharedmap fuzzing not supported/SHM_FUZZ_ENV_VAR not set\n"); +#endif + } + + void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); + +#if defined(AFL_DEBUG) + printf("[d] Entering forkserver loop\n"); +#endif + + while (1) { + + uint32_t was_killed; + int status; + + /* Wait for parent by reading from the pipe. Abort if read fails. */ + + if (read(FORKSRV_FD, &was_killed, 4) != 4) return UC_AFL_RET_FINISHED; + + /* If we stopped the child in persistent mode, but there was a race + condition and afl-fuzz already issued SIGKILL, write off the old + process. */ + + if ((child_ret != AFL_CHILD_EXITED) && was_killed) { + +#if defined(AFL_DEBUG) + printf("[d] Child was killed by AFL in the meantime.\n"); +#endif + + child_ret = AFL_CHILD_EXITED; + if (waitpid(child_pid, &status, 0) < 0) { + perror("[!] Error waiting for child!"); + return UC_AFL_RET_ERROR; + } + + } + + if (child_ret == AFL_CHILD_EXITED) { + + /* Child dead. Establish new a channel with child to grab translation commands. + We'll read from _R(afl_child_pipe), child will write to _W(afl_child_pipe). */ + + /* close the read fd of previous round. */ + + if (_R(cpu->uc->afl_child_pipe)) { + close(_R(cpu->uc->afl_child_pipe)); + close(_W(cpu->uc->afl_parent_pipe)); + } + + if (pipe(cpu->uc->afl_child_pipe)) { + perror("[!] Error creating pipe to child"); + return UC_AFL_RET_ERROR; + } + if (pipe(cpu->uc->afl_parent_pipe)) { + perror("[!] Error creating pipe to parent"); + close(_R(cpu->uc->afl_child_pipe)); + close(_W(cpu->uc->afl_child_pipe)); + return UC_AFL_RET_ERROR; + } + + /* Create a clone of our process. */ + + child_pid = fork(); + if (child_pid < 0) { + perror("[!] Could not fork! "); + return UC_AFL_RET_ERROR; + } + + /* In child process: close fds, resume execution. */ + + if (!child_pid) { // New child + + signal(SIGCHLD, old_sigchld_handler); + // FORKSRV_FD is for communication with AFL, we don't need it in the child. + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + close(_R(cpu->uc->afl_child_pipe)); + close(_W(cpu->uc->afl_parent_pipe)); + cpu->uc->afl_child_request_next = afl_request_next; + + memset(cpu->uc->afl_area_ptr, 0, MAP_SIZE); + MEM_BARRIER(); // Make very sure everything has been written to the map at this point + + if (!first_round) { + + // For persistent mode: Clear the map manually after forks. + memset(cpu->uc->afl_area_ptr, 0, MAP_SIZE); + + } else { + // For persistent mode: Clear the map manually after forks. + //memset(env->uc->afl_area_ptr, 0, MAP_SIZE); + + first_round = false; + } + + cpu->uc->afl_prev_loc = 0; + // Tell AFL we're alive + cpu->uc->afl_area_ptr[0] = 1; + + return UC_AFL_RET_CHILD; + + } else { // parent for new child + + /* If we don't close this in parent, we don't get notified on afl_child_pipe once child is gone. */ + + close(_W(cpu->uc->afl_child_pipe)); + close(_R(cpu->uc->afl_parent_pipe)); + + } + + } else { // parent, in persistent mode + + /* Special handling for persistent mode: if the child is alive but + currently stopped, simply restart it with a write to afl_parent_pipe. + In case we fuzz using shared map, use this method to forward the size + of the current testcase to the child without cost. */ + + if (write(_W(cpu->uc->afl_parent_pipe), tmp, 4) != 4) { + + fprintf(stderr,"[!] Child died when we tried to resume it\n"); + return UC_AFL_RET_ERROR; + + } + + } + + /* In parent process: write PID to AFL. */ + + if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) { + return UC_AFL_RET_FINISHED; + } + + /* Collect translation requests until child finishes a run or dies */ + + child_ret = afl_handle_child_requests(cpu); + + if (child_ret == AFL_CHILD_NEXT) { + + /* Child asks for next in persistent mode */ + + status = 0; + + } else if (child_ret == AFL_CHILD_FOUND_CRASH) { + + /* WIFSIGNALED(wifsignaled) == 1 -> tells AFL the child crashed (even though it's still alive for persistent mode) */ + + status = wifsignaled; + + } else if (child_ret == AFL_CHILD_EXITED) { + + /* If child exited, get and relay exit status to parent through waitpid. */ + + if (waitpid(child_pid, &status, 0) < 0) { + + // Zombie Child could not be collected. Scary! + perror("[!] The child's exit code could not be determined. "); + return UC_AFL_RET_ERROR; + + } + + } + + /* Relay wait status to AFL pipe, then loop back. */ + + if (write(FORKSRV_FD + 1, &status, 4) != 4) return UC_AFL_RET_FINISHED; + + } + +} + +/* This code is invoked whenever Unicorn decides that it doesn't have a + translation of a particular block and needs to compute it. When this happens, + we tell the parent to mirror the operation, so that the next fork() has a + cached copy. */ + +static inline void afl_request_tsl(CPUState *cpu, target_ulong pc, target_ulong cb, uint64_t flags, uint32_t cf_mask) { + + /* Dual use: if this func is not set, we're not a child process */ + + struct uc_struct* uc = cpu->uc; + if (uc->afl_child_request_next == NULL) return; + enum afl_child_ret tsl_req = AFL_CHILD_TSL_REQUEST; + + struct afl_tsl t = { + .pc = pc, + .cs_base = cb, + .flags = flags, + .cf_mask = cf_mask, +#if defined(TARGET_MIPS) + .hflags = cpu->uc->tcg_ctx->hflags, + .btarget = cpu->uc->tcg_ctx->btarget, +#endif + }; + +#if defined(AFL_DEBUG) + printf("Requesting tsl, pc=0x%llx, cb=0x%llx, flags=0x%llx\n", (unsigned long long) pc, (unsigned long long) cb, (unsigned long long) flags); +#endif + + // We write tsl requests in two steps but that's fine since cache requests are not very common over the time of fuzzing. + + if ((write(_W(uc->afl_child_pipe), &tsl_req, sizeof(enum afl_child_ret)) != sizeof(enum afl_child_ret)) + || write(_W(uc->afl_child_pipe), &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) { + + fprintf(stderr, "Error writing to child pipe. Parent dead?\n"); + + } + +} + +/* This code is invoked whenever the child decides that it is done with one fuzz-case. */ + +static uc_afl_ret afl_request_next(struct uc_struct* uc, bool crash_found) { + + enum afl_child_ret msg = crash_found? AFL_CHILD_FOUND_CRASH : AFL_CHILD_NEXT; + char tmp[4]; + +#if defined(AFL_DEBUG) + printf("[d] request next. crash found: %s\n", crash_found ? "true": "false"); +#endif + + MEM_BARRIER(); // Make very sure everything has been written to the map at this point + + if (write(_W(uc->afl_child_pipe), &msg, sizeof(msg)) != sizeof(msg)) { + + fprintf(stderr, "[!] Error writing to parent pipe. Parent dead?\n"); + return UC_AFL_RET_ERROR; + + } + + // Once the parent has written something, the next persistent loop starts. + // The parent itself will wait for AFL to signal the new testcases is available. + // This blocks until the next testcase is ready. + if (read(_R(uc->afl_parent_pipe), tmp, 4) != 4) { + + fprintf(stderr, "[!] Error reading from parent pipe. Parent dead?\n"); + return UC_AFL_RET_ERROR; + + } + + /* For shared map fuzzing, the forkserver parent forwards the size of the current testcase. */ + memset(uc->afl_area_ptr, 0, MAP_SIZE); + MEM_BARRIER(); // Also make sure nothing read before this point. + + // Start with a clean slate. + uc->afl_prev_loc = 0; + uc->afl_area_ptr[0] = 1; + + return UC_AFL_RET_CHILD; + +} + + +/* This is the reading side of afl_child_pipe. It will handle persistent mode and (tsl) cache requests. + Since timeouts are handled by afl-fuzz simply killing the child, we can just wait until the pipe breaks. + For persistent mode, we will also receive child responses over this chanel. + For persistent mode, if child is still alive, this will return if the child crashed or not */ + +static enum afl_child_ret afl_handle_child_requests(CPUState* cpu) { + + enum afl_child_ret child_msg; + struct afl_tsl t; + + while (1) { + + /* Broken pipe means it's time to return to the fork server routine. */ + + if (read(_R(cpu->uc->afl_child_pipe), &child_msg, sizeof(enum afl_child_ret)) != sizeof(enum afl_child_ret)) return AFL_CHILD_EXITED; // child is dead. + + if (child_msg == AFL_CHILD_NEXT || child_msg == AFL_CHILD_FOUND_CRASH) { + + // Forward if child found a crash or not, for persistent mode. + return child_msg; + + } else if (child_msg == AFL_CHILD_TSL_REQUEST) { + + // TODO: Add option to disable cache for self-modifying code? // Ignore code that has not been loaded? + + // Child will send a tsl request next, that we have to cache. + if (read(_R(cpu->uc->afl_child_pipe), &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) return AFL_CHILD_EXITED; // child is dead. + + // Prepare hflags for delay slot +#if defined(TARGET_MIPS) + struct afl_tsl tmp; + tmp.hflags = cpu->uc->tcg_ctx->hflags; + tmp.btarget = cpu->uc->tcg_ctx->btarget; + cpu->uc->tcg_ctx->hflags = t.hflags; + cpu->uc->tcg_ctx->btarget = t.btarget; +#endif + + // Cache. + //tb_find_slow(env, t.pc, t.cs_base, t.flags); + + // TODO: Caching! tb = tb_find(cpu, last_tb, tb_exit, t.cflags); + + // Restore hflags +#if defined(TARGET_MIPS) + cpu->uc->tcg_ctx->hflags = tmp.hflags; + cpu->uc->tcg_ctx->btarget = tmp.btarget; +#endif + + } else { + + fprintf(stderr, "[!] Unexpected response by child! %d. Please report this as bug for unicornafl.\n" + " Expected one of {AFL_CHILD_NEXT: %d, AFL_CHILD_FOUND_CRASH: %d, AFL_CHILD_TSL_REQUEST: %d}.\n", + child_msg, AFL_CHILD_NEXT, AFL_CHILD_FOUND_CRASH, AFL_CHILD_TSL_REQUEST); + + } + + } + +} diff --git a/include/afl/afl-cpu-translate-inl.h b/include/afl/afl-cpu-translate-inl.h new file mode 100644 index 00000000..7692ff3c --- /dev/null +++ b/include/afl/afl-cpu-translate-inl.h @@ -0,0 +1,81 @@ +/* + american fuzzy lop++ - unicorn instrumentation + ---------------------------------------------- + + Originally written by Andrew Griffiths and + Michal Zalewski + + Adapted for afl-unicorn by Dominik Maier + + CompareCoverage and NeverZero counters by Andrea Fioraldi + + + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. + Copyright 2019 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This code is a shim patched into the separately-distributed source + code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality + to implement AFL-style instrumentation and to take care of the remaining + parts of the AFL fork server logic. + + The resulting libunicorn binary is essentially a standalone instrumentation + tool; for an example of how to leverage it for other purposes, you can + have a look at afl-showmap.c. + + */ + +#include "config.h" +#include "types.h" + +/* These are executed on code generation. Execution is in afl-unicorn-tcg-runtime-inl.h */ +/* Roughly afl_gen_maybe_log -> gen_afl_maybe_log -> emit HELPER(afl_maybe_log) -> call afl_maybe_log */ + +static void afl_gen_maybe_log(TCGContext *s, uint64_t cur_loc) { + + if (!s->uc->afl_area_ptr) return; + + /* "Hash" */ + + cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); + cur_loc &= MAP_SIZE - 7; + + /* Implement probabilistic instrumentation by looking at scrambled block + address. This keeps the instrumented locations stable across runs. */ + + if (cur_loc >= s->uc->afl_inst_rms) return; + + gen_afl_maybe_log(s, cur_loc); + +} + +// Currently only arm32 and x86. We undefine it for others to silence unused func compiler warnings. +#if defined(ARCH_HAS_COMPCOV) +static void afl_gen_compcov(TCGContext *s, uint64_t cur_loc, TCGv arg1, + TCGv arg2, TCGMemOp ot, int is_imm) { + + if (!s->uc->afl_compcov_level || !s->uc->afl_area_ptr) return; + + if (!is_imm && s->uc->afl_compcov_level < 2) return; + + cur_loc = (cur_loc >> 4) ^ (cur_loc << 8); + cur_loc &= MAP_SIZE - 7; + + if (cur_loc >= s->uc->afl_inst_rms) return; + + switch (ot) { + + case MO_64: gen_afl_compcov_log_64(s, cur_loc, (TCGv_i64)arg1, (TCGv_i64)arg2); break; + case MO_32: gen_afl_compcov_log_32(s, cur_loc, (TCGv_i32)arg1, (TCGv_i32)arg2); break; + case MO_16: gen_afl_compcov_log_16(s, cur_loc, (TCGv_i32)arg1, (TCGv_i32)arg2); break; + default: return; + + } + +} +#endif \ No newline at end of file diff --git a/include/afl/afl-tcg-op-inl.h b/include/afl/afl-tcg-op-inl.h new file mode 100644 index 00000000..5770066c --- /dev/null +++ b/include/afl/afl-tcg-op-inl.h @@ -0,0 +1,76 @@ +/* + american fuzzy lop++ - unicorn instrumentation + ---------------------------------------------- + + Originally written by Andrew Griffiths and + Michal Zalewski + + Adapted for afl-unicorn by Dominik Maier + + CompareCoverage and NeverZero counters by Andrea Fioraldi + + + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. + Copyright 2019 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This code is a shim patched into the separately-distributed source + code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality + to implement AFL-style instrumentation and to take care of the remaining + parts of the AFL fork server logic. + + The resulting libunicorn binary is essentially a standalone instrumentation + tool; for an example of how to leverage it for other purposes, you can + have a look at afl-showmap.c. + + */ + +static inline void gen_afl_maybe_log(TCGContext *tcg_ctx, uint64_t cur_loc) { + + TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc); + TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc); + gen_helper_afl_maybe_log(tcg_ctx, tuc, tcur_loc); + +} + +static inline void gen_afl_compcov_log_16(TCGContext *tcg_ctx, uint64_t cur_loc, + TCGv_i32 arg1, TCGv_i32 arg2) { +#if defined(AFL_DEBUG) + printf("[d] Emitting 16 bit COMPCOV instrumentation for loc 0x%lx\n", cur_loc); +#endif + + TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc); + TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc); + gen_helper_afl_compcov_log_16(tcg_ctx, tuc, tcur_loc, arg1, arg2); + +} + +static inline void gen_afl_compcov_log_32(TCGContext *tcg_ctx, uint64_t cur_loc, + TCGv_i32 arg1, TCGv_i32 arg2) { +#if defined(AFL_DEBUG) + printf("[d] Emitting 32 bit COMPCOV instrumentation for loc 0x%lux\n", cur_loc); +#endif + + TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc); + TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc); + gen_helper_afl_compcov_log_32(tcg_ctx, tuc, tcur_loc, arg1, arg2); + +} + +static inline void gen_afl_compcov_log_64(TCGContext *tcg_ctx, uint64_t cur_loc, + TCGv_i64 arg1, TCGv_i64 arg2) { +#if defined(AFL_DEBUG) + printf("[d] Emitting 64 bit COMPCOV instrumentation for loc 0x%lux\n", cur_loc); +#endif + + TCGv_ptr tuc = tcg_const_ptr(tcg_ctx, tcg_ctx->uc); + TCGv_i64 tcur_loc = tcg_const_i64(tcg_ctx, cur_loc); + gen_helper_afl_compcov_log_64(tcg_ctx, tuc, tcur_loc, arg1, arg2); + +} + diff --git a/include/afl/afl-tcg-runtime-inl.h b/include/afl/afl-tcg-runtime-inl.h new file mode 100644 index 00000000..237c55b5 --- /dev/null +++ b/include/afl/afl-tcg-runtime-inl.h @@ -0,0 +1,123 @@ +/* + american fuzzy lop++ - unicorn instrumentation + ---------------------------------------------- + + Originally written by Andrew Griffiths and + Michal Zalewski + + Adapted for afl-unicorn by Dominik Maier + + CompareCoverage and NeverZero counters by Andrea Fioraldi + + + Copyright 2015, 2016, 2017 Google Inc. All rights reserved. + Copyright 2019 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This code is a shim patched into the separately-distributed source + code of Unicorn 1.0.1. It leverages the built-in QEMU tracing functionality + to implement AFL-style instrumentation and to take care of the remaining + parts of the AFL fork server logic. + + The resulting libunicorn binary is essentially a standalone instrumentation + tool; for an example of how to leverage it for other purposes, you can + have a look at afl-showmap.c. + + */ + +#include "uc_priv.h" +#include "afl-common.h" + +/* This is the main instrumentation function, patched in at translate. + cur_loc has already been shifted in afl-unicorn-translate-inl.h at this point. + Also this helper will only be emitted if running instrumented. */ + +void HELPER(afl_maybe_log)(void* uc_ptr, uint64_t cur_loc) { + + struct uc_struct* uc = (struct uc_struct*) uc_ptr; + u8* afl_area_ptr = uc->afl_area_ptr; // Don't remove, it's used by INC_AFL_AREA implicitly; + + register uintptr_t afl_idx = cur_loc ^ uc->afl_prev_loc; + + INC_AFL_AREA(afl_idx); + +#if defined(AFL_DEBUG) + printf("[d] At loc 0x%llx: prev: 0x%llx, afl_idx: %lu, map[afl_idx]: %d\n", (unsigned long long) cur_loc, (unsigned long long) uc->afl_prev_loc, (unsigned long) afl_idx, afl_area_ptr[afl_idx]); +#endif + + uc->afl_prev_loc = cur_loc >> 1; + +} + +void HELPER(afl_compcov_log_16)(void* uc_ptr, uint64_t cur_loc, uint32_t arg1, + uint32_t arg2) { + + u8* afl_area_ptr = ((struct uc_struct*)uc_ptr)->afl_area_ptr; + + if ((arg1 & 0xff00) == (arg2 & 0xff00)) { INC_AFL_AREA(cur_loc); } + +} + +void HELPER(afl_compcov_log_32)(void* uc_ptr, uint64_t cur_loc, uint32_t arg1, + uint32_t arg2) { + + u8* afl_area_ptr = ((struct uc_struct*)uc_ptr)->afl_area_ptr; + + if ((arg1 & 0xff000000) == (arg2 & 0xff000000)) { + + INC_AFL_AREA(cur_loc + 2); + if ((arg1 & 0xff0000) == (arg2 & 0xff0000)) { + + INC_AFL_AREA(cur_loc + 1); + if ((arg1 & 0xff00) == (arg2 & 0xff00)) { INC_AFL_AREA(cur_loc); } + + } + + } + +} + +void HELPER(afl_compcov_log_64)(void* uc_ptr, uint64_t cur_loc, uint64_t arg1, + uint64_t arg2) { + + u8* afl_area_ptr = ((struct uc_struct*)uc_ptr)->afl_area_ptr; + + if ((arg1 & 0xff00000000000000) == (arg2 & 0xff00000000000000)) { + + INC_AFL_AREA(cur_loc + 6); + if ((arg1 & 0xff000000000000) == (arg2 & 0xff000000000000)) { + + INC_AFL_AREA(cur_loc + 5); + if ((arg1 & 0xff0000000000) == (arg2 & 0xff0000000000)) { + + INC_AFL_AREA(cur_loc + 4); + if ((arg1 & 0xff00000000) == (arg2 & 0xff00000000)) { + + INC_AFL_AREA(cur_loc + 3); + if ((arg1 & 0xff000000) == (arg2 & 0xff000000)) { + + INC_AFL_AREA(cur_loc + 2); + if ((arg1 & 0xff0000) == (arg2 & 0xff0000)) { + + INC_AFL_AREA(cur_loc + 1); + if ((arg1 & 0xff00) == (arg2 & 0xff00)) { INC_AFL_AREA(cur_loc); } + + } + + } + + } + + } + + } + + } + +} + diff --git a/include/afl/config.h b/include/afl/config.h new file mode 100644 index 00000000..b5137553 --- /dev/null +++ b/include/afl/config.h @@ -0,0 +1,452 @@ +/* + american fuzzy lop++ - vaguely configurable bits + ------------------------------------------------ + + Originally written by Michal Zalewski + + Now maintained by Marc Heuse , + Heiko Eißfeldt , + Andrea Fioraldi , + Dominik Maier + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#ifndef _HAVE_CONFIG_H +#define _HAVE_CONFIG_H + +/* Version string: */ + +// c = release, d = volatile github dev, e = experimental branch +#define VERSION "++3.01a" + +/****************************************************** + * * + * Settings that may be of interest to power users: * + * * + ******************************************************/ + +/* console output colors: There are three ways to configure its behavior + * 1. default: colored outputs fixed on: defined USE_COLOR && defined + * ALWAYS_COLORED The env var. AFL_NO_COLOR will have no effect + * 2. defined USE_COLOR && !defined ALWAYS_COLORED + * -> depending on env var AFL_NO_COLOR=1 colors can be switched off + * at run-time. Default is to use colors. + * 3. colored outputs fixed off: !defined USE_COLOR + * The env var. AFL_NO_COLOR will have no effect + */ + +/* Comment out to disable terminal colors (note that this makes afl-analyze + a lot less nice): */ + +#define USE_COLOR + +#ifdef USE_COLOR + /* Comment in to always enable terminal colors */ + /* Comment out to enable runtime controlled terminal colors via AFL_NO_COLOR + */ + #define ALWAYS_COLORED 1 +#endif + +/* StatsD config + Config can be adjusted via AFL_STATSD_HOST and AFL_STATSD_PORT environment + variable. +*/ +#define STATSD_UPDATE_SEC 1 +#define STATSD_DEFAULT_PORT 8125 +#define STATSD_DEFAULT_HOST "127.0.0.1" + +/* If you want to have the original afl internal memory corruption checks. + Disabled by default for speed. it is better to use "make ASAN_BUILD=1". */ + +//#define _WANT_ORIGINAL_AFL_ALLOC + +/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */ + +#ifndef ANDROID_DISABLE_FANCY // Fancy boxes are ugly from adb + #define FANCY_BOXES +#endif + +/* Default timeout for fuzzed code (milliseconds). This is the upper bound, + also used for detecting hangs; the actual value is auto-scaled: */ + +#define EXEC_TIMEOUT 1000U + +/* Timeout rounding factor when auto-scaling (milliseconds): */ + +#define EXEC_TM_ROUND 20U + +/* 64bit arch MACRO */ +#if (defined(__x86_64__) || defined(__arm64__) || defined(__aarch64__)) + #define WORD_SIZE_64 1 +#endif + +/* Default memory limit for child process (MB) 0 = disabled : */ + +#define MEM_LIMIT 0U + +/* Default memory limit when running in QEMU mode (MB) 0 = disabled : */ + +#define MEM_LIMIT_QEMU 0U + +/* Default memory limit when running in Unicorn mode (MB) 0 = disabled : */ + +#define MEM_LIMIT_UNICORN 0U + +/* Number of calibration cycles per every new test case (and for test + cases that show variable behavior): */ + +#define CAL_CYCLES 8U +#define CAL_CYCLES_LONG 40U + +/* Number of subsequent timeouts before abandoning an input file: */ + +#define TMOUT_LIMIT 250U + +/* Maximum number of unique hangs or crashes to record: */ + +#define KEEP_UNIQUE_HANG 500U +#define KEEP_UNIQUE_CRASH 5000U + +/* Baseline number of random tweaks during a single 'havoc' stage: */ + +#define HAVOC_CYCLES 256U +#define HAVOC_CYCLES_INIT 1024U + +/* Maximum multiplier for the above (should be a power of two, beware + of 32-bit int overflows): */ + +#define HAVOC_MAX_MULT 64U +#define HAVOC_MAX_MULT_MOPT 64U + +/* Absolute minimum number of havoc cycles (after all adjustments): */ + +#define HAVOC_MIN 12U + +/* Power Schedule Divisor */ +#define POWER_BETA 1U +#define MAX_FACTOR (POWER_BETA * 32) + +/* Maximum stacking for havoc-stage tweaks. The actual value is calculated + like this: + + n = random between 1 and HAVOC_STACK_POW2 + stacking = 2^n + + In other words, the default (n = 4) produces 2, 4, 8, 16 + stacked tweaks: */ + +#define HAVOC_STACK_POW2 4U + +/* Caps on block sizes for cloning and deletion operations. Each of these + ranges has a 33% probability of getting picked, except for the first + two cycles where smaller blocks are favored: */ + +#define HAVOC_BLK_SMALL 32U +#define HAVOC_BLK_MEDIUM 128U +#define HAVOC_BLK_LARGE 1500U + +/* Extra-large blocks, selected very rarely (<5% of the time): */ + +#define HAVOC_BLK_XL 32768U + +/* Probabilities of skipping non-favored entries in the queue, expressed as + percentages: */ + +#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */ +#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */ +#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */ + +/* Splicing cycle count: */ + +#define SPLICE_CYCLES 15 + +/* Nominal per-splice havoc cycle length: */ + +#define SPLICE_HAVOC 32 + +/* Maximum offset for integer addition / subtraction stages: */ + +#define ARITH_MAX 35 + +/* Limits for the test case trimmer. The absolute minimum chunk size; and + the starting and ending divisors for chopping up the input file: */ + +#define TRIM_MIN_BYTES 4 +#define TRIM_START_STEPS 16 +#define TRIM_END_STEPS 1024 + +/* Maximum size of input file, in bytes (keep under 100MB, default 1MB): + (note that if this value is changed, several areas in afl-cc.c, afl-fuzz.c + and afl-fuzz-state.c have to be changed as well! */ + +#define MAX_FILE (1 * 1024 * 1024U) + +/* The same, for the test case minimizer: */ + +#define TMIN_MAX_FILE (10 * 1024 * 1024) + +/* Block normalization steps for afl-tmin: */ + +#define TMIN_SET_MIN_SIZE 4 +#define TMIN_SET_STEPS 128 + +/* Maximum dictionary token size (-x), in bytes: */ + +#define MAX_DICT_FILE 128 + +/* Length limits for auto-detected dictionary tokens: */ + +#define MIN_AUTO_EXTRA 3 +#define MAX_AUTO_EXTRA 32 + +/* Maximum number of user-specified dictionary tokens to use in deterministic + steps; past this point, the "extras/user" step will be still carried out, + but with proportionally lower odds: */ + +#define MAX_DET_EXTRAS 256 + +/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing + (first value), and to keep in memory as candidates. The latter should be much + higher than the former. */ + +#define USE_AUTO_EXTRAS 128 +#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 64) + +/* Scaling factor for the effector map used to skip some of the more + expensive deterministic steps. The actual divisor is set to + 2^EFF_MAP_SCALE2 bytes: */ + +#define EFF_MAP_SCALE2 3 + +/* Minimum input file length at which the effector logic kicks in: */ + +#define EFF_MIN_LEN 128 + +/* Maximum effector density past which everything is just fuzzed + unconditionally (%): */ + +#define EFF_MAX_PERC 90 + +/* UI refresh frequency (Hz): */ + +#define UI_TARGET_HZ 5 + +/* Fuzzer stats file and plot update intervals (sec): */ + +#define STATS_UPDATE_SEC 60 +#define PLOT_UPDATE_SEC 5 + +/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */ + +#define AVG_SMOOTHING 16 + +/* Sync interval (every n havoc cycles): */ + +#define SYNC_INTERVAL 8 + +/* Output directory reuse grace period (minutes): */ + +#define OUTPUT_GRACE 25 + +/* Uncomment to use simple file names (id_NNNNNN): */ + +// #define SIMPLE_FILES + +/* List of interesting values to use in fuzzing. */ + +#define INTERESTING_8 \ + -128, /* Overflow signed 8-bit when decremented */ \ + -1, /* */ \ + 0, /* */ \ + 1, /* */ \ + 16, /* One-off with common buffer size */ \ + 32, /* One-off with common buffer size */ \ + 64, /* One-off with common buffer size */ \ + 100, /* One-off with common buffer size */ \ + 127 /* Overflow signed 8-bit when incremented */ + +#define INTERESTING_8_LEN 9 + +#define INTERESTING_16 \ + -32768, /* Overflow signed 16-bit when decremented */ \ + -129, /* Overflow signed 8-bit */ \ + 128, /* Overflow signed 8-bit */ \ + 255, /* Overflow unsig 8-bit when incremented */ \ + 256, /* Overflow unsig 8-bit */ \ + 512, /* One-off with common buffer size */ \ + 1000, /* One-off with common buffer size */ \ + 1024, /* One-off with common buffer size */ \ + 4096, /* One-off with common buffer size */ \ + 32767 /* Overflow signed 16-bit when incremented */ + +#define INTERESTING_16_LEN 10 + +#define INTERESTING_32 \ + -2147483648LL, /* Overflow signed 32-bit when decremented */ \ + -100663046, /* Large negative number (endian-agnostic) */ \ + -32769, /* Overflow signed 16-bit */ \ + 32768, /* Overflow signed 16-bit */ \ + 65535, /* Overflow unsig 16-bit when incremented */ \ + 65536, /* Overflow unsig 16 bit */ \ + 100663045, /* Large positive number (endian-agnostic) */ \ + 2147483647 /* Overflow signed 32-bit when incremented */ + +#define INTERESTING_32_LEN 8 + +/*********************************************************** + * * + * Really exotic stuff you probably don't want to touch: * + * * + ***********************************************************/ + +/* Call count interval between reseeding the libc PRNG from /dev/urandom: */ + +#define RESEED_RNG 100000 + +/* The default maximum testcase cache size in MB, 0 = disable. + A value between 50 and 250 is a good default value. Note that the + number of entries will be auto assigned if not specified via the + AFL_TESTCACHE_ENTRIES env variable */ + +#define TESTCASE_CACHE_SIZE 50 + +/* Maximum line length passed from GCC to 'as' and used for parsing + configuration files: */ + +#define MAX_LINE 8192 + +/* Environment variable used to pass SHM ID to the called program. */ + +#define SHM_ENV_VAR "__AFL_SHM_ID" + +/* Environment variable used to pass SHM FUZZ ID to the called program. */ + +#define SHM_FUZZ_ENV_VAR "__AFL_SHM_FUZZ_ID" + +/* Other less interesting, internal-only variables. */ + +#define CLANG_ENV_VAR "__AFL_CLANG_MODE" +#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK" +#define PERSIST_ENV_VAR "__AFL_PERSISTENT" +#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV" + +/* In-code signatures for deferred and persistent mode. */ + +#define PERSIST_SIG "##SIG_AFL_PERSISTENT##" +#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##" + +/* Distinctive bitmap signature used to indicate failed execution: */ + +#define EXEC_FAIL_SIG 0xfee1dead + +/* Distinctive exit code used to indicate MSAN trip condition: */ + +#define MSAN_ERROR 86 + +/* Designated file descriptors for forkserver commands (the application will + use FORKSRV_FD and FORKSRV_FD + 1): */ + +#define FORKSRV_FD 198 + +/* Fork server init timeout multiplier: we'll wait the user-selected + timeout plus this much for the fork server to spin up. */ + +#define FORK_WAIT_MULT 10 + +/* Calibration timeout adjustments, to be a bit more generous when resuming + fuzzing sessions or trying to calibrate already-added internal finds. + The first value is a percentage, the other is in milliseconds: */ + +#define CAL_TMOUT_PERC 125 +#define CAL_TMOUT_ADD 50 + +/* Number of chances to calibrate a case before giving up: */ + +#define CAL_CHANCES 3 + +/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than + 2; you probably want to keep it under 18 or so for performance reasons + (adjusting AFL_INST_RATIO when compiling is probably a better way to solve + problems with complex programs). You need to recompile the target binary + after changing this - otherwise, SEGVs may ensue. */ + +#define MAP_SIZE_POW2 16 +#define MAP_SIZE (1U << MAP_SIZE_POW2) + +/* Maximum allocator request size (keep well under INT_MAX): */ + +#define MAX_ALLOC 0x40000000 + +/* A made-up hashing seed: */ + +#define HASH_CONST 0xa5b35705 + +/* Constants for afl-gotcpu to control busy loop timing: */ + +#define CTEST_TARGET_MS 5000 +#define CTEST_CORE_TRG_MS 1000 +#define CTEST_BUSY_CYCLES (10 * 1000 * 1000) + +/* Enable NeverZero counters in QEMU mode */ + +#define AFL_QEMU_NOT_ZERO + +/* AFL RedQueen */ + +#define CMPLOG_SHM_ENV_VAR "__AFL_CMPLOG_SHM_ID" + +/* CPU Affinity lockfile env var */ + +#define CPU_AFFINITY_ENV_VAR "__AFL_LOCKFILE" + +/* Uncomment this to use inferior block-coverage-based instrumentation. Note + that you need to recompile the target binary for this to have any effect: */ + +// #define COVERAGE_ONLY + +/* Uncomment this to ignore hit counts and output just one bit per tuple. + As with the previous setting, you will need to recompile the target + binary: */ + +// #define SKIP_COUNTS + +/* Uncomment this to use instrumentation data to record newly discovered paths, + but do not use them as seeds for fuzzing. This is useful for conveniently + measuring coverage that could be attained by a "dumb" fuzzing algorithm: */ + +// #define IGNORE_FINDS + +/* Text mutations */ + +/* Minimum length of a queue input to be evaluated for "is_ascii"? */ + +#define AFL_TXT_MIN_LEN 12 + +/* What is the minimum percentage of ascii characters present to be classifed + as "is_ascii"? */ + +#define AFL_TXT_MIN_PERCENT 94 + +/* How often to perform ASCII mutations 0 = disable, 1-8 are good values */ + +#define AFL_TXT_BIAS 6 + +/* Maximum length of a string to tamper with */ + +#define AFL_TXT_STRING_MAX_LEN 1024 + +/* Maximum mutations on a string */ + +#define AFL_TXT_STRING_MAX_MUTATIONS 6 + +#endif /* ! _HAVE_CONFIG_H */ + diff --git a/include/afl/types.h b/include/afl/types.h new file mode 100644 index 00000000..7b94fb83 --- /dev/null +++ b/include/afl/types.h @@ -0,0 +1,193 @@ +/* + american fuzzy lop++ - type definitions and minor macros + -------------------------------------------------------- + + Originally written by Michal Zalewski + + Now maintained by Marc Heuse , + Heiko Eißfeldt , + Andrea Fioraldi , + Dominik Maier + + Copyright 2016, 2017 Google Inc. All rights reserved. + Copyright 2019-2020 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#ifndef _HAVE_TYPES_H +#define _HAVE_TYPES_H + +#include +#include +#include "config.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +#ifdef WORD_SIZE_64 +typedef unsigned __int128 uint128_t; +typedef uint128_t u128; +#endif + +/* Extended forkserver option values */ + +/* Reporting errors */ +#define FS_OPT_ERROR 0xf800008f +#define FS_OPT_GET_ERROR(x) ((x & 0x00ffff00) >> 8) +#define FS_OPT_SET_ERROR(x) ((x & 0x0000ffff) << 8) +#define FS_ERROR_MAP_SIZE 1 +#define FS_ERROR_MAP_ADDR 2 +#define FS_ERROR_SHM_OPEN 4 +#define FS_ERROR_SHMAT 8 +#define FS_ERROR_MMAP 16 + +/* Reporting options */ +#define FS_OPT_ENABLED 0x80000001 +#define FS_OPT_MAPSIZE 0x40000000 +#define FS_OPT_SNAPSHOT 0x20000000 +#define FS_OPT_AUTODICT 0x10000000 +#define FS_OPT_SHDMEM_FUZZ 0x01000000 +#define FS_OPT_OLD_AFLPP_WORKAROUND 0x0f000000 +// FS_OPT_MAX_MAPSIZE is 8388608 = 0x800000 = 2^23 = 1 << 22 +#define FS_OPT_MAX_MAPSIZE ((0x00fffffeU >> 1) + 1) +#define FS_OPT_GET_MAPSIZE(x) (((x & 0x00fffffe) >> 1) + 1) +#define FS_OPT_SET_MAPSIZE(x) \ + (x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1)) + +typedef unsigned long long u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +#ifdef WORD_SIZE_64 +typedef __int128 int128_t; +typedef int128_t s128; +#endif + +#ifndef MIN + #define MIN(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + \ + }) + + #define MAX(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + \ + }) + +#endif /* !MIN */ + +#define SWAP16(_x) \ + ({ \ + \ + u16 _ret = (_x); \ + (u16)((_ret << 8) | (_ret >> 8)); \ + \ + }) + +#define SWAP32(_x) \ + ({ \ + \ + u32 _ret = (_x); \ + (u32)((_ret << 24) | (_ret >> 24) | ((_ret << 8) & 0x00FF0000) | \ + ((_ret >> 8) & 0x0000FF00)); \ + \ + }) + +#define SWAP64(_x) \ + ({ \ + \ + u64 _ret = (_x); \ + _ret = \ + (_ret & 0x00000000FFFFFFFF) << 32 | (_ret & 0xFFFFFFFF00000000) >> 32; \ + _ret = \ + (_ret & 0x0000FFFF0000FFFF) << 16 | (_ret & 0xFFFF0000FFFF0000) >> 16; \ + _ret = \ + (_ret & 0x00FF00FF00FF00FF) << 8 | (_ret & 0xFF00FF00FF00FF00) >> 8; \ + _ret; \ + \ + }) + +// It is impossible to define 128 bit constants, so ... +#ifdef WORD_SIZE_64 + #define SWAPN(_x, _l) \ + ({ \ + \ + u128 _res = (_x), _ret; \ + char *d = (char *)&_ret, *s = (char *)&_res; \ + int i; \ + for (i = 0; i < 16; i++) \ + d[15 - i] = s[i]; \ + u32 sr = 128U - ((_l) << 3U); \ + (_ret >>= sr); \ + (u128) _ret; \ + \ + }) +#endif + +#define SWAPNN(_x, _y, _l) \ + ({ \ + \ + char *d = (char *)(_x), *s = (char *)(_y); \ + u32 i, l = (_l)-1; \ + for (i = 0; i <= l; i++) \ + d[l - i] = s[i]; \ + \ + }) + +#ifdef AFL_LLVM_PASS + #if defined(__linux__) || !defined(__ANDROID__) + #define AFL_SR(s) (srandom(s)) + #define AFL_R(x) (random() % (x)) + #else + #define AFL_SR(s) ((void)s) + #define AFL_R(x) (arc4random_uniform(x)) + #endif +#else + #if defined(__linux__) || !defined(__ANDROID__) + #define SR(s) (srandom(s)) + #define R(x) (random() % (x)) + #else + #define SR(s) ((void)s) + #define R(x) (arc4random_uniform(x)) + #endif +#endif /* ^AFL_LLVM_PASS */ + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +#define MEM_BARRIER() __asm__ volatile("" ::: "memory") + +#if __GNUC__ < 6 + #ifndef likely + #define likely(_x) (_x) + #endif + #ifndef unlikely + #define unlikely(_x) (_x) + #endif +#else + #ifndef likely + #define likely(_x) __builtin_expect(!!(_x), 1) + #endif + #ifndef unlikely + #define unlikely(_x) __builtin_expect(!!(_x), 0) + #endif +#endif + +#endif /* ! _HAVE_TYPES_H */ + diff --git a/include/uc_priv.h b/include/uc_priv.h index 8c564396..4f1bf4a9 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -15,14 +15,26 @@ // These are masks of supported modes for each cpu/arch. // They should be updated when changes are made to the uc_mode enum typedef. +#ifdef UNICORN_HAS_AFL +#define UC_MODE_ARM_MASK (UC_MODE_ARM|UC_MODE_THUMB|UC_MODE_LITTLE_ENDIAN|UC_MODE_MCLASS \ + |UC_MODE_ARM926|UC_MODE_ARM946|UC_MODE_ARM1176|UC_MODE_BIG_ENDIAN|UC_MODE_AFL) +#define UC_MODE_X86_MASK (UC_MODE_16|UC_MODE_32|UC_MODE_64|UC_MODE_LITTLE_ENDIAN|UC_MODE_AFL) +#define UC_MODE_MIPS_MASK (UC_MODE_MIPS32|UC_MODE_MIPS64|UC_MODE_LITTLE_ENDIAN|UC_MODE_BIG_ENDIAN|UC_MODE_AFL) +#define UC_MODE_PPC_MASK (UC_MODE_PPC32|UC_MODE_PPC64|UC_MODE_BIG_ENDIAN|UC_MODE_AFL) +#define UC_MODE_SPARC_MASK (UC_MODE_SPARC32|UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN|UC_MODE_AFL) +#define UC_MODE_M68K_MASK (UC_MODE_BIG_ENDIAN|UC_MODE_AFL) +#define UC_MODE_RISCV_MASK (UC_MODE_RISCV32|UC_MODE_RISCV64|UC_MODE_LITTLE_ENDIAN|UC_MODE_AFL) +#else #define UC_MODE_ARM_MASK (UC_MODE_ARM|UC_MODE_THUMB|UC_MODE_LITTLE_ENDIAN|UC_MODE_MCLASS \ |UC_MODE_ARM926|UC_MODE_ARM946|UC_MODE_ARM1176|UC_MODE_BIG_ENDIAN) -#define UC_MODE_MIPS_MASK (UC_MODE_MIPS32|UC_MODE_MIPS64|UC_MODE_LITTLE_ENDIAN|UC_MODE_BIG_ENDIAN) #define UC_MODE_X86_MASK (UC_MODE_16|UC_MODE_32|UC_MODE_64|UC_MODE_LITTLE_ENDIAN) +#define UC_MODE_MIPS_MASK (UC_MODE_MIPS32|UC_MODE_MIPS64|UC_MODE_LITTLE_ENDIAN|UC_MODE_BIG_ENDIAN) #define UC_MODE_PPC_MASK (UC_MODE_PPC32|UC_MODE_PPC64|UC_MODE_BIG_ENDIAN) #define UC_MODE_SPARC_MASK (UC_MODE_SPARC32|UC_MODE_SPARC64|UC_MODE_BIG_ENDIAN) #define UC_MODE_M68K_MASK (UC_MODE_BIG_ENDIAN) #define UC_MODE_RISCV_MASK (UC_MODE_RISCV32|UC_MODE_RISCV64|UC_MODE_LITTLE_ENDIAN) +#endif + #define ARR_SIZE(a) (sizeof(a)/sizeof(a[0])) @@ -95,6 +107,20 @@ typedef void (*uc_softfloat_initialize)(void); // tcg flush softmmu tlb typedef void (*uc_tcg_flush_tlb)(struct uc_struct *uc); +typedef enum uc_afl_ret { + UC_AFL_RET_ERROR = 0, // Something went horribly wrong in the parent + UC_AFL_RET_CHILD, // Fork worked. we are a child + UC_AFL_RET_NO_AFL, // No AFL, no need to fork. + UC_AFL_RET_CALLED_TWICE, // AFL has already been started before. + UC_AFL_RET_FINISHED, // We forked before but now AFL is gone (parent) +} uc_afl_ret; + +// we use this as shortcut deep inside uc_afl for the arch specific uc_afl_next(uc, bool) +typedef uc_afl_ret(*uc_afl_ret_uc_bool_t)(struct uc_struct*, bool); + +// afl_forkserver_start +typedef int (*uc_afl_forkserver_t)(struct uc_struct*); + struct hook { int type; // UC_HOOK_* int insn; // instruction for HOOK_INSN @@ -282,6 +308,22 @@ struct uc_struct { bool first_tb; // is this the first Translation-Block ever generated since uc_emu_start()? struct list saved_contexts; // The contexts saved by this uc_struct. bool no_exit_request; // Disable check_exit_request temporarily. A workaround to treat the IT block as a whole block. + +#ifdef UNICORN_HAS_AFL + uc_afl_forkserver_t afl_forkserver_start; // function to start afl forkserver + uc_afl_ret_uc_bool_t afl_child_request_next; // function from child to ask for new testcase (if in child) + int afl_child_pipe[2]; // pipe used to send information from child process to forkserver + int afl_parent_pipe[2]; // pipe used to send information from parent to child in forkserver + uint8_t *afl_area_ptr; // map, shared with afl, to report coverage feedback etc. during runs + uint64_t afl_prev_loc; // previous location + int afl_compcov_level; // how much compcove we want + unsigned int afl_inst_rms; + size_t exit_count; // number of exits set in afl_fuzz or afl_forkserver + uint64_t *exits; // pointer to the actual exits + char *afl_testcase_ptr; // map, shared with afl, to get testcases delivered from for each run + uint32_t *afl_testcase_size_p; // size of the current testcase, if using shared map fuzzing with afl. + void *afl_data_ptr; // Pointer for various (bindings-related) uses. +#endif }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index d7780e56..042318e8 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -105,6 +105,7 @@ typedef enum uc_arch { typedef enum uc_mode { UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode + UC_MODE_AFL = 1 << 29, // arm / arm64 UC_MODE_ARM = 0, // ARM mode @@ -171,6 +172,10 @@ typedef enum uc_err { UC_ERR_HOOK_EXIST, // hook for this event already existed UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start() UC_ERR_EXCEPTION, // Unhandled CPU exception + UC_ERR_AFL_RET_ERROR, // Something went horribly wrong in the parent + UC_ERR_AFL_RET_NO_AFL, // No AFL, no need to fork. + UC_ERR_AFL_RET_CALLED_TWICE, // AFL has already been started before. + UC_ERR_AFL_RET_FINISHED, // We forked before but now AFL is gone (parent) } uc_err; @@ -568,6 +573,82 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size); UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count); +/* Callback function called for each input from AFL. + This function is mandatory. + It's purpose is to place the input at the right place in unicorn. + @uc: Unicorn instance + @input: The current input we're working on. Place this somewhere in unicorn's memory now. + @input_len: length of the input + @persistent_round: which round we are currently crashing in, if using persistent mode. + @data: Data pointer passed to uc_afl_fuzz(...). + @return: + If you return is true, all is well. Fuzzing starts. + If you return false, the input is rejected; we will continue with the next input. +*/ +typedef bool (*uc_afl_cb_place_input_t)(uc_engine *uc, char *input, size_t input_len, uint32_t persistent_round, void *data); + +/* Callback function called after a non-UC_ERR_OK returncode was returned by Unicorn. + This function is not mandatory (pass NULL). + @uc: Unicorn instance + @unicorn_result: The error state returned by the current testcase + @input: The current input we're workin with. + @input_len: length of the input + @persistent_round: which round we are currently crashing in, if using persistent mode. + @data: Data pointer passed to uc_afl_fuzz(...). +@Return: + If you return false, the crash is considered invalid and not reported to AFL. + If return is true, the crash is reported. + -> The child will die and the forkserver will spawn a new child. +*/ +typedef bool (*uc_afl_cb_validate_crash_t)(uc_engine *uc, uc_err unicorn_result, char *input, int input_len, int persistent_round, void *data); + +/* + The main fuzzer. + Starts uc_afl_forkserver(), then beginns a persistent loop. + Reads input, calls the place_input callback, emulates, uc_afl_next(...), repeats. + If unicorn errors out, will call the validate_crash_callback, if set. + Will only retrun in the parent after the whole fuzz thing has been finished and afl died. + The child processes never return from here. + + Note: This API is not supported on Windows. + + @uc: handle returned by uc_open() + @input_file: filename/path to the (AFL) inputfile. Usualy suplied on the commandline. + @place_input_callback: Callback function that will be called before each test runs. + This function needs to write the input from afl to the correct position on the unicorn object. + @exits: address list of exits where fuzzing should stop (len == exit_count) + @exit_count: number of exits where fuzzing should stop + @validate_crash_callback: Optional callback (if not needed, pass NULL), that determines + if a non-OK uc_err is an actual error. If false is returned, the test-case will not crash. + @always_validate: If false, validate_crash_callback will only be called for crashes. + @persistent_iters: + The amount of loop iterations in persistent mode before restarteing with a new forked child. + If your target cannot be fuzzed using persistent mode (global state changes a lot), + set persistent_iters = 1 for the normal fork-server experience. + Else, the default is usually around 1000. + If your target is super stable (and unicorn is, too - not sure about that one), + you may pass persistent_iter = 0 for that an infinite fuzz loop. + @data: Your very own data pointer. This will passed into every callback. + @return uc_afl_ret: + >UC_AFL_RET_ERROR = 0, // Something went horribly wrong in the parent + >UC_AFL_RET_CHILD, // Can never happen, the child will loop happily or exit. + >UC_AFL_RET_NO_AFL, // No AFL, we ran the testacse once and are done. + >UC_AFL_RET_FINISHED, // We forked before but now AFL is gone (parent) + >> We're retuning after having fuzzed. We may now pack our bags and exit. +*/ +UNICORN_EXPORT +uc_err uc_afl_fuzz( + uc_engine *uc, + char* input_file, + uc_afl_cb_place_input_t place_input_callback, + uint64_t *exits, + size_t exit_count, + uc_afl_cb_validate_crash_t validate_crash_callback, + bool always_validate, + uint32_t persistent_iters, + void *data +); + /* Stop emulation (which was started by uc_emu_start() API. This is typically called from callback functions registered via tracing APIs. diff --git a/qemu/aarch64.h b/qemu/aarch64.h index 75d71514..21397dcd 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _aarch64 #endif +#define afl_forkserver_start afl_forkserver_start_aarch64 +#define helper_afl_maybe_log helper_afl_maybe_log_aarch64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_aarch64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_aarch64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_aarch64 #define use_idiv_instructions use_idiv_instructions_aarch64 #define arm_arch arm_arch_aarch64 #define tb_target_set_jmp_target tb_target_set_jmp_target_aarch64 diff --git a/qemu/aarch64eb.h b/qemu/aarch64eb.h index ee750153..55536832 100644 --- a/qemu/aarch64eb.h +++ b/qemu/aarch64eb.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _aarch64eb #endif +#define afl_forkserver_start afl_forkserver_start_aarch64eb +#define helper_afl_maybe_log helper_afl_maybe_log_aarch64eb +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_aarch64eb +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_aarch64eb +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_aarch64eb #define use_idiv_instructions use_idiv_instructions_aarch64eb #define arm_arch arm_arch_aarch64eb #define tb_target_set_jmp_target tb_target_set_jmp_target_aarch64eb diff --git a/qemu/accel/tcg/cpu-exec.c b/qemu/accel/tcg/cpu-exec.c index 79bcdd13..ea5b8045 100644 --- a/qemu/accel/tcg/cpu-exec.c +++ b/qemu/accel/tcg/cpu-exec.c @@ -29,6 +29,10 @@ #include "sysemu/cpus.h" #include "uc_priv.h" +#ifdef UNICORN_HAS_AFL +#include "afl/afl-cpu-inl.h" +#endif + /* -icount align implementation. */ typedef struct SyncClocks { @@ -254,6 +258,11 @@ static inline TranslationBlock *tb_find(CPUState *cpu, /* We add the TB in the virtual pc hash table for the fast lookup */ cpu->tb_jmp_cache[tb_jmp_cache_hash_func(cpu->uc, pc)] = tb; } + +#if defined(UNICORN_HAS_AFL) + afl_request_tsl(cpu, pc, cs_base, flags, cf_mask); +#endif + /* We don't take care of direct jumps when address mapping changes in * system emulation. So it's not safe to make a direct jump to a TB * spanning two pages because the mapping for the second page can change. @@ -579,3 +588,31 @@ int cpu_exec(struct uc_struct *uc, CPUState *cpu) return ret; } + +#ifdef UNICORN_HAS_AFL +int afl_forkserver_start(struct uc_struct *uc) +{ + // Not sure if we need all of this setup foo. + CPUState *cpu = uc->cpu; + if (!cpu->created) { + cpu->created = true; + cpu->halted = 0; + qemu_init_vcpu(cpu); + + } + cpu_resume(cpu); + + if (uc->count_hook != 0) { + uc_hook_del(uc, uc->count_hook); + uc->count_hook = 0; + } + + uc->quit_request = false; + uc->cpu = cpu; + smp_mb(); + + // Would love to not have the extra step in cpus.c, but it doesn't work otherwise(?) + afl_setup(uc); + return afl_forkserver(cpu); +} +#endif \ No newline at end of file diff --git a/qemu/accel/tcg/tcg-runtime.c b/qemu/accel/tcg/tcg-runtime.c index 1e8283ea..67829b91 100644 --- a/qemu/accel/tcg/tcg-runtime.c +++ b/qemu/accel/tcg/tcg-runtime.c @@ -32,6 +32,10 @@ #include +#ifdef UNICORN_HAS_AFL +#include "afl/afl-tcg-runtime-inl.h" +#endif + /* 32-bit helpers */ int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2) diff --git a/qemu/accel/tcg/tcg-runtime.h b/qemu/accel/tcg/tcg-runtime.h index ab7369e8..813c1c07 100644 --- a/qemu/accel/tcg/tcg-runtime.h +++ b/qemu/accel/tcg/tcg-runtime.h @@ -259,3 +259,10 @@ DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +#if defined(UNICORN_HAS_AFL) +DEF_HELPER_FLAGS_2(afl_maybe_log, 0, void, ptr, i64) +DEF_HELPER_FLAGS_4(afl_compcov_log_16, 0, void, ptr, i64, i32, i32) +DEF_HELPER_FLAGS_4(afl_compcov_log_32, 0, void, ptr, i64, i32, i32) +DEF_HELPER_FLAGS_4(afl_compcov_log_64, 0, void, ptr, i64, i64, i64) +#endif \ No newline at end of file diff --git a/qemu/accel/tcg/translator.c b/qemu/accel/tcg/translator.c index 0fca28a9..96f92873 100644 --- a/qemu/accel/tcg/translator.c +++ b/qemu/accel/tcg/translator.c @@ -17,6 +17,11 @@ #include +#if defined(UNICORN_HAS_AFL) +#undef ARCH_HAS_COMPCOV +#include "afl/afl-cpu-translate-inl.h" +#endif + /* Pairs with tcg_clear_temp_count. To be called by #TranslatorOps.{translate_insn,tb_stop} if (1) the target is sufficiently clean to support reporting, @@ -56,6 +61,27 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, /* Reset the temp count so that we can identify leaks */ tcg_clear_temp_count(); + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = cpu->uc->exits; + size_t exit_count = cpu->uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (tb->pc == exits[i]) { + // This should catch that instruction is at the end + // and generate appropriate halting code. + gen_tb_start(tcg_ctx, db->tb); + ops->tb_start(db, cpu); + db->num_insns++; + ops->insn_start(db, cpu); + ops->translate_insn(db, cpu); + goto _end_loop; + } + } + } + } + /* Unicorn: early check to see if the address of this block is * the "run until" address. */ if (tb->pc == cpu->uc->addr_end) { @@ -81,6 +107,10 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, // tcg_dump_ops(tcg_ctx, false, "translator loop"); +#ifdef UNICORN_HAS_AFL + afl_gen_maybe_log(tcg_ctx, tb->pc); +#endif + /* Start translating. */ gen_tb_start(tcg_ctx, db->tb); // tcg_dump_ops(tcg_ctx, false, "tb start"); diff --git a/qemu/arm.h b/qemu/arm.h index d3cc372d..ef9fa72d 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _arm #endif +#define afl_forkserver_start afl_forkserver_start_arm +#define helper_afl_maybe_log helper_afl_maybe_log_arm +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_arm +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_arm +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_arm #define use_idiv_instructions use_idiv_instructions_arm #define arm_arch arm_arch_arm #define tb_target_set_jmp_target tb_target_set_jmp_target_arm diff --git a/qemu/armeb.h b/qemu/armeb.h index 0a51a80d..7ecccaaa 100644 --- a/qemu/armeb.h +++ b/qemu/armeb.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _armeb #endif +#define afl_forkserver_start afl_forkserver_start_armeb +#define helper_afl_maybe_log helper_afl_maybe_log_armeb +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_armeb +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_armeb +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_armeb #define use_idiv_instructions use_idiv_instructions_armeb #define arm_arch arm_arch_armeb #define tb_target_set_jmp_target tb_target_set_jmp_target_armeb diff --git a/qemu/include/tcg/tcg-op.h b/qemu/include/tcg/tcg-op.h index 73d1b93f..915fe79f 100644 --- a/qemu/include/tcg/tcg-op.h +++ b/qemu/include/tcg/tcg-op.h @@ -29,6 +29,10 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#ifdef UNICORN_HAS_AFL +#include "afl/afl-tcg-op-inl.h" +#endif + static inline void gen_uc_tracecode(TCGContext *tcg_ctx, int32_t size, int32_t type, void *uc, uint64_t pc) { TCGv_i32 tsize = tcg_const_i32(tcg_ctx, size); diff --git a/qemu/m68k.h b/qemu/m68k.h index cb3d430b..77fb249c 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _m68k #endif +#define afl_forkserver_start afl_forkserver_start_m68k +#define helper_afl_maybe_log helper_afl_maybe_log_m68k +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_m68k +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_m68k +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_m68k #define use_idiv_instructions use_idiv_instructions_m68k #define arm_arch arm_arch_m68k #define tb_target_set_jmp_target tb_target_set_jmp_target_m68k diff --git a/qemu/mips.h b/qemu/mips.h index a2b78379..9eef9f63 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _mips #endif +#define afl_forkserver_start afl_forkserver_start_mips +#define helper_afl_maybe_log helper_afl_maybe_log_mips +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_mips +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_mips +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_mips #define use_idiv_instructions use_idiv_instructions_mips #define arm_arch arm_arch_mips #define tb_target_set_jmp_target tb_target_set_jmp_target_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index 9faef36b..9dde191e 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _mips64 #endif +#define afl_forkserver_start afl_forkserver_start_mips64 +#define helper_afl_maybe_log helper_afl_maybe_log_mips64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_mips64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_mips64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_mips64 #define use_idiv_instructions use_idiv_instructions_mips64 #define arm_arch arm_arch_mips64 #define tb_target_set_jmp_target tb_target_set_jmp_target_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index 6f748b36..4634c429 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _mips64el #endif +#define afl_forkserver_start afl_forkserver_start_mips64el +#define helper_afl_maybe_log helper_afl_maybe_log_mips64el +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_mips64el +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_mips64el +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_mips64el #define use_idiv_instructions use_idiv_instructions_mips64el #define arm_arch arm_arch_mips64el #define tb_target_set_jmp_target tb_target_set_jmp_target_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index a4831183..1e6ce28b 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _mipsel #endif +#define afl_forkserver_start afl_forkserver_start_mipsel +#define helper_afl_maybe_log helper_afl_maybe_log_mipsel +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_mipsel +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_mipsel +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_mipsel #define use_idiv_instructions use_idiv_instructions_mipsel #define arm_arch arm_arch_mipsel #define tb_target_set_jmp_target tb_target_set_jmp_target_mipsel diff --git a/qemu/ppc.h b/qemu/ppc.h index 8cb9ea3e..e787604c 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _ppc #endif +#define afl_forkserver_start afl_forkserver_start_ppc +#define helper_afl_maybe_log helper_afl_maybe_log_ppc +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_ppc +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_ppc +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_ppc #define use_idiv_instructions use_idiv_instructions_ppc #define arm_arch arm_arch_ppc #define tb_target_set_jmp_target tb_target_set_jmp_target_ppc diff --git a/qemu/ppc64.h b/qemu/ppc64.h index 1d055072..ada125c0 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _ppc64 #endif +#define afl_forkserver_start afl_forkserver_start_ppc64 +#define helper_afl_maybe_log helper_afl_maybe_log_ppc64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_ppc64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_ppc64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_ppc64 #define use_idiv_instructions use_idiv_instructions_ppc64 #define arm_arch arm_arch_ppc64 #define tb_target_set_jmp_target tb_target_set_jmp_target_ppc64 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index df9eed70..f91bcf0b 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _riscv32 #endif +#define afl_forkserver_start afl_forkserver_start_riscv32 +#define helper_afl_maybe_log helper_afl_maybe_log_riscv32 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_riscv32 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_riscv32 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_riscv32 #define use_idiv_instructions use_idiv_instructions_riscv32 #define arm_arch arm_arch_riscv32 #define tb_target_set_jmp_target tb_target_set_jmp_target_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index c41e7178..c59c5148 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _riscv64 #endif +#define afl_forkserver_start afl_forkserver_start_riscv64 +#define helper_afl_maybe_log helper_afl_maybe_log_riscv64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_riscv64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_riscv64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_riscv64 #define use_idiv_instructions use_idiv_instructions_riscv64 #define arm_arch arm_arch_riscv64 #define tb_target_set_jmp_target tb_target_set_jmp_target_riscv64 diff --git a/qemu/softmmu/cpus.c b/qemu/softmmu/cpus.c index f983f634..acb7f14b 100644 --- a/qemu/softmmu/cpus.c +++ b/qemu/softmmu/cpus.c @@ -194,6 +194,21 @@ void resume_all_vcpus(struct uc_struct* uc) tb_flush_jmp_cache(cpu, uc->addr_end); } + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = uc->exits; + size_t exit_count = uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + TranslationBlock *tb = cpu->tb_jmp_cache[tb_jmp_cache_hash_func(uc, exits[i])]; + if (tb) { + qht_remove(&uc->tcg_ctx->tb_ctx.htable, tb, tb->hash); + tb_flush_jmp_cache(cpu, uc->exits[i]); + } + } + } + } cpu->created = false; } diff --git a/qemu/sparc.h b/qemu/sparc.h index 741e7565..daa2dcb6 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _sparc #endif +#define afl_forkserver_start afl_forkserver_start_sparc +#define helper_afl_maybe_log helper_afl_maybe_log_sparc +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_sparc +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_sparc +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_sparc #define use_idiv_instructions use_idiv_instructions_sparc #define arm_arch arm_arch_sparc #define tb_target_set_jmp_target tb_target_set_jmp_target_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 5262a11b..a2e1ec32 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _sparc64 #endif +#define afl_forkserver_start afl_forkserver_start_sparc64 +#define helper_afl_maybe_log helper_afl_maybe_log_sparc64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_sparc64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_sparc64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_sparc64 #define use_idiv_instructions use_idiv_instructions_sparc64 #define arm_arch arm_arch_sparc64 #define tb_target_set_jmp_target tb_target_set_jmp_target_sparc64 diff --git a/qemu/target/arm/translate-a64.c b/qemu/target/arm/translate-a64.c index a9eb1ca0..5ab0e963 100644 --- a/qemu/target/arm/translate-a64.c +++ b/qemu/target/arm/translate-a64.c @@ -37,6 +37,11 @@ #include "qemu/atomic128.h" #include "kvm-consts.h" +#if defined(UNICORN_HAS_AFL) +#undef ARCH_HAS_COMPCOV +#include "afl/afl-cpu-translate-inl.h" +#endif + static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", @@ -14636,6 +14641,21 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; + if (dc->uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = dc->uc->exits; + size_t exit_count = dc->uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (dcbase->pc_next == exits[i]) { + dcbase->is_jmp = DISAS_WFI; + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (dcbase->pc_next == dc->uc->addr_end) { // imitate WFI instruction to halt emulation diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index 742a55fd..fe325e1c 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -11420,6 +11420,21 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) return; } + if (dc->uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = dc->uc->exits; + size_t exit_count = dc->uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (dcbase->pc_next == exits[i]) { + dcbase->is_jmp = DISAS_WFI; + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (dcbase->pc_next == dc->uc->addr_end) { // imitate WFI instruction to halt emulation diff --git a/qemu/target/i386/translate.c b/qemu/target/i386/translate.c index cf148c4b..4b44bafd 100644 --- a/qemu/target/i386/translate.c +++ b/qemu/target/i386/translate.c @@ -4764,6 +4764,25 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) s->uc = env->uc; + if (s->uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = s->uc->exits; + size_t exit_count = s->uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (s->pc == exits[i]) { + // imitate the HLT instruction + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_hlt(tcg_ctx, tcg_ctx->cpu_env, tcg_const_i32(tcg_ctx, s->pc - pc_start)); + s->base.is_jmp = DISAS_NORETURN; + return s->pc; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (s->pc == s->uc->addr_end) { // imitate the HLT instruction diff --git a/qemu/target/m68k/translate.c b/qemu/target/m68k/translate.c index 323eff44..c1940270 100644 --- a/qemu/target/m68k/translate.c +++ b/qemu/target/m68k/translate.c @@ -6325,6 +6325,21 @@ static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) CPUM68KState *env = cpu->env_ptr; uint16_t insn; + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = uc->exits; + size_t exit_count = uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (dc->pc == exits[i]) { + gen_exception(dc, dc->pc, EXCP_HLT); + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (dc->pc == uc->addr_end) { gen_exception(dc, dc->pc, EXCP_HLT); diff --git a/qemu/target/mips/translate.c b/qemu/target/mips/translate.c index 97e680a3..ff36123b 100644 --- a/qemu/target/mips/translate.c +++ b/qemu/target/mips/translate.c @@ -30930,6 +30930,22 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) bool hook_insn = false; is_slot = ctx->hflags & MIPS_HFLAG_BMASK; + + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = uc->exits; + size_t exit_count = uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (ctx->base.pc_next == exits[i]) {// raise a special interrupt to quit + gen_helper_wait(tcg_ctx, tcg_ctx->cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return; + } + } + } + } // Unicorn: end address tells us to stop emulation if (ctx->base.pc_next == uc->addr_end) { diff --git a/qemu/target/ppc/translate.c b/qemu/target/ppc/translate.c index 8cfebab2..623e8b4c 100644 --- a/qemu/target/ppc/translate.c +++ b/qemu/target/ppc/translate.c @@ -7625,6 +7625,21 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n", ctx->base.pc_next, ctx->mem_idx, (int)msr_ir); + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = uc->exits; + size_t exit_count = uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (ctx->base.pc_next == exits[i]) { + gen_wait(ctx); + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (ctx->base.pc_next == uc->addr_end) { gen_wait(ctx); diff --git a/qemu/target/riscv/translate.c b/qemu/target/riscv/translate.c index 9898093d..3e78dbfc 100644 --- a/qemu/target/riscv/translate.c +++ b/qemu/target/riscv/translate.c @@ -849,6 +849,22 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) TCGOp *tcg_op, *prev_op = NULL; bool insn_hook = false; + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = ctx->uc->exits; + size_t exit_count = ctx->uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (ctx->base.pc_next == exits[i]) { + // Unicorn: We have to exit current execution here. + dcbase->is_jmp = DISAS_UC_EXIT; + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (ctx->base.pc_next == ctx->uc->addr_end) { // Unicorn: We have to exit current execution here. diff --git a/qemu/target/sparc/translate.c b/qemu/target/sparc/translate.c index c6f3d9cd..66f834d3 100644 --- a/qemu/target/sparc/translate.c +++ b/qemu/target/sparc/translate.c @@ -5950,6 +5950,24 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) CPUSPARCState *env = cs->env_ptr; unsigned int insn; + if (uc->mode & UC_MODE_AFL) { + // UNICORN-AFL supports (and needs) multiple exits. + uint64_t *exits = uc->exits; + size_t exit_count = uc->exit_count; + if (exit_count) { + size_t i; + for (i = 0; i < exit_count; i++) { + if (dc->pc == exits[i]) { +#ifndef TARGET_SPARC64 + gen_helper_power_down(tcg_ctx, tcg_ctx->cpu_env); +#endif + dcbase->is_jmp = DISAS_NORETURN; + return; + } + } + } + } + // Unicorn: end address tells us to stop emulation if (dc->pc == uc->addr_end) { #ifndef TARGET_SPARC64 diff --git a/qemu/x86_64.h b/qemu/x86_64.h index a1896af7..233e03c3 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -4,6 +4,11 @@ #ifndef UNICORN_ARCH_POSTFIX #define UNICORN_ARCH_POSTFIX _x86_64 #endif +#define afl_forkserver_start afl_forkserver_start_x86_64 +#define helper_afl_maybe_log helper_afl_maybe_log_x86_64 +#define helper_afl_compcov_log_16 helper_afl_compcov_log_16_x86_64 +#define helper_afl_compcov_log_32 helper_afl_compcov_log_32_x86_64 +#define helper_afl_compcov_log_64 helper_afl_compcov_log_64_x86_64 #define use_idiv_instructions use_idiv_instructions_x86_64 #define arm_arch arm_arch_x86_64 #define tb_target_set_jmp_target tb_target_set_jmp_target_x86_64 diff --git a/symbols.sh b/symbols.sh index 19997fb4..0f8541b2 100755 --- a/symbols.sh +++ b/symbols.sh @@ -4,6 +4,11 @@ CMD_PATH=$(realpath $0) SOURCE_DIR=$(dirname ${CMD_PATH}) COMMON_SYMBOLS=" +afl_forkserver_start \ +helper_afl_maybe_log \ +helper_afl_compcov_log_16 \ +helper_afl_compcov_log_32 \ +helper_afl_compcov_log_64 \ use_idiv_instructions \ arm_arch \ tb_target_set_jmp_target \ diff --git a/uc.c b/uc.c index 3e24674c..40ac6001 100644 --- a/uc.c +++ b/uc.c @@ -26,6 +26,17 @@ #include "qemu/include/qemu/queue.h" +#ifdef UNICORN_HAS_AFL +#include +#include +#include +#include +#include +#include +#include "afl/config.h" +#include "afl/types.h" +#endif + UNICORN_EXPORT unsigned int uc_version(unsigned int *major, unsigned int *minor) { @@ -93,6 +104,14 @@ const char *uc_strerror(uc_err code) return "Insufficient resource (UC_ERR_RESOURCE)"; case UC_ERR_EXCEPTION: return "Unhandled CPU exception (UC_ERR_EXCEPTION)"; + case UC_ERR_AFL_RET_ERROR: + return "Something went horribly wrong in the parent (UC_ERR_AFL_RET_ERROR)"; + case UC_ERR_AFL_RET_NO_AFL: + return "No AFL, no need to fork (UC_ERR_AFL_RET_NO_AFL)"; + case UC_ERR_AFL_RET_CALLED_TWICE: + return "AFL has already been started before (UC_ERR_AFL_RET_CALLED_TWICE)"; + case UC_ERR_AFL_RET_FINISHED: + return "We forked before but now parent is gone (UC_ERR_AFL_REG_FINISHED)"; } } @@ -748,6 +767,315 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time } +static inline uc_afl_ret uc_afl_forkserver_start(uc_engine *uc, uint64_t *exits, size_t exit_count) +{ + /* + Why we need exits as parameter to forkserver: + In the original unicorn-afl, Unicorn needed to flush the tb cache for every iteration. + This is super slow. + Problem was, that the original forked server doesn't know about possible future exits. + The cached blocks, in the next child, therefore would have no exit set and run forever. + Also it's nice to have multiple exits, so let's just do it right. + */ + + if (!uc) { + fprintf(stderr, "[!] Unicorn Engine passed to uc_afl_fuzz is NULL!\n"); + return UC_AFL_RET_ERROR; + } + if (!exits) { + fprintf(stderr, "[!] Nullptr provided for exits.\n"); + return UC_AFL_RET_ERROR; + } + if (!exit_count) { + fprintf(stderr, "[!] No exits provided (exit_count was 0).\n"); + return UC_AFL_RET_ERROR; + } + if (unlikely(uc->afl_area_ptr)) { +#if defined(AFL_DEBUG) + fprintf(stderr, "[!] forkserver_start(...) called twice. Already fuzzing!\n"); +#endif + return UC_AFL_RET_CALLED_TWICE; // AFL has already been started before. + } + + /* Copy exits to unicorn env buffer */ + uc->exits = g_realloc(uc->exits, sizeof(exits[0]) * exit_count); + if (uc->exits == NULL) { + perror("[!] malloc failed when starting forkserver."); + return UC_AFL_RET_ERROR; + } + memcpy(uc->exits, exits, sizeof(exits[0]) * exit_count); + uc->exit_count = exit_count; + // Set addr_end to make sure unicorn will not stop at addr 0x0. + uc->addr_end = uc->exits[0]; + + /* Fork() :) */ + return uc->afl_forkserver_start(uc); + +} + +/* AFL++ supports testcase forwarding via shared map. + If the env variable is set, get the shared map here. + returns true if we enabled shmap fuzzing, false otherwise. */ +static bool uc_afl_enable_shm_testcases(uc_engine *uc) { + + char *id_str = getenv(SHM_FUZZ_ENV_VAR); + if (id_str) { + int shm_id = atoi(id_str); + char *map = (char *)shmat(shm_id, NULL, 0); + if (!map || map == (void *)-1) { + perror("[!] could not access fuzzing shared memory"); + exit(1); + } + uc->afl_testcase_size_p = (u32 *)map; + uc->afl_testcase_ptr = (map + sizeof(u32)); +#if defined(AFL_DEBUG) + printf("[d] successfully opened shared memory for testcases with id %d\n", shm_id); +#endif + return true; + + } else { +#if defined(AFL_DEBUG) + printf("[d] SHM_FUZZ_ENV_VAR not set - not using shared map fuzzing.\n"); +#endif + return false; + } + +} + +/* returns the filesize in bytes, -1 or error. */ +static inline off_t uc_afl_mmap_file(char *filename, char **buf_ptr) { + + off_t ret = -1; + + int fd = open(filename, O_RDONLY); + + struct stat st = {0}; + if (fstat(fd, &st)) goto exit; + + off_t in_len = st.st_size; + + *buf_ptr = mmap(0, in_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + + if (*buf_ptr != MAP_FAILED) ret = in_len; + +exit: + close(fd); + return ret; + +} + +/* A start with "less features" for our afl use-case */ +/* this is largely copied from uc_emu_start, just without setting the entry point, counter and timeout. */ +int uc_afl_emu_start(uc_engine *uc) { + + uc->emu_counter = 0; + uc->invalid_error = UC_ERR_OK; + uc->emulation_done = false; + uc->stop_request = false; + + // remove count hook if counting isn't necessary + if (uc->count_hook != 0) { + uc_hook_del(uc, uc->count_hook); + uc->count_hook = 0; + } + + uc->vm_start(uc); + + // emulation is done + uc->emulation_done = true; + + return uc->invalid_error; + +} + +/* afl_next that expects you know what you're doing + Specifically, it won't check for afl_area_ptr and next to be set. */ +static inline uc_afl_ret uc_afl_next_inl(uc_engine *uc, bool crash_found) +{ + // Tell the parent we need a new testcase, then stop until testcase is available. + if (uc->afl_child_request_next(uc, crash_found) == UC_AFL_RET_ERROR) return UC_AFL_RET_ERROR; + return UC_AFL_RET_CHILD; + +} + + +/* similar to __afl_persistent loop */ +static inline uc_afl_ret uc_afl_next(uc_engine *uc, bool crash_found) +{ + + if (unlikely(!uc->afl_area_ptr)) { + fprintf(stderr, "[!] uc_afl_next(...) called before forkserver_start(...)."); + return UC_AFL_RET_ERROR; + } + + // Tell the parent we need a new testcase, then stop until testcase is available. + if (uc->afl_child_request_next) { + + return uc_afl_next_inl(uc, crash_found); + + } + + return UC_AFL_RET_NO_AFL; +} + +UNICORN_EXPORT +uc_err uc_afl_fuzz( + uc_engine *uc, + char* input_file, + uc_afl_cb_place_input_t place_input_callback, + uint64_t *exits, + size_t exit_count, + uc_afl_cb_validate_crash_t validate_crash_callback, + bool always_validate, + uint32_t persistent_iters, + void *data +){ +#ifndef UNICORN_HAS_AFL + return UC_ERR_MODE; +#else + if (!uc) { + fprintf(stderr, "[!] Unicorn Engine passed to uc_afl_fuzz is NULL!\n"); + return UC_AFL_RET_ERROR; + } + if (!input_file || input_file[0] == 0) { + fprintf(stderr, "[!] No input file provided to uc_afl_fuzz.\n"); + return UC_AFL_RET_ERROR; + } + if (!place_input_callback) { + fprintf(stderr, "[!] no place_input_callback set.\n"); + return UC_AFL_RET_ERROR; + } + if (always_validate && !validate_crash_callback) { + fprintf(stderr, "[!] always_validate set but validate_crash_callback is missing.\n"); + return UC_AFL_RET_ERROR; + } + if (!exit_count) { + fprintf(stderr, "[!] Nullptr provided for exits.\n"); + return UC_AFL_RET_ERROR; + } + + uint32_t mmap_in_len = 0; + char *in_buf = NULL; + uint32_t *in_len_p = NULL; + + bool use_shmap_input = uc_afl_enable_shm_testcases(uc); + if (use_shmap_input) { + /* For shared map fuzzing, the ptr stays the same */ + in_buf = uc->afl_testcase_ptr; + in_len_p = uc->afl_testcase_size_p; + } else { + in_len_p = &mmap_in_len; + } + + uc_afl_ret afl_ret = uc_afl_forkserver_start(uc, exits, exit_count); + switch(afl_ret) { + case UC_AFL_RET_CHILD: + break; + case UC_AFL_RET_NO_AFL: + // Not running in AFL. + persistent_iters = 1; + break; + case UC_AFL_RET_FINISHED: + // Nothing more to do + return afl_ret; + case UC_AFL_RET_ERROR: + case UC_AFL_RET_CALLED_TWICE: + // Nothing more we can do + return afl_ret; + default: + // What have we done + fprintf(stderr, "[!] Unexpected forkserver return: %d", afl_ret); + return UC_AFL_RET_ERROR; + } + + bool first_round = true; + bool crash_found = false; + +#if defined(AFL_DEBUG) + if (uc->afl_testcase_ptr) { + printf("[d] uc->afl_testcase_ptr = %p, len = %d\n", uc->afl_testcase_ptr, *uc->afl_testcase_size_p); + } +#endif + + // 0 means never stop child in persistence mode. + uint32_t i; + for (i = 0; persistent_iters == 0 || i < persistent_iters; i++) { + + // The main fuzz loop starts here :) + if (first_round) { + first_round = false; + } else { + if (uc_afl_next_inl(uc, crash_found) == UC_AFL_RET_ERROR) { + /* parent is probably gone */ + exit(1); + } + crash_found = false; + } + + /* get input, call place input callback, emulate, unmap input (if needed) */ + if (unlikely(!use_shmap_input)) { + /* in_buf and the len are not in a shared map (as it would be for sharedmem fuzzing + No shmap fuzzing involved - Let's read a "normal" file. */ + off_t in_len = uc_afl_mmap_file(input_file, &in_buf); + if (unlikely(in_len < 0)) { + fprintf(stderr, "[!] Unable to mmap file: %s (return was %ld)\n", input_file, (long int) in_len); + perror("mmap"); + fflush(stderr); + return UC_AFL_RET_ERROR; + } + mmap_in_len = in_len; + } + bool input_accepted = place_input_callback(uc, in_buf, *in_len_p, i, data); + + if (unlikely(!input_accepted)) { + // Apparently the input was not to the users' liking. Let's continue. + goto next_iter; + } + + uc_err uc_emu_ret = uc_afl_emu_start(uc); + + if (unlikely((uc_emu_ret != UC_ERR_OK) || (always_validate && validate_crash_callback))) { + + if (validate_crash_callback != NULL && validate_crash_callback( + uc, uc_emu_ret, in_buf, *in_len_p, i, data) != true) { + // The callback thinks this is not a valid crash. Ignore. + goto next_iter; + } + if (persistent_iters != 1) { + // We're inpersistent mode and can report the crash via afl_next. No reason to die. + crash_found = true; + goto next_iter; + } + + fprintf(stderr, "[!] UC returned Error: '%s' - let's abort().\n", uc_strerror(uc_emu_ret)); + fflush(stderr); + + abort(); + + } +next_iter: + if (!use_shmap_input) munmap(in_buf, mmap_in_len); + } + // UC_AFL_RET_CHILD -> We looped through all iters. + // We are still in the child, nothing good will come after this. + // Exit and let the next generation run. + if (likely(afl_ret == UC_AFL_RET_CHILD)) { + exit(0); + } + + if (uc->afl_area_ptr) { + // Nothing should ever come after this but clean it up still. + // shmdt(uc->afl_area_ptr); + uc->afl_area_ptr = NULL; + uc->afl_testcase_ptr = NULL; + + } + + // UC_AFL_RET_NO_AFL -> Not fuzzing. We ran once. + return UC_AFL_RET_NO_AFL; +#endif +} + UNICORN_EXPORT uc_err uc_emu_stop(uc_engine *uc) {