diff mbox

[AFL] Fuzzing the hypervisor

Message ID 20170817101500.1410-1-eggi.innovations@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Felix Schmoll Aug. 17, 2017, 10:15 a.m. UTC
Changes based on version 2.43b of AFL
---
 Makefile   |   2 +-
 afl-fuzz.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 hash_map.h |  82 ++++++++++++++++++++++
 3 files changed, 306 insertions(+), 11 deletions(-)
 create mode 100644 hash_map.h
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 44d1ffa..3a75c8c 100644
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,7 @@  afl-as: afl-as.c afl-as.h $(COMM_HDR) | test_x86
 	ln -sf afl-as as
 
 afl-fuzz: afl-fuzz.c $(COMM_HDR) | test_x86
-	$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
+	$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS) -lxenctrl
 
 afl-showmap: afl-showmap.c $(COMM_HDR) | test_x86
 	$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
diff --git a/afl-fuzz.c b/afl-fuzz.c
index 562fd50..08fa346 100644
--- a/afl-fuzz.c
+++ b/afl-fuzz.c
@@ -56,6 +56,10 @@ 
 #include <sys/ioctl.h>
 #include <sys/file.h>
 
+#include <xenctrl.h>
+#include <xen/public/trace_pc.h>
+#include "hash_map.h"
+
 #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__)
 #  include <sys/sysctl.h>
 #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */
@@ -80,6 +84,25 @@ 
    really makes no sense to haul them around as function parameters. */
 
 
+/* number of buckets in the hash map */
+#define NUM_BUCKETS 1000
+/* number of bytes written to xtf */
+#define SIZE_MEM_WRITE_TO_TESTCASE 80
+/* path for the file containing the last test case */
+#define TEST_CASE_LOG_PATH "/var/log/testcase"
+/* path for the file containing all test cases */
+#define OVERALL_LOG_PATH "/var/log/afl.log"
+#define XENCONSOLE_PATH "/usr/local/lib/xen/bin/xenconsole"
+
+int pipefd_to_xtf[2];
+int pipefd_from_xtf[2];
+char* domain;
+hash_map* map;
+
+long mem_write_to_testcase[SIZE_MEM_WRITE_TO_TESTCASE];
+
+FILE* log_file;
+
 EXP_ST u8 *in_dir,                    /* Input directory with test cases  */
           *out_file,                  /* File to fuzz, if any             */
           *out_dir,                   /* Working & output directory       */
@@ -2253,6 +2276,107 @@  EXP_ST void init_forkserver(char** argv) {
 }
 
 
+/** Process program counters into format expected by AFL and
+    insert into trace_bits.
+
+    cur_location = <COMPILE_TIME_RANDOM>;
+    shared_mem[cur_location ^ prev_location]++;
+    prev_location = cur_location >> 1;
+*/
+void process_program_counters(uint64_t* pc_buffer, long pc_num) {
+
+  u32 cur_location = 0, prev_location = 0;
+  hash_bucket* bucket;
+
+  for(int i = 0; i < pc_num; ++i) {
+    bucket = _hash_map_lookup(map, pc_buffer[i]);
+
+    if( !bucket ) {
+       cur_location = UR(MAP_SIZE);
+       if(!_hash_map_insert(map, pc_buffer[i], cur_location))
+         FATAL("process_program_counters: Could not insert into hash map\n");
+    } else {
+      cur_location = bucket->val;
+    }
+
+    trace_bits[(cur_location ^ prev_location) % MAP_SIZE]++;
+    prev_location = cur_location >> 1;
+  }
+
+}
+
+/* Send test case to XTF-server. */
+
+static u8 send_test_to_xtf(char** argv, u32 timeout) {
+
+  size_t buf_size = 100;
+  char buffer[buf_size];
+
+  int pc_buffer_size = 100000;
+  uint64_t pc_buffer[pc_buffer_size];
+  long ret;
+
+  /* log the test case that is about to be send */
+  if( fprintf(log_file, "get_cur_time %ld %li %li %ld %ld %ld\n",
+                         (long) get_cur_time(),
+                         mem_write_to_testcase[0] % 41,
+                         mem_write_to_testcase[1],
+                         mem_write_to_testcase[2],
+                         mem_write_to_testcase[3],
+                         mem_write_to_testcase[4]) < 0 )
+    FATAL("send_test_to_xtf: Couldn't write to file\n");
+
+  fflush(log_file);
+  memset(trace_bits, 0, MAP_SIZE);
+
+  xc_interface *xch = xc_interface_open(NULL, NULL, 0);
+  if( xch == NULL ) {
+    fclose(log_file);
+    FATAL("send_test_to_xtf: Couldn't open xen interface\n");
+  }
+
+  if( xc_trace_pc(xch, atoi(domain), XEN_TRACE_PC_START,
+                  pc_buffer_size, pc_buffer) < 0 ) {
+    fclose(log_file);
+    xc_interface_close(xch);
+    FATAL("send_test_to_xtf: Start edge_trace failed\n");
+  }
+
+  /* send tet case to XTF */
+  if( write(pipefd_to_xtf[1], (char*) mem_write_to_testcase,
+              SIZE_MEM_WRITE_TO_TESTCASE) <= 0 ) {
+     fclose(log_file);
+     xc_interface_close(xch);
+     FATAL("send_test_to_xtf: Couldn't write to XTF\n");
+  }
+
+  /* XTF will message us when it's hypercall returned */
+  if( read(pipefd_from_xtf[0], buffer, buf_size) < 0 ) {
+     fclose(log_file);
+     xc_interface_close(xch);
+     FATAL("send_test_to_xtf: Couldn't read from XTF\n");
+  }
+
+  ret = xc_trace_pc(xch, atoi(domain), 1, pc_buffer_size, pc_buffer);
+  xc_interface_close(xch);
+
+  if( ret < 0 ) {
+     fclose(log_file);
+     FATAL("send_test_to_xtf: Stop edge_trace failed\n");
+  }
+
+  process_program_counters(pc_buffer, ret);
+
+#ifdef __x86_64__
+  classify_counts((u64*)trace_bits);
+#else
+  classify_counts((u32*)trace_bits);
+#endif /* ^__x86_64__ */
+
+  return FAULT_NONE;
+}
+
+
 /* Execute target application, monitoring for timeouts. Return status
    information. The called program will update trace_bits[]. */
 
@@ -2455,13 +2579,21 @@  static u8 run_target(char** argv, u32 timeout) {
 
 }
 
-
 /* Write modified data to file for testing. If out_file is set, the old file
    is unlinked and a new one is created. Otherwise, out_fd is rewound and
    truncated. */
 
 static void write_to_testcase(void* mem, u32 len) {
 
+  memset((char*) mem_write_to_testcase, 0, SIZE_MEM_WRITE_TO_TESTCASE);
+  memcpy((char*) mem_write_to_testcase, mem, (len < SIZE_MEM_WRITE_TO_TESTCASE)?len:SIZE_MEM_WRITE_TO_TESTCASE);
+
+  remove(TEST_CASE_LOG_PATH);
+  s32 my_file = open(TEST_CASE_LOG_PATH, O_WRONLY | O_CREAT | O_EXCL , 0600);
+  ck_write(my_file, mem, SIZE_MEM_WRITE_TO_TESTCASE, TEST_CASE_LOG_PATH);
+  fsync(my_file);
+  close(my_file);
+
   s32 fd = out_fd;
 
   if (out_file) {
@@ -2490,6 +2622,15 @@  static void write_to_testcase(void* mem, u32 len) {
 
 static void write_with_gap(void* mem, u32 len, u32 skip_at, u32 skip_len) {
 
+  memset((char*) mem_write_to_testcase, 0, SIZE_MEM_WRITE_TO_TESTCASE);
+  memcpy((char*) mem_write_to_testcase, mem, (len < SIZE_MEM_WRITE_TO_TESTCASE)?len:SIZE_MEM_WRITE_TO_TESTCASE);
+
+  remove(TEST_CASE_LOG_PATH);
+  s32 my_file = open(TEST_CASE_LOG_PATH, O_WRONLY | O_CREAT | O_EXCL , 0600);
+  ck_write(my_file, mem, SIZE_MEM_WRITE_TO_TESTCASE, TEST_CASE_LOG_PATH);
+  fsync(my_file);
+  close(my_file);
+
   s32 fd = out_fd;
   u32 tail_len = len - skip_at - skip_len;
 
@@ -2568,7 +2709,7 @@  static u8 calibrate_case(char** argv, struct queue_entry* q, u8* use_mem,
 
     write_to_testcase(use_mem, q->len);
 
-    fault = run_target(argv, use_tmout);
+    fault = send_test_to_xtf(argv, use_tmout);
 
     /* stop_soon is set by the handler for Ctrl+C. When it's pressed,
        we want to bail out quickly. */
@@ -3198,7 +3339,7 @@  static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {
 
         u8 new_fault;
         write_to_testcase(mem, len);
-        new_fault = run_target(argv, hang_tmout);
+        new_fault = send_test_to_xtf(argv, hang_tmout);
 
         if (stop_soon || new_fault != FAULT_TMOUT) return keeping;
 
@@ -4479,7 +4620,7 @@  static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {
 
       write_with_gap(in_buf, q->len, remove_pos, trim_avail);
 
-      fault = run_target(argv, exec_tmout);
+      fault = send_test_to_xtf(argv, exec_tmout);
       trim_execs++;
 
       if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;
@@ -4572,7 +4713,7 @@  EXP_ST u8 common_fuzz_stuff(char** argv, u8* out_buf, u32 len) {
 
   write_to_testcase(out_buf, len);
 
-  fault = run_target(argv, exec_tmout);
+  fault = send_test_to_xtf(argv, exec_tmout);
 
   if (stop_soon) return 1;
 
@@ -6701,7 +6842,7 @@  static void sync_fuzzers(char** argv) {
 
         write_to_testcase(mem, st.st_size);
 
-        fault = run_target(argv, exec_tmout);
+        fault = send_test_to_xtf(argv, exec_tmout);
 
         if (stop_soon) return;
 
@@ -7665,6 +7806,73 @@  static void save_cmdline(u32 argc, char** argv) {
 
 }
 
+/**
+  * @param domid_s Point to domid of XTF
+  *
+  * This function does initial setup needed for the fuzzing. It also
+  * sets up pipes such that stdout and stdin can be used to communicate
+  * with the XTF-server.
+  *
+  * TODO      make this two separate functions
+  */
+static void setup_pipe_and_fork(char *domid_s) {
+  map = _hash_map_create(NUM_BUCKETS);
+
+  if(!map) {
+    FATAL("setup_pipe_and_fork: Hash map could not be created\n");
+  }
+
+  log_file = fopen(OVERALL_LOG_PATH, "w");
+
+  if(log_file < 0)
+    goto FAIL;
+
+  pid_t childpid;
+  int ret;
+
+  if( (ret = pipe(pipefd_to_xtf)) < 0)
+    goto FAIL;
+
+  if( (ret = pipe(pipefd_from_xtf)) < 0)
+    goto FAIL;
+
+  if((childpid = fork()) == -1)
+    FATAL("setup_pipe_and_fork: fork failed");
+
+  if (childpid == 0) { /* child */
+
+    /* close unnecessary pipe ends */
+    close(pipefd_to_xtf[1]);
+    close(pipefd_from_xtf[0]);
+
+    /* stdin */
+    close(0);
+    if( (ret = dup(pipefd_to_xtf[0])) < 0 )
+      goto FAIL;
+
+    /* stdout */
+    close(1);
+    if( (ret = dup(pipefd_from_xtf[1])) < 0 )
+      goto FAIL;
+
+    if( execl(XENCONSOLE_PATH, XENCONSOLE_PATH, domid_s, "--num", "0",
+              "--type", "pv", "--interactive", (void *)NULL) < 0 )
+      FATAL("setup_pipe_and_fork: execl");
+
+  } else { /* parent */
+
+    /* close unnecessary pipe ends */
+    close(pipefd_to_xtf[0]);
+    close(pipefd_from_xtf[1]);
+
+  }
+
+  return;
+
+  FAIL:
+    FATAL("setup_pipe_and_fork: Failed to setup pipes\n");
+}
+
 
 #ifndef AFL_LIB
 
@@ -7690,7 +7898,7 @@  int main(int argc, char** argv) {
   gettimeofday(&tv, &tz);
   srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
 
-  while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Q")) > 0)
+  while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:Qr:")) > 0)
 
     switch (opt) {
 
@@ -7858,6 +8066,12 @@  int main(int argc, char** argv) {
 
         break;
 
+      case 'r': /* domain id for XTF server */
+
+        domain = optarg;
+        printf("domain set: %s\n", domain);
+        break;
+
       default:
 
         usage(argv[0]);
@@ -7935,8 +8149,6 @@  int main(int argc, char** argv) {
 
   if (!out_file) setup_stdio_file();
 
-  check_binary(argv[optind]);
-
   start_time = get_cur_time();
 
   if (qemu_mode)
@@ -7944,6 +8156,8 @@  int main(int argc, char** argv) {
   else
     use_argv = argv + optind;
 
+  setup_pipe_and_fork(domain);
+
   perform_dry_run(use_argv);
 
   cull_queue();
@@ -7966,7 +8180,6 @@  int main(int argc, char** argv) {
   }
 
   while (1) {
-
     u8 skipped_fuzz;
 
     cull_queue();
diff --git a/hash_map.h b/hash_map.h
new file mode 100644
index 0000000..e5e6602
--- /dev/null
+++ b/hash_map.h
@@ -0,0 +1,82 @@ 
+/* @file hash_map.h
+ *
+ */
+
+typedef struct hash_bucket {
+    struct hash_bucket* next;
+    uint64_t key;
+    uint64_t val;
+} hash_bucket;
+
+typedef struct hash_map {
+    hash_bucket** buckets;
+    int num_buckets;
+} hash_map;
+
+uint64_t _hash_function(hash_map* map, uint64_t key) {
+    return (key % map->num_buckets);
+}
+
+hash_map* _hash_map_create(int num_buckets) {
+    hash_map* map = (hash_map*) malloc(sizeof(hash_map));
+
+    if( map ) {
+      map->num_buckets = num_buckets;
+      map->buckets = (hash_bucket**) malloc(sizeof(hash_bucket*) * num_buckets);
+
+       if( !map->buckets ) {
+         free(map);
+         return NULL;
+       }
+
+       for(int i = 0; i < num_buckets; ++i)
+         map->buckets[i] = NULL;
+    }
+
+    return map;
+}
+
+void _hash_map_destroy(hash_map* map) {
+  //not implemented
+}
+
+hash_bucket* _hash_map_lookup(hash_map* map, uint64_t key) {
+    uint64_t hash = _hash_function(map, key);
+
+    if(map->buckets[hash] == NULL) {
+      return NULL;
+    } else {
+      hash_bucket* cur = map->buckets[hash];
+
+      while(cur->key != key && cur->next)
+        cur = cur->next;
+
+      return cur;
+    }
+}
+
+bool _hash_map_insert(hash_map* map, uint64_t key, uint64_t val) {
+    uint64_t hash = _hash_function(map, key);
+
+    hash_bucket* bucket = (hash_bucket*) malloc(sizeof(hash_bucket));
+
+    if(!bucket)
+      return false;
+
+    bucket->next = NULL;
+    bucket->key = key;
+    bucket->val = val;
+
+    if(map->buckets[hash] == NULL) {
+      map->buckets[hash] = bucket;
+    } else {
+      hash_bucket* cur = map->buckets[hash];
+      while(cur->next) {
+        cur = cur->next;
+      }
+
+      cur->next = bucket;
+    }
+
+    return true;
+}