From nobody Wed Jul 1 05:28:18 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C457FC433FE for ; Tue, 28 Dec 2021 23:24:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237775AbhL1XYo (ORCPT ); Tue, 28 Dec 2021 18:24:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34160 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237760AbhL1XYn (ORCPT ); Tue, 28 Dec 2021 18:24:43 -0500 Received: from mail-pj1-x104a.google.com (mail-pj1-x104a.google.com [IPv6:2607:f8b0:4864:20::104a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EC467C06173E for ; Tue, 28 Dec 2021 15:24:42 -0800 (PST) Received: by mail-pj1-x104a.google.com with SMTP id p4-20020a17090a348400b001b103a13f69so16518151pjb.8 for ; Tue, 28 Dec 2021 15:24:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=reply-to:date:in-reply-to:message-id:mime-version:references :subject:from:to:cc; bh=lXOCiSUjAOZ34Jj0Nyj7RQEDujOi8kdviMryNWrmPbE=; b=j7LsVQbBIxQMwmKpw4VwDbzIkcGAyglJn91wnZMXP6V9BNwSdIFTGp6Z6lmXbkqqfu DjStgYMd5pYnVxnkazRAkxlCaNLNtjnjhb5PFbVYNuwqumDQNLHbdx9YQ00JyjZeZwm4 RRlerzdTTmw86bge5mO6GqanyiWNmNEjGmBP2evMzvVujjg/jeueq0MBp/Ux4GgS+POu 1idEIBM1DXaCbg74c9HgPt8DGqaqMr3b2r3DyvP736Gxnj11H5F+qirxoICiWR9KyIk0 CuxCDDSzIxciL4dFQR0aToz5e5ZaQfgFLC2ADed1FPd/tyg46Hi0JnERwcq9E5WBoRd6 67DA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:reply-to:date:in-reply-to:message-id :mime-version:references:subject:from:to:cc; bh=lXOCiSUjAOZ34Jj0Nyj7RQEDujOi8kdviMryNWrmPbE=; b=FwjK1E2y1DXjCWknvxDgYT9WDDjs5gQM/stMxxsb6k+oson04llH1tzPeln06ujbzb aNI0mJxQJ9SSQspj9IlGBmIASIH9hLntflseT6lFKb9nyxp/YxMXtEMJBqqDdSkICUgQ NfNohFGkXFDCBI1ZX42yY4MFQeoOQNSuLl7T1LEJe71ou49Pcvqztw3czT4PVnCshNdj zwyG/Awpb1WppvEYW5yWtcJfkoLM3UqS6psv+GFiwKcLlj6xaOkKNyQblWlVtRzVjBlo j5fO4d828rR8oPM6vh8/R0YbM5mkxqklotyr36VbVDGjfgp7XzDvnw/+TM30WsGkko21 CBBg== X-Gm-Message-State: AOAM532UkowM/uOoQNfeL5QzQuovxwy4I/QkJdNLnSujykMkxmRewRUW 1OmAyF/Uxo30OFySMT1saJrzLBE1y7E= X-Google-Smtp-Source: ABdhPJzTGYVOggBqNEn2z3mXiIKBL1Nc0MyqR75hnX8dIxIeHF+nsvcAIxSD1WRiWwg0WmV1IWUGSL3eVwc= X-Received: from seanjc.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:3e5]) (user=seanjc job=sendgmr) by 2002:a17:902:760e:b0:149:202b:4a9 with SMTP id k14-20020a170902760e00b00149202b04a9mr24078254pll.16.1640733882513; Tue, 28 Dec 2021 15:24:42 -0800 (PST) Reply-To: Sean Christopherson Date: Tue, 28 Dec 2021 23:24:36 +0000 In-Reply-To: <20211228232437.1875318-1-seanjc@google.com> Message-Id: <20211228232437.1875318-2-seanjc@google.com> Mime-Version: 1.0 References: <20211228232437.1875318-1-seanjc@google.com> X-Mailer: git-send-email 2.34.1.448.ga2b2bfdf31-goog Subject: [PATCH 1/2] KVM: VMX: Reject KVM_RUN if emulation is required with pending exception From: Sean Christopherson To: Paolo Bonzini Cc: Sean Christopherson , Vitaly Kuznetsov , Wanpeng Li , Jim Mattson , Joerg Roedel , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, syzbot+82112403ace4cbd780d8@syzkaller.appspotmail.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Reject KVM_RUN if emulation is required (because VMX is running without unrestricted guest) and an exception is pending, as KVM doesn't support emulating exceptions except when emulating real mode via vm86. The vCPU is hosed either way, but letting KVM_RUN proceed triggers a WARN due to the impossible condition. Alternatively, the WARN could be removed, but then userspace and/or KVM bugs would result in the vCPU silently running in a bad state, which isn't very friendly to users. Originally, the bug was hit by syzkaller with a nested guest as that doesn't require kvm_intel.unrestricted_guest=3D0. That particular flavor is likely fixed by commit cd0e615c49e5 ("KVM: nVMX: Synthesize TRIPLE_FAULT for L2 if emulation is required"), but it's trivial to trigger the WARN with a non-nested guest, and userspace can likely force bad state via ioctls() for a nested guest as well. Checking for the impossible condition needs to be deferred until KVM_RUN because KVM can't force specific ordering between ioctls. E.g. clearing exception.pending in KVM_SET_SREGS doesn't prevent userspace from setting it in KVM_SET_VCPU_EVENTS, and disallowing KVM_SET_VCPU_EVENTS with emulation_required would prevent userspace from queuing an exception and then stuffing sregs. Note, if KVM were to try and detect/prevent the condition prior to KVM_RUN, handle_invalid_guest_state() and/or handle_emulation_failure() would need to be modified to clear the pending exception prior to exiting to userspace. ------------[ cut here ]------------ WARNING: CPU: 6 PID: 137812 at arch/x86/kvm/vmx/vmx.c:1623 vmx_queue_excep= tion+0x14f/0x160 [kvm_intel] CPU: 6 PID: 137812 Comm: vmx_invalid_nes Not tainted 5.15.2-7cc36c3e14ae-p= op #279 Hardware name: ASUS Q87M-E/Q87M-E, BIOS 1102 03/03/2014 RIP: 0010:vmx_queue_exception+0x14f/0x160 [kvm_intel] Code: <0f> 0b e9 fd fe ff ff 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 RSP: 0018:ffffa45c83577d38 EFLAGS: 00010202 RAX: 0000000000000003 RBX: 0000000080000006 RCX: 0000000000000006 RDX: 0000000000000000 RSI: 0000000000010002 RDI: ffff9916af734000 RBP: ffff9916af734000 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000006 R13: 0000000000000000 R14: ffff9916af734038 R15: 0000000000000000 FS: 00007f1e1a47c740(0000) GS:ffff99188fb80000(0000) knlGS:00000000000000= 00 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f1e1a6a8008 CR3: 000000026f83b005 CR4: 00000000001726e0 Call Trace: kvm_arch_vcpu_ioctl_run+0x13a2/0x1f20 [kvm] kvm_vcpu_ioctl+0x279/0x690 [kvm] __x64_sys_ioctl+0x83/0xb0 do_syscall_64+0x3b/0xc0 entry_SYSCALL_64_after_hwframe+0x44/0xae Reported-by: syzbot+82112403ace4cbd780d8@syzkaller.appspotmail.com Signed-off-by: Sean Christopherson --- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 6 ++++++ arch/x86/kvm/vmx/vmx.c | 22 ++++++++++++++++++++-- arch/x86/kvm/x86.c | 12 +++++++++--- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-= x86-ops.h index 37624a9e3e40..631d5040b31e 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -55,6 +55,7 @@ KVM_X86_OP_NULL(tlb_remote_flush) KVM_X86_OP_NULL(tlb_remote_flush_with_range) KVM_X86_OP(tlb_flush_gva) KVM_X86_OP(tlb_flush_guest) +KVM_X86_OP(vcpu_pre_run) KVM_X86_OP(run) KVM_X86_OP_NULL(handle_exit) KVM_X86_OP_NULL(skip_emulated_instruction) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_hos= t.h index 5d97f4adc1cb..fad555b726a9 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1379,6 +1379,7 @@ struct kvm_x86_ops { */ void (*tlb_flush_guest)(struct kvm_vcpu *vcpu); =20 + int (*vcpu_pre_run)(struct kvm_vcpu *vcpu); enum exit_fastpath_completion (*run)(struct kvm_vcpu *vcpu); int (*handle_exit)(struct kvm_vcpu *vcpu, enum exit_fastpath_completion exit_fastpath); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 6cb38044a860..bb9748d14f79 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -3594,6 +3594,11 @@ static void svm_cancel_injection(struct kvm_vcpu *vc= pu) svm_complete_interrupts(vcpu); } =20 +static int svm_vcpu_pre_run(struct kvm_vcpu *vcpu) +{ + return 1; +} + static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) { if (to_svm(vcpu)->vmcb->control.exit_code =3D=3D SVM_EXIT_MSR && @@ -4423,6 +4428,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata =3D { .tlb_flush_gva =3D svm_flush_tlb_gva, .tlb_flush_guest =3D svm_flush_tlb, =20 + .vcpu_pre_run =3D svm_vcpu_pre_run, .run =3D svm_vcpu_run, .handle_exit =3D handle_exit, .skip_emulated_instruction =3D skip_emulated_instruction, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index fe06b02994e6..64d32fb728c9 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5386,6 +5386,14 @@ static int handle_nmi_window(struct kvm_vcpu *vcpu) return 1; } =20 +static bool vmx_emulation_required_with_pending_exception(struct kvm_vcpu = *vcpu) +{ + struct vcpu_vmx *vmx =3D to_vmx(vcpu); + + return vmx->emulation_required && !vmx->rmode.vm86_active && + vcpu->arch.exception.pending; +} + static int handle_invalid_guest_state(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx =3D to_vmx(vcpu); @@ -5405,8 +5413,7 @@ static int handle_invalid_guest_state(struct kvm_vcpu= *vcpu) if (!kvm_emulate_instruction(vcpu, 0)) return 0; =20 - if (vmx->emulation_required && !vmx->rmode.vm86_active && - vcpu->arch.exception.pending) { + if (vmx_emulation_required_with_pending_exception(vcpu)) { kvm_prepare_emulation_failure_exit(vcpu); return 0; } @@ -5428,6 +5435,16 @@ static int handle_invalid_guest_state(struct kvm_vcp= u *vcpu) return 1; } =20 +static int vmx_vcpu_pre_run(struct kvm_vcpu *vcpu) +{ + if (vmx_emulation_required_with_pending_exception(vcpu)) { + kvm_prepare_emulation_failure_exit(vcpu); + return 0; + } + + return 1; +} + static void grow_ple_window(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx =3D to_vmx(vcpu); @@ -7623,6 +7640,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata =3D { .tlb_flush_gva =3D vmx_flush_tlb_gva, .tlb_flush_guest =3D vmx_flush_tlb_guest, =20 + .vcpu_pre_run =3D vmx_vcpu_pre_run, .run =3D vmx_vcpu_run, .handle_exit =3D vmx_handle_exit, .skip_emulated_instruction =3D vmx_skip_emulated_instruction, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c194a8cbd25f..3b58649b7067 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10317,10 +10317,16 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) } else WARN_ON(vcpu->arch.pio.count || vcpu->mmio_needed); =20 - if (kvm_run->immediate_exit) + if (kvm_run->immediate_exit) { r =3D -EINTR; - else - r =3D vcpu_run(vcpu); + goto out; + } + + r =3D static_call(kvm_x86_vcpu_pre_run)(vcpu); + if (r <=3D 0) + goto out; + + r =3D vcpu_run(vcpu); =20 out: kvm_put_guest_fpu(vcpu); --=20 2.34.1.448.ga2b2bfdf31-goog From nobody Wed Jul 1 05:28:18 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E7B6DC4332F for ; Tue, 28 Dec 2021 23:24:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237788AbhL1XYq (ORCPT ); Tue, 28 Dec 2021 18:24:46 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34164 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237773AbhL1XYo (ORCPT ); Tue, 28 Dec 2021 18:24:44 -0500 Received: from mail-pg1-x549.google.com (mail-pg1-x549.google.com [IPv6:2607:f8b0:4864:20::549]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8DFA6C06173E for ; Tue, 28 Dec 2021 15:24:44 -0800 (PST) Received: by mail-pg1-x549.google.com with SMTP id k21-20020a63f015000000b0033db7baf101so10066557pgh.19 for ; Tue, 28 Dec 2021 15:24:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=reply-to:date:in-reply-to:message-id:mime-version:references :subject:from:to:cc; bh=CsFax13aN3w5jVa5kYXR5b4+1yckp+HjPm+0ljFIyEI=; b=H4oG2FF/36t4k9XmWPcGPeAyYW180NoJyDcnM3ni9w1QBoxA7appMcRo4NhOBTt1Ug pniZvQdGUTxueyLsfhx2KH+1RjhFRd6NaPH9dY3nao57O2GxPWSQQrVEBzILTTlnvN79 4VSEgGqBhwKt6XqZgftQK2NQOLXy3aga9cBAhxvWMG/Vg5v0pqGILrBl/iDGBLP5yZ5i uNW+cDCy3GR62yIrN5Rp4LnVuWBmf9wJzK1kDEBBu8LqFA+3TL0UR5Yp6uvcycvslPAQ iDLforAd5G1hIWrC0JyG6IJ4vtpnUUp51j9PUpjWOtrd5HKgDzzeveA2oKdKaMmCsPNj EBmw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:reply-to:date:in-reply-to:message-id :mime-version:references:subject:from:to:cc; bh=CsFax13aN3w5jVa5kYXR5b4+1yckp+HjPm+0ljFIyEI=; b=Q1aoxsaFckIIsDAnMA0WVlpdDhtKqkOe5GwnP1AcxLODpcpWLr5ddWtIIlxU7973xG vz58m/G1Sy5+P6AF8CcX7Elb+o9csC3EKlHg4eUKrN/AX+xuwUyQh98bIUWauSIP+szc ZtCwCke4MQjUozDANm0LLg9AzKF+btUvs1XSOl7A0Zfbd9/zaSViQkaNdWzKCi5n5LtI +JeBSe0M3fsOc0DLMjmn34/jwbueNkOWVSyrazb1r0VDVkI2E9bOA2VRlau98Az8vDCH vcwlrrL8tPYn+0zbloff1XjVgQjlNQ4JcHqsQVXNueZNp5eZU1aXMoePX4uA++ytKPbG PPKw== X-Gm-Message-State: AOAM530VbBTJp4wg+iS0+tEG4K159PVbYpm0kA/06Y6nqaO0DoUXofhc catJrlQlIgZz/OL/w0hbrUD3+mwvi1o= X-Google-Smtp-Source: ABdhPJzq4gwlG4P0/3rRyMeAVdVBSLEJpZIE10ShEk+KtiJ6X9cY3ZaWEWpGvQwGtDdlaPpB/UyCiuv1rmA= X-Received: from seanjc.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:3e5]) (user=seanjc job=sendgmr) by 2002:a17:903:1ca:b0:149:2125:9a13 with SMTP id e10-20020a17090301ca00b0014921259a13mr24163900plh.73.1640733884079; Tue, 28 Dec 2021 15:24:44 -0800 (PST) Reply-To: Sean Christopherson Date: Tue, 28 Dec 2021 23:24:37 +0000 In-Reply-To: <20211228232437.1875318-1-seanjc@google.com> Message-Id: <20211228232437.1875318-3-seanjc@google.com> Mime-Version: 1.0 References: <20211228232437.1875318-1-seanjc@google.com> X-Mailer: git-send-email 2.34.1.448.ga2b2bfdf31-goog Subject: [PATCH 2/2] KVM: selftests: Add a test to force emulation with a pending exception From: Sean Christopherson To: Paolo Bonzini Cc: Sean Christopherson , Vitaly Kuznetsov , Wanpeng Li , Jim Mattson , Joerg Roedel , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, syzbot+82112403ace4cbd780d8@syzkaller.appspotmail.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a VMX specific test to verify that KVM doesn't explode if userspace attempts KVM_RUN when emulation is required with a pending exception. KVM VMX's emulation support for !unrestricted_guest punts exceptions to userspace instead of attempting to synthesize the exception with all the correct state (and stack switching, etc...). Punting is acceptable as there's never been a request to support injecting exceptions when emulating due to invalid state, but KVM has historically assumed that userspace will do the right thing and either clear the exception or kill the guest. Deliberately do the opposite and attempt to re-enter the guest with a pending exception and emulation required to verify KVM continues to punt the combination to userspace, e.g. doesn't explode, WARN, etc... Signed-off-by: Sean Christopherson --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../vmx_exception_with_invalid_guest_state.c | 139 ++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/vmx_exception_with_i= nvalid_guest_state.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftes= ts/kvm/.gitignore index 3cb5ac5da087..68bb1e72038a 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -35,6 +35,7 @@ /x86_64/vmx_apic_access_test /x86_64/vmx_close_while_nested_test /x86_64/vmx_dirty_log_test +/x86_64/vmx_exception_with_invalid_guest_state /x86_64/vmx_invalid_nested_guest_state /x86_64/vmx_preemption_timer_test /x86_64/vmx_set_nested_state_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests= /kvm/Makefile index 17342b575e85..af98b0503eff 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -64,6 +64,7 @@ TEST_GEN_PROGS_x86_64 +=3D x86_64/userspace_msr_exit_test TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_apic_access_test TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_close_while_nested_test TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_dirty_log_test +TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_exception_with_invalid_guest_state TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_invalid_nested_guest_state TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_set_nested_state_test TEST_GEN_PROGS_x86_64 +=3D x86_64/vmx_tsc_adjust_test diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_= guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_inval= id_guest_state.c new file mode 100644 index 000000000000..27a850f3d7ce --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_s= tate.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +#include +#include +#include +#include + +#include "kselftest.h" + +#define VCPU_ID 0 + +static struct kvm_vm *vm; + +static void guest_ud_handler(struct ex_regs *regs) +{ + /* Loop on the ud2 until guest state is made invalid. */ +} + +static void guest_code(void) +{ + asm volatile("ud2"); +} + +static void __run_vcpu_with_invalid_state(void) +{ + struct kvm_run *run =3D vcpu_state(vm, VCPU_ID); + + vcpu_run(vm, VCPU_ID); + + TEST_ASSERT(run->exit_reason =3D=3D KVM_EXIT_INTERNAL_ERROR, + "Expected KVM_EXIT_INTERNAL_ERROR, got %d (%s)\n", + run->exit_reason, exit_reason_str(run->exit_reason)); + TEST_ASSERT(run->emulation_failure.suberror =3D=3D KVM_INTERNAL_ERROR_EMU= LATION, + "Expected emulation failure, got %d\n", + run->emulation_failure.suberror); +} + +static void run_vcpu_with_invalid_state(void) +{ + /* + * Always run twice to verify KVM handles the case where _KVM_ queues + * an exception with invalid state and then exits to userspace, i.e. + * that KVM doesn't explode if userspace ignores the initial error. + */ + __run_vcpu_with_invalid_state(); + __run_vcpu_with_invalid_state(); +} + +static void set_timer(void) +{ + struct itimerval timer; + + timer.it_value.tv_sec =3D 0; + timer.it_value.tv_usec =3D 200; + timer.it_interval =3D timer.it_value; + ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0); +} + +static void set_or_clear_invalid_guest_state(bool set) +{ + static struct kvm_sregs sregs; + + if (!sregs.cr0) + vcpu_sregs_get(vm, VCPU_ID, &sregs); + sregs.tr.unusable =3D !!set; + vcpu_sregs_set(vm, VCPU_ID, &sregs); +} + +static void set_invalid_guest_state(void) +{ + set_or_clear_invalid_guest_state(true); +} + +static void clear_invalid_guest_state(void) +{ + set_or_clear_invalid_guest_state(false); +} + +static void sigalrm_handler(int sig) +{ + struct kvm_vcpu_events events; + + TEST_ASSERT(sig =3D=3D SIGALRM, "Unexpected signal =3D %d", sig); + + vcpu_events_get(vm, VCPU_ID, &events); + + /* + * If an exception is pending, attempt KVM_RUN with invalid guest, + * otherwise rearm the timer and keep doing so until the timer fires + * between KVM queueing an exception and re-entering the guest. + */ + if (events.exception.pending) { + set_invalid_guest_state(); + run_vcpu_with_invalid_state(); + } else { + set_timer(); + } +} + +int main(int argc, char *argv[]) +{ + if (!is_intel_cpu() || vm_is_unrestricted_guest(NULL)) { + print_skip("Must be run with kvm_intel.unrestricted_guest=3D0"); + exit(KSFT_SKIP); + } + + vm =3D vm_create_default(VCPU_ID, 0, (void *)guest_code); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vm, VCPU_ID); + + vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); + + /* + * Stuff invalid guest state for L2 by making TR unusuable. The next + * KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support + * emulating invalid guest state for L2. + */ + set_invalid_guest_state(); + run_vcpu_with_invalid_state(); + + /* + * Verify KVM also handles the case where userspace gains control while + * an exception is pending and stuffs invalid state. Run with valid + * guest state and a timer firing every 200us, and attempt to enter the + * guest with invalid state when the handler interrupts KVM with an + * exception pending. + */ + clear_invalid_guest_state(); + TEST_ASSERT(signal(SIGALRM, sigalrm_handler) !=3D SIG_ERR, + "Failed to register SIGALRM handler, errno =3D %d (%s)", + errno, strerror(errno)); + + set_timer(); + run_vcpu_with_invalid_state(); +} --=20 2.34.1.448.ga2b2bfdf31-goog