From 6b40189045645938098772260ecda1c0bcbf6467 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 12 Sep 2019 12:34:53 +0200 Subject: first version of persistent QEMU --- qemu_mode/patches/afl-qemu-cpu-inl.h | 166 ++++++++++++++++++++++++++++++----- 1 file changed, 145 insertions(+), 21 deletions(-) (limited to 'qemu_mode/patches/afl-qemu-cpu-inl.h') diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index 2a1331cb..826167eb 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -34,6 +34,8 @@ #include #include "../../config.h" +#define PERSISTENT_DEFAULT_MAX_CNT 1000 + /*************************** * VARIOUS AUXILIARY STUFF * ***************************/ @@ -71,13 +73,19 @@ abi_ulong afl_entry_point, /* ELF entry point (_start) */ afl_start_code, /* .text start pointer */ afl_end_code; /* .text end pointer */ +abi_ulong afl_persistent_addr, afl_persistent_ret_addr; +unsigned int afl_persistent_cnt; + u8 afl_compcov_level; +__thread abi_ulong afl_prev_loc; + /* Set in the child process in forkserver mode: */ -static int forkserver_installed = 0; -static unsigned char afl_fork_child; -unsigned int afl_forksrv_pid; +static int forkserver_installed = 0; +unsigned char afl_fork_child; +unsigned int afl_forksrv_pid; +unsigned char is_persistent; /* Instrumentation ratio: */ @@ -187,6 +195,22 @@ static void afl_setup(void) { rcu_disable_atfork(); + is_persistent = getenv("AFL_QEMU_PERSISTENT_ADDR") != NULL; + + if (is_persistent) { + + afl_persistent_addr = strtoll(getenv("AFL_QEMU_PERSISTENT_ADDR"), NULL, 16); + if (getenv("AFL_QEMU_PERSISTENT_RET") == NULL) exit(1); + afl_persistent_ret_addr = + strtoll(getenv("AFL_QEMU_PERSISTENT_RET"), NULL, 16); + + } + + if (getenv("AFL_QEMU_PERSISTENT_CNT")) + afl_persistent_cnt = strtoll(getenv("AFL_QEMU_PERSISTENT_CNT"), NULL, 16); + else + afl_persistent_cnt = PERSISTENT_DEFAULT_MAX_CNT; + } /* Fork server logic, invoked once we hit _start. */ @@ -197,8 +221,13 @@ static void afl_forkserver(CPUState *cpu) { if (forkserver_installed == 1) return; forkserver_installed = 1; + // if (!afl_area_ptr) return; // not necessary because of fixed dummy buffer + pid_t child_pid; + int t_fd[2]; + u8 child_stopped = 0; + /* Tell the parent that we're alive. If the parent doesn't want to talk, assume that we're not running in forkserver mode. */ @@ -210,38 +239,63 @@ static void afl_forkserver(CPUState *cpu) { while (1) { - pid_t child_pid; - int status, t_fd[2]; + int status; + u32 was_killed; /* Whoops, parent dead? */ - if (read(FORKSRV_FD, tmp, 4) != 4) exit(2); + if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2); + + /* 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_stopped && was_killed) { + + child_stopped = 0; + if (waitpid(child_pid, &status, 0) < 0) exit(8); + + } + + if (!child_stopped) { - /* Establish a channel with child to grab translation commands. We'll + /* Establish a channel with child to grab translation commands. We'll read from t_fd[0], child will write to TSL_FD. */ - if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3); - close(t_fd[1]); + if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3); + close(t_fd[1]); - child_pid = fork(); - if (child_pid < 0) exit(4); + child_pid = fork(); + if (child_pid < 0) exit(4); - if (!child_pid) { + if (!child_pid) { - /* Child process. Close descriptors and run free. */ + /* Child process. Close descriptors and run free. */ - afl_fork_child = 1; - close(FORKSRV_FD); - close(FORKSRV_FD + 1); - close(t_fd[0]); - return; + afl_fork_child = 1; + close(FORKSRV_FD); + close(FORKSRV_FD + 1); + close(t_fd[0]); + return; + + } + + /* Parent. */ + + close(TSL_FD); + + } else { + + /* Special handling for persistent mode: if the child is alive but + currently stopped, simply restart it with SIGCONT. */ + + kill(child_pid, SIGCONT); + child_stopped = 0; } /* Parent. */ - close(TSL_FD); - if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5); /* Collect translation requests until child dies and closes the pipe. */ @@ -250,13 +304,79 @@ static void afl_forkserver(CPUState *cpu) { /* Get and relay exit status to parent. */ - if (waitpid(child_pid, &status, 0) < 0) exit(6); + if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) exit(6); + + /* In persistent mode, the child stops itself with SIGSTOP to indicate + a successful run. In this case, we want to wake it up without forking + again. */ + + if (WIFSTOPPED(status)) child_stopped = 1; + if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7); } } +/* A simplified persistent mode handler, used as explained in README.llvm. */ + +void afl_persistent_loop() { + + static u8 first_pass = 1; + static u32 cycle_cnt; + static struct afl_tsl exit_cmd_tsl = {{-1, 0, 0, 0}, NULL}; + + if (!afl_fork_child) return; + + if (first_pass) { + + /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate. + On subsequent calls, the parent will take care of that, but on the first + iteration, it's our job to erase any trace of whatever happened + before the loop. */ + + if (is_persistent) { + + memset(afl_area_ptr, 0, MAP_SIZE); + afl_area_ptr[0] = 1; + afl_prev_loc = 0; + + } + + cycle_cnt = afl_persistent_cnt; + first_pass = 0; + + return; + + } + + if (is_persistent) { + + if (--cycle_cnt) { + + if (write(TSL_FD, &exit_cmd_tsl, sizeof(struct afl_tsl)) != + sizeof(struct afl_tsl)) { + + /* Exit the persistent loop on pipe error */ + exit(0); + + } + + raise(SIGSTOP); + + afl_area_ptr[0] = 1; + afl_prev_loc = 0; + + } else { + + exit(0); + + } + + } + +} + /* This code is invoked whenever QEMU decides that it doesn't have a translation of a particular block and needs to compute it, or when it decides to chain two TBs together. When this happens, we tell the parent to @@ -330,6 +450,10 @@ static void afl_wait_tsl(CPUState *cpu, int fd) { if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break; + /* Exit command for persistent */ + + if (t.tb.pc == (target_ulong)(-1)) return; + tb = tb_htable_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask); if (!tb) { -- cgit 1.4.1 From 75d28813023c00144675eae7b75f4138ae9f317e Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 12 Sep 2019 16:57:17 +0200 Subject: ret addr patching --- include/alloc-inl.h | 34 ++++----------------- qemu_mode/libcompcov/compcovtest | Bin 0 -> 8624 bytes qemu_mode/patches/afl-qemu-common.h | 1 + qemu_mode/patches/afl-qemu-cpu-inl.h | 9 ++++-- qemu_mode/patches/afl-qemu-cpu-translate-inl.h | 39 ++++++++++++++++--------- 5 files changed, 37 insertions(+), 46 deletions(-) create mode 100755 qemu_mode/libcompcov/compcovtest (limited to 'qemu_mode/patches/afl-qemu-cpu-inl.h') diff --git a/include/alloc-inl.h b/include/alloc-inl.h index 814d8511..b0815ab1 100644 --- a/include/alloc-inl.h +++ b/include/alloc-inl.h @@ -106,44 +106,20 @@ \ } while (0) -/* -#define CHECK_PTR(_p) do { \ - \ - \ - \ - \ - \ +/* #define CHECK_PTR(_p) do { \ if (_p) { \ - \ - \ - \ - \ - \ if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\ - \ - \ - \ - \ - \ if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ ABORT("Use after free."); \ else ABORT("Corrupted head alloc canary."); \ - \ + } \ - \ - \ - \ - \ + if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ ABORT("Corrupted tail alloc canary."); \ - \ + } \ - \ - \ - \ - \ - \ - \ + } while (0) */ diff --git a/qemu_mode/libcompcov/compcovtest b/qemu_mode/libcompcov/compcovtest new file mode 100755 index 00000000..0bb68d60 Binary files /dev/null and b/qemu_mode/libcompcov/compcovtest differ diff --git a/qemu_mode/patches/afl-qemu-common.h b/qemu_mode/patches/afl-qemu-common.h index f05dc05b..053585a7 100644 --- a/qemu_mode/patches/afl-qemu-common.h +++ b/qemu_mode/patches/afl-qemu-common.h @@ -57,6 +57,7 @@ extern abi_ulong afl_persistent_ret_addr; extern u8 afl_compcov_level; extern unsigned char afl_fork_child; extern unsigned char is_persistent; +extern target_long persistent_stack_offset; extern __thread abi_ulong afl_prev_loc; diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h index 826167eb..2e685d8d 100644 --- a/qemu_mode/patches/afl-qemu-cpu-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-inl.h @@ -86,6 +86,7 @@ static int forkserver_installed = 0; unsigned char afl_fork_child; unsigned int afl_forksrv_pid; unsigned char is_persistent; +target_long persistent_stack_offset; /* Instrumentation ratio: */ @@ -200,9 +201,10 @@ static void afl_setup(void) { if (is_persistent) { afl_persistent_addr = strtoll(getenv("AFL_QEMU_PERSISTENT_ADDR"), NULL, 16); - if (getenv("AFL_QEMU_PERSISTENT_RET") == NULL) exit(1); - afl_persistent_ret_addr = - strtoll(getenv("AFL_QEMU_PERSISTENT_RET"), NULL, 16); + if (getenv("AFL_QEMU_PERSISTENT_RET")) + afl_persistent_ret_addr = + strtoll(getenv("AFL_QEMU_PERSISTENT_RET"), NULL, 16); + /* If AFL_QEMU_PERSISTENT_RET is not specified patch the return addr */ } @@ -345,6 +347,7 @@ void afl_persistent_loop() { cycle_cnt = afl_persistent_cnt; first_pass = 0; + persistent_stack_offset = TARGET_LONG_BITS / 8; return; diff --git a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h index fe1b26b8..cd5c21aa 100644 --- a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h +++ b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h @@ -128,19 +128,30 @@ static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2, } -#define AFL_QEMU_TARGET_i386_SNIPPET \ - if (is_persistent) { \ - \ - if (s->pc == afl_persistent_addr) { \ - \ - tcg_gen_afl_call0(&afl_persistent_loop); \ - \ - } else if (s->pc == afl_persistent_ret_addr) { \ - \ - gen_jmp_im(s, afl_persistent_addr); \ - gen_eob(s); \ - \ - } \ - \ +#define AFL_QEMU_TARGET_i386_SNIPPET \ + if (is_persistent) { \ + \ + if (s->pc == afl_persistent_addr) { \ + \ + if (afl_persistent_ret_addr == 0) { \ + \ + TCGv_ptr stack_off_ptr = tcg_const_ptr(&persistent_stack_offset); \ + TCGv stack_off = tcg_temp_new(); \ + tcg_gen_ld_tl(stack_off, stack_off_ptr, 0); \ + tcg_gen_sub_tl(cpu_regs[R_ESP], cpu_regs[R_ESP], stack_off); \ + tcg_temp_free(stack_off); \ + \ + } \ + TCGv_ptr paddr = tcg_const_ptr(afl_persistent_addr); \ + tcg_gen_st_tl(paddr, cpu_regs[R_ESP], 0); \ + tcg_gen_afl_call0(&afl_persistent_loop); \ + \ + } else if (afl_persistent_ret_addr && s->pc == afl_persistent_ret_addr) { \ + \ + gen_jmp_im(s, afl_persistent_addr); \ + gen_eob(s); \ + \ + } \ + \ } -- cgit 1.4.1