@@ -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)
@@ -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();
new file mode 100644
@@ -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;
+}