From patchwork Tue Apr 2 13:01:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Beulich X-Patchwork-Id: 10881655 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D4B7215AC for ; Tue, 2 Apr 2019 13:03:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C027228660 for ; Tue, 2 Apr 2019 13:03:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B44E9288C3; Tue, 2 Apr 2019 13:03:18 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, 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 AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B700728660 for ; Tue, 2 Apr 2019 13:03:17 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1hBJ2T-0007iu-DF; Tue, 02 Apr 2019 13:01:17 +0000 Received: from us1-rack-dfw2.inumbo.com ([104.130.134.6]) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1hBJ2R-0007in-Sn for xen-devel@lists.xenproject.org; Tue, 02 Apr 2019 13:01:15 +0000 X-Inumbo-ID: 653c9a20-5547-11e9-bc90-bc764e045a96 Received: from prv1-mh.provo.novell.com (unknown [137.65.248.33]) by us1-rack-dfw2.inumbo.com (Halon) with ESMTPS id 653c9a20-5547-11e9-bc90-bc764e045a96; Tue, 02 Apr 2019 13:01:14 +0000 (UTC) Received: from INET-PRV1-MTA by prv1-mh.provo.novell.com with Novell_GroupWise; Tue, 02 Apr 2019 07:01:13 -0600 Message-Id: <5CA35D130200007800223E53@prv1-mh.provo.novell.com> X-Mailer: Novell GroupWise Internet Agent 18.1.0 Date: Tue, 02 Apr 2019 07:01:07 -0600 From: "Jan Beulich" To: "xen-devel" References: <5C9E31960200007800222DDF@prv1-mh.provo.novell.com> Mime-Version: 1.0 Content-Disposition: inline Subject: [Xen-devel] [PATCH v3] x86emul/fuzz: add a state sanity checking function X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: George Dunlap , Andrew Cooper , Wei Liu , Roger Pau Monne Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP This is to accompany sanitize_input(). Just like for initial state we want to have state between two emulated insns sane, at least as far as assumptions in the main emulator go. Do minimal checking after segment register, CR, and MSR writes, and roll back to the old value in case of failure (raising #GP(0) at the same time). In the particular case observed, a CR0 write clearing CR0.PE was followed by a VEX-encoded insn, which the decoder accepts based on guest address size, restricting things just outside of the 64-bit case (real and virtual modes don't allow VEX-encoded insns). Subsequently _get_fpu() would then assert that CR0.PE must be set (and EFLAGS.VM clear) when trying to invoke YMM, ZMM, or OPMASK state. Signed-off-by: Jan Beulich Reviewed-by: George Dunlap --- v3: Rename helper function to check_state(). v2: Correct placement of new declaration in fuzz_write_segment(). --- a/tools/fuzz/x86_instruction_emulator/fuzz-emul.c +++ b/tools/fuzz/x86_instruction_emulator/fuzz-emul.c @@ -76,6 +76,8 @@ static inline bool input_read(struct fuz return true; } +static bool check_state(struct x86_emulate_ctxt *ctxt); + static const char* const x86emul_return_string[] = { [X86EMUL_OKAY] = "X86EMUL_OKAY", [X86EMUL_UNHANDLEABLE] = "X86EMUL_UNHANDLEABLE", @@ -424,8 +426,19 @@ static int fuzz_write_segment( rc = maybe_fail(ctxt, "write_segment", true); if ( rc == X86EMUL_OKAY ) + { + struct segment_register old = c->segments[seg]; + c->segments[seg] = *reg; + if ( !check_state(ctxt) ) + { + c->segments[seg] = old; + x86_emul_hw_exception(13 /* #GP */, 0, ctxt); + rc = X86EMUL_EXCEPTION; + } + } + return rc; } @@ -452,6 +465,7 @@ static int fuzz_write_cr( { struct fuzz_state *s = ctxt->data; struct fuzz_corpus *c = s->corpus; + unsigned long old; int rc; if ( reg >= ARRAY_SIZE(c->cr) ) @@ -461,9 +475,17 @@ static int fuzz_write_cr( if ( rc != X86EMUL_OKAY ) return rc; + old = c->cr[reg]; c->cr[reg] = val; - return X86EMUL_OKAY; + if ( !check_state(ctxt) ) + { + c->cr[reg] = old; + x86_emul_hw_exception(13 /* #GP */, 0, ctxt); + rc = X86EMUL_EXCEPTION; + } + + return rc; } #define fuzz_read_xcr emul_test_read_xcr @@ -561,7 +583,16 @@ static int fuzz_write_msr( { if ( msr_index[idx] == reg ) { + uint64_t old = c->msr[idx]; + c->msr[idx] = val; + + if ( !check_state(ctxt) ) + { + c->msr[idx] = old; + break; + } + return X86EMUL_OKAY; } } @@ -811,6 +842,30 @@ static void sanitize_input(struct x86_em } } +/* + * Call this function from hooks potentially altering machine state into + * something that's not architecturally valid, yet which - as per above - + * the emulator relies on. + */ +static bool check_state(struct x86_emulate_ctxt *ctxt) +{ + const struct fuzz_state *s = ctxt->data; + const struct fuzz_corpus *c = s->corpus; + const struct cpu_user_regs *regs = &c->regs; + + if ( long_mode_active(ctxt) && !(c->cr[0] & X86_CR0_PG) ) + return false; + + if ( (c->cr[0] & X86_CR0_PG) && !(c->cr[0] & X86_CR0_PE) ) + return false; + + if ( (regs->rflags & X86_EFLAGS_VM) && + (c->segments[x86_seg_cs].db || c->segments[x86_seg_ss].db) ) + return false; + + return true; +} + int LLVMFuzzerInitialize(int *argc, char ***argv) { if ( !emul_test_init() )