experimental x86 support for compcov in QEMU

This commit is contained in:
Andrea Fioraldi
2019-07-19 00:55:41 +02:00
parent 5b2cb426be
commit 81dd1aea82
6 changed files with 320 additions and 10 deletions

View File

@ -133,6 +133,7 @@ patch -p1 <../patches/cpu-exec.diff || exit 1
patch -p1 <../patches/syscall.diff || exit 1 patch -p1 <../patches/syscall.diff || exit 1
patch -p1 <../patches/translate-all.diff || exit 1 patch -p1 <../patches/translate-all.diff || exit 1
patch -p1 <../patches/tcg.diff || exit 1 patch -p1 <../patches/tcg.diff || exit 1
patch -p1 <../patches/i386-translate.diff || exit 1
echo "[+] Patching done." echo "[+] Patching done."

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -65,6 +66,8 @@ abi_ulong afl_entry_point, /* ELF entry point (_start) */
afl_start_code, /* .text start pointer */ afl_start_code, /* .text start pointer */
afl_end_code; /* .text end pointer */ afl_end_code; /* .text end pointer */
u8 afl_enable_compcov;
/* Set in the child process in forkserver mode: */ /* Set in the child process in forkserver mode: */
static int forkserver_installed = 0; static int forkserver_installed = 0;
@ -157,6 +160,12 @@ static void afl_setup(void) {
} }
if (getenv("AFL_QEMU_COMPCOV")) {
fprintf(stderr, "EEe\n");
afl_enable_compcov = 1;
}
/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm /* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
not entirely sure what is the cause. This disables that not entirely sure what is the cause. This disables that
behaviour, and seems to work alright? */ behaviour, and seems to work alright? */

View File

@ -0,0 +1,125 @@
/*
american fuzzy lop - high-performance binary-only instrumentation
-----------------------------------------------------------------
Written by Andrew Griffiths <agriffiths@google.com> and
Michal Zalewski <lcamtuf@google.com>
Idea & design very much by Andrew Griffiths.
TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com>
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. 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 QEMU 3.1.0. 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 QEMU 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 "tcg.h"
#include "tcg-op.h"
/* Declared in afl-qemu-cpu-inl.h */
extern unsigned char *afl_area_ptr;
extern unsigned int afl_inst_rms;
extern abi_ulong afl_start_code, afl_end_code;
extern u8 afl_enable_compcov;
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
TCGv_i64 arg1, TCGv_i64 arg2);
static void afl_compcov_log_16(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
}
}
static void afl_compcov_log_32(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
afl_area_ptr[cur_loc +1]++;
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
afl_area_ptr[cur_loc +2]++;
}
}
}
}
static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
target_ulong arg2) {
if ((arg1 & 0xff) == (arg2 & 0xff)) {
afl_area_ptr[cur_loc]++;
if ((arg1 & 0xffff) == (arg2 & 0xffff)) {
afl_area_ptr[cur_loc +1]++;
if ((arg1 & 0xffffff) == (arg2 & 0xffffff)) {
afl_area_ptr[cur_loc +2]++;
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffff)) {
afl_area_ptr[cur_loc +3]++;
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffffff)) {
afl_area_ptr[cur_loc +4]++;
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffffffff)) {
afl_area_ptr[cur_loc +5]++;
if ((arg1 & 0xffffffff) == (arg2 & 0xffffffffffffff)) {
afl_area_ptr[cur_loc +6]++;
}
}
}
}
}
}
}
}
static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
TCGMemOp ot) {
void *func;
if (!afl_enable_compcov || cur_loc > afl_end_code || cur_loc < afl_start_code)
return;
switch (ot) {
case MO_64:
func = &afl_compcov_log_64;
break;
case MO_32:
func = &afl_compcov_log_32;
break;
case MO_16:
func = &afl_compcov_log_16;
break;
default:
return;
}
cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
cur_loc &= MAP_SIZE - 1;
if (cur_loc >= afl_inst_rms) return;
tcg_gen_afl_compcov_log_call(func, cur_loc, arg1, arg2);
}

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -42,10 +43,10 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
unsigned sizemask, flags; unsigned sizemask, flags;
TCGOp *op; TCGOp *op;
TCGTemp *arg = tcgv_ptr_temp( tcg_const_tl(cur_loc) ); TCGTemp *arg = tcgv_i64_temp( tcg_const_tl(cur_loc) );
flags = 0; flags = 0;
sizemask = dh_sizemask(void, 0) | dh_sizemask(ptr, 1); sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1);
#if defined(__sparc__) && !defined(__arch64__) \ #if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER) && !defined(CONFIG_TCG_INTERPRETER)
@ -151,7 +152,7 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them. /* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
Note that describing these as TCGv_i64 eliminates an unnecessary Note that describing these as TCGv_i64 eliminates an unnecessary
zero-extension that tcg_gen_concat_i32_i64 would create. */ zero-extension that tcg_gen_concat_i32_i64 would create. */
tcg_gen_concat32_i64(temp_tcgv_i64(ret), retl, reth); tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
tcg_temp_free_i64(retl); tcg_temp_free_i64(retl);
tcg_temp_free_i64(reth); tcg_temp_free_i64(reth);
} }
@ -163,3 +164,143 @@ void tcg_gen_afl_maybe_log_call(target_ulong cur_loc)
#endif /* TCG_TARGET_EXTEND_ARGS */ #endif /* TCG_TARGET_EXTEND_ARGS */
} }
void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2)
{
int i, real_args, nb_rets, pi;
unsigned sizemask, flags;
TCGOp *op;
const int nargs = 3;
TCGTemp *args[3] = { tcgv_i64_temp( tcg_const_tl(cur_loc) ),
tcgv_i64_temp(arg1),
tcgv_i64_temp(arg2) };
flags = 0;
sizemask = dh_sizemask(void, 0) | dh_sizemask(i64, 1) |
dh_sizemask(i64, 2) | dh_sizemask(i64, 3);
#if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER)
/* We have 64-bit values in one register, but need to pass as two
separate parameters. Split them. */
int orig_sizemask = sizemask;
int orig_nargs = nargs;
TCGv_i64 retl, reth;
TCGTemp *split_args[MAX_OPC_PARAM];
retl = NULL;
reth = NULL;
if (sizemask != 0) {
for (i = real_args = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (is_64bit) {
TCGv_i64 orig = temp_tcgv_i64(args[i]);
TCGv_i32 h = tcg_temp_new_i32();
TCGv_i32 l = tcg_temp_new_i32();
tcg_gen_extr_i64_i32(l, h, orig);
split_args[real_args++] = tcgv_i32_temp(h);
split_args[real_args++] = tcgv_i32_temp(l);
} else {
split_args[real_args++] = args[i];
}
}
nargs = real_args;
args = split_args;
sizemask = 0;
}
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
for (i = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
int is_signed = sizemask & (2 << (i+1)*2);
if (!is_64bit) {
TCGv_i64 temp = tcg_temp_new_i64();
TCGv_i64 orig = temp_tcgv_i64(args[i]);
if (is_signed) {
tcg_gen_ext32s_i64(temp, orig);
} else {
tcg_gen_ext32u_i64(temp, orig);
}
args[i] = tcgv_i64_temp(temp);
}
}
#endif /* TCG_TARGET_EXTEND_ARGS */
op = tcg_emit_op(INDEX_op_call);
pi = 0;
nb_rets = 0;
TCGOP_CALLO(op) = nb_rets;
real_args = 0;
for (i = 0; i < nargs; i++) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (TCG_TARGET_REG_BITS < 64 && is_64bit) {
#ifdef TCG_TARGET_CALL_ALIGN_ARGS
/* some targets want aligned 64 bit args */
if (real_args & 1) {
op->args[pi++] = TCG_CALL_DUMMY_ARG;
real_args++;
}
#endif
/* If stack grows up, then we will be placing successive
arguments at lower addresses, which means we need to
reverse the order compared to how we would normally
treat either big or little-endian. For those arguments
that will wind up in registers, this still works for
HPPA (the only current STACK_GROWSUP target) since the
argument registers are *also* allocated in decreasing
order. If another such target is added, this logic may
have to get more complicated to differentiate between
stack arguments and register arguments. */
#if defined(HOST_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
op->args[pi++] = temp_arg(args[i] + 1);
op->args[pi++] = temp_arg(args[i]);
#else
op->args[pi++] = temp_arg(args[i]);
op->args[pi++] = temp_arg(args[i] + 1);
#endif
real_args += 2;
continue;
}
op->args[pi++] = temp_arg(args[i]);
real_args++;
}
op->args[pi++] = (uintptr_t)func;
op->args[pi++] = flags;
TCGOP_CALLI(op) = real_args;
/* Make sure the fields didn't overflow. */
tcg_debug_assert(TCGOP_CALLI(op) == real_args);
tcg_debug_assert(pi <= ARRAY_SIZE(op->args));
#if defined(__sparc__) && !defined(__arch64__) \
&& !defined(CONFIG_TCG_INTERPRETER)
/* Free all of the parts we allocated above. */
for (i = real_args = 0; i < orig_nargs; ++i) {
int is_64bit = orig_sizemask & (1 << (i+1)*2);
if (is_64bit) {
tcg_temp_free_internal(args[real_args++]);
tcg_temp_free_internal(args[real_args++]);
} else {
real_args++;
}
}
if (orig_sizemask & 1) {
/* The 32-bit ABI returned two 32-bit pieces. Re-assemble them.
Note that describing these as TCGv_i64 eliminates an unnecessary
zero-extension that tcg_gen_concat_i32_i64 would create. */
tcg_gen_concat32_i64(temp_tcgv_i64(NULL), retl, reth);
tcg_temp_free_i64(retl);
tcg_temp_free_i64(reth);
}
#elif defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
for (i = 0; i < nargs; ++i) {
int is_64bit = sizemask & (1 << (i+1)*2);
if (!is_64bit) {
tcg_temp_free_internal(args[i]);
}
}
#endif /* TCG_TARGET_EXTEND_ARGS */
}

View File

@ -9,7 +9,8 @@
TCG instrumentation and block chaining support by Andrea Biondo TCG instrumentation and block chaining support by Andrea Biondo
<andrea.biondo965@gmail.com> <andrea.biondo965@gmail.com>
QEMU 3.1.0 port and thread-safety by Andrea Fioraldi
QEMU 3.1.0 port, TCG thread-safety and CompareCoverage by Andrea Fioraldi
<andreafioraldi@gmail.com> <andreafioraldi@gmail.com>
Copyright 2015, 2016, 2017 Google Inc. All rights reserved. Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
@ -41,12 +42,12 @@ extern abi_ulong afl_start_code, afl_end_code;
void tcg_gen_afl_maybe_log_call(target_ulong cur_loc); void tcg_gen_afl_maybe_log_call(target_ulong cur_loc);
void afl_maybe_log(void* cur_loc) { void afl_maybe_log(target_ulong cur_loc) {
static __thread abi_ulong prev_loc; static __thread abi_ulong prev_loc;
afl_area_ptr[(abi_ulong)cur_loc ^ prev_loc]++; afl_area_ptr[cur_loc ^ prev_loc]++;
prev_loc = (abi_ulong)cur_loc >> 1; prev_loc = cur_loc >> 1;
} }
@ -59,7 +60,7 @@ static void afl_gen_trace(target_ulong cur_loc) {
if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer if (cur_loc > afl_end_code || cur_loc < afl_start_code /*|| !afl_area_ptr*/) // not needed because of static dummy buffer
return; return;
/* Looks like QEMU always maps to fixed locations, so ASAN is not a /* Looks like QEMU always maps to fixed locations, so ASLR is not a
concern. Phew. But instruction addresses may be aligned. Let's mangle concern. Phew. But instruction addresses may be aligned. Let's mangle
the value to get something quasi-uniform. */ the value to get something quasi-uniform. */

View File

@ -0,0 +1,33 @@
diff --git a/target/i386/translate.c b/target/i386/translate.c
index 0dd5fbe4..b95d341e 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -32,6 +32,8 @@
#include "trace-tcg.h"
#include "exec/log.h"
+#include "../patches/afl-qemu-cpu-translate-inl.h"
+
#define PREFIX_REPZ 0x01
#define PREFIX_REPNZ 0x02
#define PREFIX_LOCK 0x04
@@ -1343,9 +1345,11 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0,
s1->mem_index, ot | MO_LE);
tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1);
+ afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot);
} else {
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1);
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
gen_op_st_rm_T0_A0(s1, ot, d);
}
gen_op_update2_cc(s1);
@@ -1389,6 +1393,7 @@ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d)
tcg_gen_mov_tl(cpu_cc_src, s1->T1);
tcg_gen_mov_tl(s1->cc_srcT, s1->T0);
tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1);
+ afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot);
set_cc_op(s1, CC_OP_SUBB + ot);
break;
}