about summary refs log tree commit diff
path: root/qemu_mode
diff options
context:
space:
mode:
Diffstat (limited to 'qemu_mode')
-rw-r--r--qemu_mode/README.qemu20
-rw-r--r--qemu_mode/libcompcov/README.compcov8
-rw-r--r--qemu_mode/libcompcov/libcompcov.so.c54
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-inl.h9
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-translate-inl.h9
-rw-r--r--qemu_mode/patches/i386-translate.diff6
6 files changed, 86 insertions, 20 deletions
diff --git a/qemu_mode/README.qemu b/qemu_mode/README.qemu
index 754c0259..cd8559ad 100644
--- a/qemu_mode/README.qemu
+++ b/qemu_mode/README.qemu
@@ -16,14 +16,16 @@ with afl-gcc.
 The usual performance cost is 2-5x, which is considerably better than
 seen so far in experiments with tools such as DynamoRIO and PIN.
 
-The idea and much of the implementation comes from Andrew Griffiths.
+The idea and much of the initial implementation comes from Andrew Griffiths.
+The actual implementation on QEMU 3 (shipped with afl++) is from
+Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining.
 
 2) How to use
 -------------
 
-The feature is implemented with a fairly simple patch to QEMU 2.10.0. The
-simplest way to build it is to run ./build_qemu_support.sh. The script will
-download, configure, and compile the QEMU binary for you.
+The feature is implemented with a patch to QEMU 3.1.0. The simplest way
+to build it is to run ./build_qemu_support.sh. The script will download,
+configure, and compile the QEMU binary for you.
 
 QEMU is a big project, so this will take a while, and you may have to
 resolve a couple of dependencies (most notably, you will definitely need
@@ -53,10 +55,18 @@ There is ./libcompcov/ which implements laf-intel (splitting memcmp,
 strncmp, etc. to make these conditions easier solvable by afl-fuzz).
 Highly recommended.
 
+The option that enables QEMU CompareCoverage is QEMU_COMPCOV_LEVEL.
+QEMU_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate
+values / read-only memory. QEMU_COMPCOV_LEVEL=2 instruments all
+comparison instructions and memory comparison functions when libcompcov
+is preloaded. Comparison instructions are currently instrumented only
+on the x86 and x86_64 targets.
+
 Another option is the environment variable AFL_ENTRYPOINT which allows
 move the forkserver to a different part, e.g. just before the file is
 opened (e.g. way after command line parsing and config file loading, etc)
-which can be a huge speed improvement.
+which can be a huge speed improvement. Note that the specified address
+must be an address of a basic block.
 
 4) Notes on linking
 -------------------
diff --git a/qemu_mode/libcompcov/README.compcov b/qemu_mode/libcompcov/README.compcov
index 2a4a0ee5..9be13d88 100644
--- a/qemu_mode/libcompcov/README.compcov
+++ b/qemu_mode/libcompcov/README.compcov
@@ -18,15 +18,19 @@ For optimized binaries this is an issue, those functions are often inlined
 and this module is not capable to log the coverage in this case.
 
 If you have the source code of the fuzzing target you should nto use this
-library and QEMU but build ot with afl-clang-fast and the laf-intel options.
+library and QEMU but build it with afl-clang-fast and the laf-intel options.
 
 To use this library make sure to preload it with AFL_PRELOAD.
 
   export AFL_PRELOAD=/path/to/libcompcov.so
-  export AFL_QEMU_COMPCOV=1
+  export AFL_COMPCOV_LEVEL=1
   
   afl-fuzz -Q -i input -o output <your options> -- <target args>
 
+The AFL_COMPCOV_LEVEL tells to QEMU and libcompcov how to log comaprisons.
+Level 1 logs just comparison with immediates / read-only memory and level 2
+logs all the comparisons.
+
 The library make use of https://github.com/ouadev/proc_maps_parser and so it is
 Linux specific. However this is not a strict dependency, other UNIX operating
 systems can be supported simply replacing the code related to the
diff --git a/qemu_mode/libcompcov/libcompcov.so.c b/qemu_mode/libcompcov/libcompcov.so.c
index 582230db..92e4dbaa 100644
--- a/qemu_mode/libcompcov/libcompcov.so.c
+++ b/qemu_mode/libcompcov/libcompcov.so.c
@@ -45,6 +45,8 @@ static void *__compcov_code_start,
 
 static u8 *__compcov_afl_map;
 
+static u32 __compcov_level;
+
 static int (*__libc_strcmp)(const char*, const char*);
 static int (*__libc_strncmp)(const char*, const char*, size_t);
 static int (*__libc_strcasecmp)(const char*, const char*);
@@ -54,6 +56,28 @@ static int (*__libc_memcmp)(const void*, const void*, size_t);
 static int debug_fd = -1;
 
 
+#define MAX_MAPPINGS 1024
+
+static struct mapping {
+  void *st, *en;
+} __compcov_ro[MAX_MAPPINGS];
+
+static u32   __compcov_ro_cnt;
+
+
+/* Check an address against the list of read-only mappings. */
+
+static u8 __compcov_is_ro(const void* ptr) {
+
+  u32 i;
+
+  for (i = 0; i < __compcov_ro_cnt; i++) 
+    if (ptr >= __compcov_ro[i].st && ptr <= __compcov_ro[i].en) return 1;
+
+  return 0;
+}
+
+
 static size_t __strlen2(const char *s1, const char *s2, size_t max_length) {
   // from https://github.com/googleprojectzero/CompareCoverage
   
@@ -71,6 +95,15 @@ static void __compcov_load(void) {
   __libc_strcasecmp = dlsym(RTLD_NEXT, "strcasecmp");
   __libc_strncasecmp = dlsym(RTLD_NEXT, "strncasecmp");
   __libc_memcmp = dlsym(RTLD_NEXT, "memcmp");
+
+  if (getenv("AFL_QEMU_COMPCOV")) {
+
+    __compcov_level = 1;
+  }
+  if (getenv("AFL_COMPCOV_LEVEL")) {
+
+    __compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
+  }
   
   char *id_str = getenv(SHM_ENV_VAR);
   int shm_id;
@@ -110,6 +143,12 @@ static void __compcov_load(void) {
             __compcov_code_end = maps_tmp->addr_end;
       }
     }
+    
+    if ((maps_tmp->is_w && !maps_tmp->is_r) || __compcov_ro_cnt == MAX_MAPPINGS)
+      continue;
+    
+    __compcov_ro[__compcov_ro_cnt].st = maps_tmp->addr_start;
+    __compcov_ro[__compcov_ro_cnt].en = maps_tmp->addr_end;
   }
 
   pmparser_free(maps);
@@ -149,7 +188,8 @@ int strcmp(const char* str1, const char* str2) {
 
   void* retaddr = __builtin_return_address(0);
   
-  if (__compcov_is_in_bound(retaddr)) {
+  if (__compcov_is_in_bound(retaddr) && !(__compcov_level < 2 &&
+      !__compcov_is_ro(str1) && !__compcov_is_ro(str2))) {
 
     size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
     
@@ -173,7 +213,8 @@ int strncmp(const char* str1, const char* str2, size_t len) {
 
   void* retaddr = __builtin_return_address(0);
   
-  if (__compcov_is_in_bound(retaddr)) {
+  if (__compcov_is_in_bound(retaddr) && !(__compcov_level < 2 &&
+      !__compcov_is_ro(str1) && !__compcov_is_ro(str2))) {
 
     size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
     n = MIN(n, len);
@@ -198,7 +239,8 @@ int strcasecmp(const char* str1, const char* str2) {
 
   void* retaddr = __builtin_return_address(0);
   
-  if (__compcov_is_in_bound(retaddr)) {
+  if (__compcov_is_in_bound(retaddr) && !(__compcov_level < 2 &&
+      !__compcov_is_ro(str1) && !__compcov_is_ro(str2))) {
     /* Fallback to strcmp, maybe improve in future */
 
     size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
@@ -223,7 +265,8 @@ int strncasecmp(const char* str1, const char* str2, size_t len) {
 
   void* retaddr = __builtin_return_address(0);
   
-  if (__compcov_is_in_bound(retaddr)) {
+  if (__compcov_is_in_bound(retaddr) && !(__compcov_level < 2 &&
+      !__compcov_is_ro(str1) && !__compcov_is_ro(str2))) {
     /* Fallback to strncmp, maybe improve in future */
 
     size_t n = __strlen2(str1, str2, MAX_CMP_LENGTH +1);
@@ -249,7 +292,8 @@ int memcmp(const void* mem1, const void* mem2, size_t len) {
 
   void* retaddr = __builtin_return_address(0);
   
-  if (__compcov_is_in_bound(retaddr)) {
+  if (__compcov_is_in_bound(retaddr) && !(__compcov_level < 2 &&
+      !__compcov_is_ro(mem1) && !__compcov_is_ro(mem2))) {
 
     size_t n = len;
     
diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h
index 86203a5b..d7bb4d25 100644
--- a/qemu_mode/patches/afl-qemu-cpu-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-inl.h
@@ -66,7 +66,7 @@ abi_ulong afl_entry_point, /* ELF entry point (_start) */
           afl_start_code,  /* .text start pointer      */
           afl_end_code;    /* .text end pointer        */
 
-u8 afl_enable_compcov;
+u8 afl_compcov_level;
 
 /* Set in the child process in forkserver mode: */
 
@@ -159,9 +159,14 @@ static void afl_setup(void) {
 
   }
   
+  /* Maintain for compatibility */
   if (getenv("AFL_QEMU_COMPCOV")) {
 
-    afl_enable_compcov = 1;
+    afl_compcov_level = 1;
+  }
+  if (getenv("AFL_COMPCOV_LEVEL")) {
+
+    afl_compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
   }
 
   /* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
diff --git a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
index 0ca89c98..4716c2ac 100644
--- a/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
+++ b/qemu_mode/patches/afl-qemu-cpu-translate-inl.h
@@ -40,7 +40,7 @@
 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;
+extern u8 afl_compcov_level;
 
 void tcg_gen_afl_compcov_log_call(void *func, target_ulong cur_loc,
                                   TCGv_i64 arg1, TCGv_i64 arg2);
@@ -95,11 +95,14 @@ static void afl_compcov_log_64(target_ulong cur_loc, target_ulong arg1,
 
 
 static void afl_gen_compcov(target_ulong cur_loc, TCGv_i64 arg1, TCGv_i64 arg2,
-                            TCGMemOp ot) {
+                            TCGMemOp ot, int is_imm) {
 
   void *func;
   
-  if (!afl_enable_compcov || cur_loc > afl_end_code || cur_loc < afl_start_code)
+  if (!afl_compcov_level || cur_loc > afl_end_code || cur_loc < afl_start_code)
+    return;
+  
+  if (!is_imm && afl_compcov_level < 2)
     return;
 
   switch (ot) {
diff --git a/qemu_mode/patches/i386-translate.diff b/qemu_mode/patches/i386-translate.diff
index 0bc48828..239b2404 100644
--- a/qemu_mode/patches/i386-translate.diff
+++ b/qemu_mode/patches/i386-translate.diff
@@ -15,11 +15,11 @@ index 0dd5fbe4..b95d341e 100644
              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);
++            afl_gen_compcov(s1->pc, s1->cc_srcT, s1->T1, ot, d == OR_EAX);
          } 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);
++            afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot, d == OR_EAX);
              gen_op_st_rm_T0_A0(s1, ot, d);
          }
          gen_op_update2_cc(s1);
@@ -27,7 +27,7 @@ index 0dd5fbe4..b95d341e 100644
          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);
++        afl_gen_compcov(s1->pc, s1->T0, s1->T1, ot, d == OR_EAX);
          set_cc_op(s1, CC_OP_SUBB + ot);
          break;
      }