From patchwork Wed Mar 30 19:24:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Matlack X-Patchwork-Id: 8702231 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 68575C0553 for ; Wed, 30 Mar 2016 19:25:16 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 77B9C20361 for ; Wed, 30 Mar 2016 19:25:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7F93820357 for ; Wed, 30 Mar 2016 19:25:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753746AbcC3TYy (ORCPT ); Wed, 30 Mar 2016 15:24:54 -0400 Received: from mail-pa0-f42.google.com ([209.85.220.42]:35813 "EHLO mail-pa0-f42.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752085AbcC3TYw (ORCPT ); Wed, 30 Mar 2016 15:24:52 -0400 Received: by mail-pa0-f42.google.com with SMTP id td3so47384831pab.2 for ; Wed, 30 Mar 2016 12:24:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=bboKNCq9xF+0SNxntq7Uqbq4h6vkYbaYL+hXCGhc6tQ=; b=TAhH8MmoDB95er5Ce2NOXD2FLPQ1OtYz14JQD9vjzkU/MHbGXlf7J0XHn2zsYd2JCv uvvA39LRvCIQF2EmbcU8X08t2w6W/v3CO5BMLW8OOI2c+TepBX8Z7gex+ZOyEwYjKh6U ctkbbmFNKU7mssto1Pqg66A6lkGUbkOJJsdiF7H5idLNeHa8T0jHKjQ9yV/p8qh/8P1S HfJGbnBsqzLqofOwoMUdgcr/hUvtilRpG2QxTtS4TIV0rSxQUIoqx7rv8uyB84A+bFig 2h3YejAM/Hei/mhUqAMwI3uxnxLMGkd2idkpWKe3v+osoqEOllZ+5iAfo3lgLiJ/HjoB SP9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=bboKNCq9xF+0SNxntq7Uqbq4h6vkYbaYL+hXCGhc6tQ=; b=jYjYyBnkh6vzsgJJC/gtgwNoW/1vULhOWo3j3IXjNdQ1hEswAcrNr83RxDGxubUuYS KsSyxW5YhpxbtDWsBG2InDNxODQ0Ob+ps3yDvunUmTp2sT6L7qavyl58pkUnUFQ5+ttE Wof3VuHM871qnRreiP/sINgfpdon926hrDlQr34Xw0E/CNSXqA/im85WNh3k/EkyAcBq 6mvQl2jAy+2BfRTKOovF1uWZGmf7pszlrq4Fw8LfJ/sFzG4gTROivIUwYqK5tfzc0Yai 5pQi1UcN5W1wfeayFYNOmLkTmMebmbZkGRFosucqdT0u92FWwTWxEve1XhhGJUfdneG8 eekQ== X-Gm-Message-State: AD7BkJJ5AKbtqr2XRCno0zZw44rx8thcAYA7w2uvJfOy+ITSL9/LnGRqk7iq3l6JQ3blQv+F X-Received: by 10.66.123.17 with SMTP id lw17mr16046473pab.108.1459365891826; Wed, 30 Mar 2016 12:24:51 -0700 (PDT) Received: from dmatlack.sea.corp.google.com ([172.31.89.32]) by smtp.gmail.com with ESMTPSA id fk10sm7746918pab.33.2016.03.30.12.24.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 30 Mar 2016 12:24:51 -0700 (PDT) From: David Matlack To: kvm@vger.kernel.org Cc: pbonzini@redhat.com, linux-kernel@vger.kernel.org, luto@amacapital.net, David Matlack , stable@vger.kernel.org Subject: [PATCH] kvm: x86: do not leak guest xcr0 into host interrupt handlers Date: Wed, 30 Mar 2016 12:24:47 -0700 Message-Id: <1459365887-146735-1-git-send-email-dmatlack@google.com> X-Mailer: git-send-email 2.8.0.rc3.226.g39d4020 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP An interrupt handler that uses the fpu can kill a KVM VM, if it runs under the following conditions: - the guest's xcr0 register is loaded on the cpu - the guest's fpu context is not loaded - the host is using eagerfpu Note that the guest's xcr0 register and fpu context are not loaded as part of the atomic world switch into "guest mode". They are loaded by KVM while the cpu is still in "host mode". Usage of the fpu in interrupt context is gated by irq_fpu_usable(). The interrupt handler will look something like this: if (irq_fpu_usable()) { kernel_fpu_begin(); [... code that uses the fpu ...] kernel_fpu_end(); } As long as the guest's fpu is not loaded and the host is using eager fpu, irq_fpu_usable() returns true (interrupted_kernel_fpu_idle() returns true). The interrupt handler proceeds to use the fpu with the guest's xcr0 live. kernel_fpu_begin() saves the current fpu context. If this uses XSAVE[OPT], it may leave the xsave area in an undesirable state. According to the SDM, during XSAVE bit i of XSTATE_BV is not modified if bit i is 0 in xcr0. So it's possible that XSTATE_BV[i] == 1 and xcr0[i] == 0 following an XSAVE. kernel_fpu_end() restores the fpu context. Now if any bit i in XSTATE_BV == 1 while xcr0[i] == 0, XRSTOR generates a #GP. The fault is trapped and SIGSEGV is delivered to the current process. Only pre-4.2 kernels appear to be vulnerable to this sequence of events. Commit 653f52c ("kvm,x86: load guest FPU context more eagerly") from 4.2 forces the guest's fpu to always be loaded on eagerfpu hosts. This patch fixes the bug by keeping the host's xcr0 loaded outside of the interrupts-disabled region where KVM switches into guest mode. Cc: stable@vger.kernel.org Suggested-by: Andy Lutomirski Signed-off-by: David Matlack --- arch/x86/kvm/x86.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e260ccb..8df1167 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -700,7 +700,6 @@ static int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr) if ((xcr0 & XFEATURE_MASK_AVX512) != XFEATURE_MASK_AVX512) return 1; } - kvm_put_guest_xcr0(vcpu); vcpu->arch.xcr0 = xcr0; if ((xcr0 ^ old_xcr0) & XFEATURE_MASK_EXTEND) @@ -6590,8 +6589,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_x86_ops->prepare_guest_switch(vcpu); if (vcpu->fpu_active) kvm_load_guest_fpu(vcpu); - kvm_load_guest_xcr0(vcpu); - vcpu->mode = IN_GUEST_MODE; srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); @@ -6607,6 +6604,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) local_irq_disable(); + kvm_load_guest_xcr0(vcpu); + if (vcpu->mode == EXITING_GUEST_MODE || vcpu->requests || need_resched() || signal_pending(current)) { vcpu->mode = OUTSIDE_GUEST_MODE; @@ -6667,6 +6666,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) vcpu->mode = OUTSIDE_GUEST_MODE; smp_wmb(); + kvm_put_guest_xcr0(vcpu); + /* Interrupt is enabled by handle_external_intr() */ kvm_x86_ops->handle_external_intr(vcpu); @@ -7314,7 +7315,6 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu) * and assume host would use all available bits. * Guest xcr0 would be loaded later. */ - kvm_put_guest_xcr0(vcpu); vcpu->guest_fpu_loaded = 1; __kernel_fpu_begin(); __copy_kernel_to_fpregs(&vcpu->arch.guest_fpu.state); @@ -7323,8 +7323,6 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu) void kvm_put_guest_fpu(struct kvm_vcpu *vcpu) { - kvm_put_guest_xcr0(vcpu); - if (!vcpu->guest_fpu_loaded) { vcpu->fpu_counter = 0; return;