From patchwork Thu Aug 17 10:15:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felix Schmoll X-Patchwork-Id: 9905583 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E8A5C60386 for ; Thu, 17 Aug 2017 10:17:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC0C328AA6 for ; Thu, 17 Aug 2017 10:17:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E0CD428AD0; Thu, 17 Aug 2017 10:17:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.6 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id F084E28AA6 for ; Thu, 17 Aug 2017 10:17:52 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1diHps-0003WR-15; Thu, 17 Aug 2017 10:15:32 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1diHpr-0003WJ-5c for xen-devel@lists.xenproject.org; Thu, 17 Aug 2017 10:15:31 +0000 Received: from [85.158.143.35] by server-4.bemta-6.messagelabs.com id 92/E6-02962-2CC65995; Thu, 17 Aug 2017 10:15:30 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrFIsWRWlGSWpSXmKPExsVyMbThsO7BnKm RBs8OSVp83zKZyYHR4/CHKywBjFGsmXlJ+RUJrBnrWz+wFZxMqni1LKCB8ZdPFyMXh5DADEaJ 3bM/MoE4LAIvWSS6TjwAcyQE+lkl1lw9ztzFyAHkpEk8fFQGYVZJHPzK18XICdSsKfGlu4sRY lADk8SU9/NYQBJsAgYSs1deZAaxRQSUJO6tmswEYjMLuEh0zWtmA7GFBbQlXr9ZBFbPIqAqse XBVDCbV8BOYs+nvWA1EgLyErvaLrKC2JwCgRIXdvexQywOkJi9dCLTBEaBBYwMqxg1ilOLylK LdI0s9JKKMtMzSnITM3N0DQ3M9HJTi4sT01NzEpOK9ZLzczcxAsOKAQh2MJ5fG3iIUZKDSUmU 9/esKZFCfEn5KZUZicUZ8UWlOanFhxhlODiUJHibsqdGCgkWpaanVqRl5gADHCYtwcGjJMLrC 5LmLS5IzC3OTIdInWK05LhyZd0XJo4pB7YDyVcT/n9jEmLJy89LlRLnTQBpEABpyCjNgxsHi8 JLjLJSwryMQAcK8RSkFuVmlqDKv2IU52BUEuZdADKFJzOvBG7rK6CDmIAOutI+CeSgkkSElFQ Do+yDlS6O1lsM1eUZM+WP3/MPs1jM9ar+nPGh+obXTiIV/Z3iiXK6nBvWJV54+UDs7rcpeWHO G4Nd08sNwpP9k1oY5+cdjfLdWuc0Oaz478ZdXhmtFRM4Gc4IxOfwf9yd4hV2YTsL44T0l+e/b 1XpPvHvtIqswGIvtQXrDCt6WzkUas/kRT9RYinOSDTUYi4qTgQAts9xFL0CAAA= X-Env-Sender: eggi.innovations@gmail.com X-Msg-Ref: server-4.tower-21.messagelabs.com!1502964929!72127096!1 X-Originating-IP: [209.85.128.195] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 43210 invoked from network); 17 Aug 2017 10:15:29 -0000 Received: from mail-wr0-f195.google.com (HELO mail-wr0-f195.google.com) (209.85.128.195) by server-4.tower-21.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 17 Aug 2017 10:15:29 -0000 Received: by mail-wr0-f195.google.com with SMTP id x43so7581890wrb.1 for ; Thu, 17 Aug 2017 03:15:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=CHcNOhXZfnDSXyxqWLFSAPv9MTRSRGRA5elWk4TDIpc=; b=o3RFdO9sSf45QBGnbAp5nVsFQUyP8q101VxSGKxvk4dUdU5HjOtSMUiGCbCUmYwH+e auMWP5zX2dA+M61ZF8BDR1Gn4ITDMJhFOHK0TRE9/Vk9Pyz8xlteUB0aIvfRO2xCv9c9 x0d1HxoCnoMQzxOBKtNpcZd9Zj5c7vWPTJIkNv+VYE40LAMDwMfr5RaVdQqCWMEgbuen 7W/xDW7jwxTlFu8WTgn1JbyG/YaxFB8mTJKQg6kUPE5u3FJd+R1k/ldilH5vVhirpuRF VFSpy2PTRwWt7vHuT+JgYgH1Siob3xdLzY/OIE97e5UjgLFsh3mThAQfVMAq1oB8xwHe f3oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CHcNOhXZfnDSXyxqWLFSAPv9MTRSRGRA5elWk4TDIpc=; b=K258GeXs6/RHOllg05DZTJjiwFr+PmpzHTkp41PigoYxWJrFqmzVNnBDsDyo4oJDFM W/WhjCOLRbUieJqwZpXGhhRQsX1sJXZWL8yXGTBAGBw0m/KpCF9Sq8VoodxAG8cWlhm4 lSd6MzSO14IW/DjXKXkoDo2bQJoY4KpFuq1KN/EoNjqRSEvl8tk9jr+VHm2s83G/2pEK cIl2q7FQUgE5Dtbm5XMIWVvSvDStBaWSBXRXdQ21XJ4vU2i3dftqzRMDf0tG5/UbtRpe UQS+I6+xyJHR7uXbJQKmHN2eK9mDvKWN0ht77ecWdk2L/MhMVmS5HHuAZ00WxUKSkDux WmFQ== X-Gm-Message-State: AHYfb5hzyWfaBDKZJqiNIDRh+Sil7hte2JeMDBxJGejR4IO0OhwlRSN+ doNcByaND1bimYRtPF+S4Q== X-Received: by 10.223.150.48 with SMTP id b45mr3233297wra.139.1502964928476; Thu, 17 Aug 2017 03:15:28 -0700 (PDT) Received: from localhost.localdomain ([213.55.184.183]) by smtp.gmail.com with ESMTPSA id v17sm2621327wrg.24.2017.08.17.03.15.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 17 Aug 2017 03:15:27 -0700 (PDT) From: Felix Schmoll To: xen-devel@lists.xenproject.org Date: Thu, 17 Aug 2017 12:15:00 +0200 Message-Id: <20170817101500.1410-1-eggi.innovations@gmail.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: References: Cc: wei.liu2@citrix.com, Felix Schmoll Subject: [Xen-devel] [PATCH AFL] Fuzzing the hypervisor X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --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 #include +#include +#include +#include "hash_map.h" + #if defined(__APPLE__) || defined(__FreeBSD__) || defined (__OpenBSD__) # include #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 = ; + 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; +}