From patchwork Mon Sep 25 14:26:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: George Dunlap X-Patchwork-Id: 9970069 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 6842260225 for ; Mon, 25 Sep 2017 14:30:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5864228811 for ; Mon, 25 Sep 2017 14:30:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4CF7628C98; Mon, 25 Sep 2017 14:30:41 +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=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED 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 7EA7F28CD2 for ; Mon, 25 Sep 2017 14:30:40 +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 1dwUN0-0004YJ-AW; Mon, 25 Sep 2017 14:28:26 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dwUMy-0004W7-HF for xen-devel@lists.xenproject.org; Mon, 25 Sep 2017 14:28:24 +0000 Received: from [193.109.254.147] by server-10.bemta-6.messagelabs.com id BE/6B-03642-78219C95; Mon, 25 Sep 2017 14:28:23 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprOIsWRWlGSWpSXmKPExsXitHSDvW6L0Ml Ig+n/2Cy+b5nM5MDocfjDFZYAxijWzLyk/IoE1ozOS/1sBd3hFT8XPmZsYJxi38XIySEh4C/x 7sUtZhCbTUBPYt7xryxdjBwcIgIqErf3GnQxcnEwC+xnlOj9+oMNpEZYIF7i75ZnLCA2i4Cqx LbJx1lBbF4BO4lbHZ9ZIWbKS5x7cBtsJidQ/PXtKewgtpCArcTs3f9YIWxVicUPjrJD9ApKnJ z5BGwms4CExMEXL5gnMPLOQpKahSS1gJFpFaNGcWpRWWqRrpGRXlJRZnpGSW5iZo6uoYGZXm5 qcXFiempOYlKxXnJ+7iZGYPAwAMEOxjXzAw8xSnIwKYny3uU7ESnEl5SfUpmRWJwRX1Sak1p8 iFGGg0NJgveI4MlIIcGi1PTUirTMHGAYw6QlOHiURHhngaR5iwsSc4sz0yFSpxiNOQ69uP2Hi aPj5t0/TEIsefl5qVLivGtASgVASjNK8+AGweLrEqOslDAvI9BpQjwFqUW5mSWo8q8YxTkYlY R5r4BM4cnMK4Hb9wroFCagU3qnngA5pSQRISXVwDh76aMVcw0LQ/pa1gl6iFUvP6yS23P08w4 hhoUlH/4K90h2MKnJ6D9tnyRkWXDtbn1q9cu9C/bZHMheFSHl2/dlXUln3YEP9/ev7C2o9T0j NTfgdHHoNz6rvwIFSed6nrW/ezNhcZKXs+U8g4PB36wXH5+j7OtU/Km7k9PQmFPmWlrlrKzJi kosxRmJhlrMRcWJAGYLZciqAgAA X-Env-Sender: prvs=43409e5e2=George.Dunlap@citrix.com X-Msg-Ref: server-16.tower-27.messagelabs.com!1506349698!118091176!1 X-Originating-IP: [66.165.176.63] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogNjYuMTY1LjE3Ni42MyA9PiAzMDYwNDg=\n, received_headers: No Received headers X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 43625 invoked from network); 25 Sep 2017 14:28:19 -0000 Received: from smtp02.citrix.com (HELO SMTP02.CITRIX.COM) (66.165.176.63) by server-16.tower-27.messagelabs.com with RC4-SHA encrypted SMTP; 25 Sep 2017 14:28:19 -0000 X-IronPort-AV: E=Sophos;i="5.42,436,1500940800"; d="scan'208";a="449106015" From: George Dunlap To: Date: Mon, 25 Sep 2017 15:26:46 +0100 Message-ID: <20170925142648.25959-11-george.dunlap@citrix.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20170925142648.25959-1-george.dunlap@citrix.com> References: <20170925142648.25959-1-george.dunlap@citrix.com> MIME-Version: 1.0 Cc: Ian Jackson , Wei Liu , George Dunlap , Jan Beulich , Andrew Cooper Subject: [Xen-devel] [PATCH v2 11/13] fuzz/x86_emulate: Add --rerun option to try to track down instability 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: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Current stability numbers are not 100%. In order to help track this down, add a --rerun option which will run the same input twice, resetting the state in between each run, and comparing the state afterwards. If the state differs, call abort(). This allows AFL to help the process of tracking down what state is not being reset properly between runs by proving testcases that demonstrate the behavior. To do this: - Move ctxt into struct fuzz-state to simplify handling - Rather than copying the data into input, treat the data handed as immutable and point each "copy" to it - Factor out various steps (setting up fuzz state, running an individual test) so that they can be efficiently run either once or twice (as necessary) - Compare the states afterwards, printing what's different and calling abort() if anything is found. Signed-off-by: George Dunlap --- v2: - Fix some coding style issues - Port over previous changes CC: Ian Jackson CC: Wei Liu CC: Andrew Cooper CC: Jan Beulich --- tools/fuzz/x86_instruction_emulator/afl-harness.c | 9 +- tools/fuzz/x86_instruction_emulator/fuzz-emul.c | 188 ++++++++++++++++++---- 2 files changed, 165 insertions(+), 32 deletions(-) diff --git a/tools/fuzz/x86_instruction_emulator/afl-harness.c b/tools/fuzz/x86_instruction_emulator/afl-harness.c index 806f54d606..6b0f66f923 100644 --- a/tools/fuzz/x86_instruction_emulator/afl-harness.c +++ b/tools/fuzz/x86_instruction_emulator/afl-harness.c @@ -14,6 +14,7 @@ extern unsigned int fuzz_minimal_input_size(void); static uint8_t input[INPUT_SIZE]; extern bool opt_compact; +extern bool opt_rerun; int main(int argc, char **argv) { @@ -32,10 +33,12 @@ int main(int argc, char **argv) enum { OPT_MIN_SIZE, OPT_COMPACT, + OPT_RERUN, }; static const struct option lopts[] = { { "min-input-size", no_argument, NULL, OPT_MIN_SIZE }, { "compact", required_argument, NULL, OPT_COMPACT }, + { "rerun", no_argument, NULL, OPT_RERUN }, { 0, 0, 0, 0 } }; int c = getopt_long_only(argc, argv, "", lopts, NULL); @@ -54,8 +57,12 @@ int main(int argc, char **argv) opt_compact = atoi(optarg); break; + case OPT_RERUN: + opt_rerun = true; + break; + case '?': - printf("Usage: %s [--compact=0|1] $FILE [$FILE...] | [--min-input-size]\n", argv[0]); + printf("Usage: %s [--compact=0|1] [--rerun] $FILE [$FILE...] | [--min-input-size]\n", argv[0]); exit(-1); break; diff --git a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c index d99a50d12c..21d00b7416 100644 --- a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c +++ b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c @@ -40,7 +40,7 @@ struct fuzz_state struct cpu_user_regs regs; /* Fuzzer's input data. */ - struct fuzz_corpus *corpus; + const struct fuzz_corpus *corpus; /* Real amount of data backing corpus->data[]. */ size_t data_num; @@ -50,6 +50,7 @@ struct fuzz_state /* Emulation ops, some of which are disabled based on corpus->options. */ struct x86_emulate_ops ops; + struct x86_emulate_ctxt ctxt; }; #define DATA_OFFSET offsetof(struct fuzz_state, corpus) @@ -496,6 +497,12 @@ static int fuzz_read_msr( const struct fuzz_state *s = ctxt->data; unsigned int idx; + /* + * NB at the moment dump_state() relies on the fact that this + * cannot fail. If we add in fuzzed failures we'll have to handle + * that differently. + */ + switch ( reg ) { case MSR_TSC_AUX: @@ -616,6 +623,7 @@ static void dump_state(struct x86_emulate_ctxt *ctxt) printf(" rip: %"PRIx64"\n", regs->rip); + /* read_msr() never fails at the moment */ fuzz_read_msr(MSR_EFER, &val, ctxt); printf("EFER: %"PRIx64"\n", val); } @@ -660,7 +668,10 @@ static void setup_state(struct x86_emulate_ctxt *ctxt) { /* Fuzz all of the state in one go */ if (!input_read(s, s, DATA_OFFSET)) + { + printf("Input size too small\n"); exit(-1); + } return; } @@ -789,9 +800,8 @@ enum { printf("Disabling hook "#h"\n"); \ } -static void disable_hooks(struct x86_emulate_ctxt *ctxt) +static void disable_hooks(struct fuzz_state *s) { - struct fuzz_state *s = ctxt->data; unsigned long bitmap = s->options; /* See also sanitize_input, some hooks can't be disabled. */ @@ -839,7 +849,7 @@ static void disable_hooks(struct x86_emulate_ctxt *ctxt) * - ...bases to below 1Mb, 16-byte aligned * - ...selectors to (base >> 4) */ -static void sanitize_input(struct x86_emulate_ctxt *ctxt) +static void sanitize_state(struct x86_emulate_ctxt *ctxt) { struct fuzz_state *s = ctxt->data; struct cpu_user_regs *regs = ctxt->regs; @@ -886,21 +896,138 @@ int LLVMFuzzerInitialize(int *argc, char ***argv) return 0; } -int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size) +void setup_fuzz_state(struct fuzz_state *state, const uint8_t *data_p, size_t size) { - struct fuzz_state state = { - .ops = all_fuzzer_ops, - }; - struct x86_emulate_ctxt ctxt = { - .data = &state, - .regs = &state.regs, - .addr_size = 8 * sizeof(void *), - .sp_size = 8 * sizeof(void *), - }; + memset(state, 0, sizeof(*state)); + state->corpus = (struct fuzz_corpus *)data_p; + state->data_num = size; +} + +int runtest(struct fuzz_state *state) { int rc; - /* Reset all global state variables */ - memset(&input, 0, sizeof(input)); + struct x86_emulate_ctxt *ctxt = &state->ctxt; + + state->ops = all_fuzzer_ops; + + ctxt->data = state; + ctxt->regs = &state->regs; + ctxt->addr_size = ctxt->sp_size = 8 * sizeof(void *); + + setup_state(ctxt); + + sanitize_state(ctxt); + + disable_hooks(state); + + do { + /* FIXME: Until we actually implement SIGFPE handling properly */ + setup_fpu_exception_handler(); + + set_sizes(ctxt); + dump_state(ctxt); + + rc = x86_emulate(ctxt, &state->ops); + printf("Emulation result: %d\n", rc); + } while ( rc == X86EMUL_OKAY ); + + return 0; +} + +void compare_states(struct fuzz_state state[2]) +{ + // First zero any "internal" pointers + state[0].corpus = state[1].corpus = NULL; + state[0].ctxt.data = state[1].ctxt.data = NULL; + state[0].ctxt.regs = state[1].ctxt.regs = NULL; + + + if ( memcmp(&state[0], &state[1], sizeof(struct fuzz_state)) ) + { + int i; + + printf("State mismatch\n"); + + for ( i=0; i<5; i++) + if ( state[0].cr[i] != state[1].cr[i] ) + printf("cr[%d]: %lx != %lx\n", + i, state[0].cr[i], state[1].cr[i]); + + for ( i=0; i sizeof(input) ) + if ( size > sizeof(struct fuzz_corpus) ) { printf("Input too large\n"); return 1; @@ -916,25 +1043,24 @@ int LLVMFuzzerTestOneInput(const uint8_t *data_p, size_t size) memcpy(&input, data_p, size); - state.corpus = &input; - state.data_num = size; - - setup_state(&ctxt); + setup_fuzz_state(&state[0], data_p, size); + + if ( opt_rerun ) + printf("||| INITIAL RUN |||\n"); + + runtest(&state[0]); - sanitize_input(&ctxt); + if ( !opt_rerun ) + return 0; - disable_hooks(&ctxt); + /* Reset all global state variables again */ + setup_fuzz_state(&state[1], data_p, size); - do { - /* FIXME: Until we actually implement SIGFPE handling properly */ - setup_fpu_exception_handler(); + printf("||| SECOND RUN |||\n"); - set_sizes(&ctxt); - dump_state(&ctxt); + runtest(&state[1]); - rc = x86_emulate(&ctxt, &state.ops); - printf("Emulation result: %d\n", rc); - } while ( rc == X86EMUL_OKAY ); + compare_states(state); return 0; }