From patchwork Wed Oct 11 17:52:42 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: George Dunlap X-Patchwork-Id: 10000267 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 A132460244 for ; Wed, 11 Oct 2017 18:00:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 995E928B01 for ; Wed, 11 Oct 2017 18:00:41 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8E4EC28B04; Wed, 11 Oct 2017 18:00: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 E555028B01 for ; Wed, 11 Oct 2017 18:00: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 1e2LGx-00022h-1X; Wed, 11 Oct 2017 17:58:23 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1e2LGv-00022b-Hf for xen-devel@lists.xenproject.org; Wed, 11 Oct 2017 17:58:21 +0000 Received: from [85.158.139.211] by server-6.bemta-5.messagelabs.com id B0/C8-07895-CBB5ED95; Wed, 11 Oct 2017 17:58:20 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprCIsWRWlGSWpSXmKPExsXitHRDpO6e6Hu RBm928Fh83zKZyYHR4/CHKywBjFGsmXlJ+RUJrBmTt7kUHDKt2L7tPVsD4yu1LkYODgkBf4lZ 9zi6GDk52AT0JOYd/8oCEhYRUJG4vdegi5GLg1lgP6NE79cfbCA1wgLuEp9nnWMBsVkEVCX6t j1kAqnnFbCTeNMDNkZCQF7i/YL7jCA2J0j48X1WEFtIwFZixdK9jBC2qsTiB0fZQWxeAUGJkz OfgI1kFpCQOPjiBfMERt5ZSFKzkKQWMDKtYtQoTi0qSy3SNTTXSyrKTM8oyU3MzNE1NDDVy00 tLk5MT81JTCrWS87P3cQIDBsGINjBePG05yFGSQ4mJVHeYL17kUJ8SfkplRmJxRnxRaU5qcWH GGU4OJQkeI9EAeUEi1LTUyvSMnOAAQyTluDgURLhZQJJ8xYXJOYWZ6ZDpE4xGnMc23T5DxNHx 827f5iEWPLy81KlxHlzQEoFQEozSvPgBsEi6xKjrJQwLyPQaUI8BalFuZklqPKvGMU5GJWEeX VApvBk5pXA7XsFdAoT0CmiaXdATilJREhJNTC+TXAqLD4UlvA04VQPi37acbk3n79H6kVP0u1 w9N5TE2r49VvndufO6v4ml75Qt1Uacot2H2l+LqFnrjip+4aI3P+Wm98C8ixnK/d1LJgWwJbK cGXy1c7f3q/enD3o/IVh+ufmDMaOooCGx7Oat97f4Wi7vbGce7LIjQkPHdPL/rzZarfQU1SJp Tgj0VCLuag4EQB1Tv3upwIAAA== X-Env-Sender: prvs=450528267=George.Dunlap@citrix.com X-Msg-Ref: server-14.tower-206.messagelabs.com!1507744698!67716706!1 X-Originating-IP: [66.165.176.89] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogNjYuMTY1LjE3Ni44OSA9PiAyMDMwMDc=\n, received_headers: No Received headers X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 50224 invoked from network); 11 Oct 2017 17:58:19 -0000 Received: from smtp.citrix.com (HELO SMTP.CITRIX.COM) (66.165.176.89) by server-14.tower-206.messagelabs.com with RC4-SHA encrypted SMTP; 11 Oct 2017 17:58:19 -0000 X-IronPort-AV: E=Sophos;i="5.43,362,1503360000"; d="scan'208";a="445493475" From: George Dunlap To: Date: Wed, 11 Oct 2017 18:52:42 +0100 Message-ID: <20171011175243.19871-11-george.dunlap@citrix.com> X-Mailer: git-send-email 2.14.2 In-Reply-To: <20171011175243.19871-1-george.dunlap@citrix.com> References: <20171011175243.19871-1-george.dunlap@citrix.com> MIME-Version: 1.0 Cc: Ian Jackson , Wei Liu , George Dunlap , Jan Beulich , Andrew Cooper Subject: [Xen-devel] [PATCH v4 11/12] fuzz/x86_emulate: Set and fuzz more CPU state 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 x86_emulate() operates not only on state passed to it in cpu_user_regs, but also on state currently found on the cpu: namely, the FPU and XMM registers. At the moment, we re-zero (and/or re-initialize) cpu_user_regs on every invocation, but leave the cpu-stored state alone. In "persistent mode", this causes test cases to behave differently -- sometimes significantly so -- depending on which test cases have been run beforehand. Zero out the state before each test run, and then fuzz it based on the corpus input. The Intel manual claims that, "If [certain CPUID bits] are set, the processor deprecates FCS and FDS, and the field is saved as 0000h"; but experimentally it would be more accurate to say, "the field is occasionally saved as 0000h". This causes the --rerun checking to trip non-deterministically. Sanitize them to zero. Signed-off-by: George Dunlap --- v4: - Remove ineffective fxrstor - Sanitize fcs and fds elements v3: - Make type 512 bytes rather than 464 - Style changes - Change argument from 'store' to 'write' - Add a comment explaining why we always 'save' even for a write - Sanitize mxcsr with mxcrs_mask when writing instead of zeroing it in sanitize_state - Get rid of redundant mxcsr_mask setting - Add comments explaining why we're arbitrarily writing 32 bits v2: Rebase on top of previous changes CC: Ian Jackson CC: Wei Liu CC: Andrew Cooper CC: Jan Beulich --- tools/fuzz/x86_instruction_emulator/fuzz-emul.c | 102 +++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c index f1621f98da..881c4d03c1 100644 --- a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c +++ b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c @@ -36,6 +36,7 @@ struct fuzz_state uint64_t msr[MSR_INDEX_MAX]; struct segment_register segments[SEG_NUM]; struct cpu_user_regs regs; + char fxsave[512] __attribute__((aligned(16))); /* Fuzzer's input data. */ #define DATA_SIZE_FULL offsetof(struct fuzz_state, corpus) @@ -594,6 +595,75 @@ static const struct x86_emulate_ops all_fuzzer_ops = { }; #undef SET +/* + * This funciton will read or write fxsave to the fpu. When writing, + * it 'sanitizes' the state: It will mask off the appropriate bits in + * the mxcsr, 'restore' the state to the fpu, then 'save' it again so + * that the data in fxsave reflects what's actually in the FPU. + * + * TODO: Extend state beyond just FPU (ymm registers, &c) + */ +static void _set_fpu_state(char *fxsave, bool write) +{ + if ( cpu_has_fxsr ) + { + static union __attribute__((__aligned__(16))) { + char x[512]; + struct { + uint16_t cw, sw; + uint8_t tw, _rsvd1; + uint16_t op; + uint32_t ip; + uint16_t cs, _rsvd2; + uint32_t dp; + uint16_t ds, _rsvd3; + uint32_t mxcsr; + uint32_t mxcsr_mask; + /* ... */ + }; + } *fxs; + + fxs = (typeof(fxs))fxsave; + + if ( write ) + { + /* + * Clear reserved bits to make sure we don't get any + * exceptions + */ + fxs->mxcsr &= mxcsr_mask; + + /* + * The Intel manual says that on newer models CS/DS are + * deprecated and that these fields "are saved as 0000h". + * Experimentally, however, at least on my test box, + * whether this saved as 0000h or as the previously + * written value is random; meaning that when run with + * --rerun, we occasionally detect a "state mismatch" in these + * bytes. Instead, simply sanitize them to zero. + * + * TODO Check CPUID as specified in the manual before + * clearing + */ + fxs->cs = fxs->ds = 0; + + asm volatile( "fxrstor %0" :: "m" (*fxs) ); + } + + asm volatile( "fxsave %0" : "=m" (*fxs) ); + } +} + +static void set_fpu_state(char *fxsave) +{ + _set_fpu_state(fxsave, true); +} + +static void save_fpu_state(char *fxsave) +{ + _set_fpu_state(fxsave, false); +} + static void setup_fpu_exception_handler(void) { /* FIXME - just disable exceptions for now */ @@ -669,7 +739,11 @@ static void setup_state(struct x86_emulate_ctxt *ctxt) return; } - /* Modify only select bits of state */ + /* + * Modify only select bits of state. In general, try not to fuzz less + * than 32 bits at a time; otherwise we're reading 2 bytes in order to fuzz only + * one byte. + */ /* Always read 'options' */ if ( !input_read(s, s, DATA_SIZE_COMPACT) ) @@ -732,6 +806,18 @@ static void setup_state(struct x86_emulate_ctxt *ctxt) printf("Setting cpu_user_regs offset %x\n", offset); continue; } + offset -= sizeof(struct cpu_user_regs); + + /* Fuzz fxsave state */ + if ( offset < sizeof(s->fxsave) / 4 ) + { + /* 32-bit size is arbitrary; see comment above */ + if ( !input_read(s, s->fxsave + (offset * 4), 4) ) + return; + printf("Setting fxsave offset %x\n", offset * 4); + continue; + } + offset -= 128; /* None of the above -- take that as "start emulating" */ @@ -914,6 +1000,8 @@ static int runtest(struct fuzz_state *state) { disable_hooks(state); + set_fpu_state(state->fxsave); + do { /* FIXME: Until we actually implement SIGFPE handling properly */ setup_fpu_exception_handler(); @@ -925,6 +1013,8 @@ static int runtest(struct fuzz_state *state) { printf("Emulation result: %d\n", rc); } while ( rc == X86EMUL_OKAY ); + save_fpu_state(state->fxsave); + return 0; } @@ -1008,6 +1098,16 @@ static void compare_states(struct fuzz_state state[2]) if ( memcmp(&state[0].ops, &state[1].ops, sizeof(state[0].ops)) ) printf("ops differ!\n"); + if ( memcmp(&state[0].fxsave, &state[1].fxsave, sizeof(state[0].fxsave)) ) + { + printf("fxsave differs!\n"); + for ( i = 0; i < sizeof(state[0].fxsave)/sizeof(unsigned); i++ ) + { + printf("[%04lu] %08x %08x\n", + i * sizeof(unsigned), ((unsigned *)&state[0].fxsave)[i], ((unsigned *)&state[1].fxsave)[i]); + } + } + if ( memcmp(&state[0].ctxt, &state[1].ctxt, sizeof(state[0].ctxt)) ) { printf("ctxt differs!\n");