| From 47353fb81d51fdf16a0c593daaf88b08091b0c01 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 14 Apr 2025 20:12:06 +0300 |
| Subject: KVM: SVM: Forcibly leave SMM mode on SHUTDOWN interception |
| |
| From: Mikhail Lobanov <m.lobanov@rosa.ru> |
| |
| [ Upstream commit a2620f8932fa9fdabc3d78ed6efb004ca409019f ] |
| |
| Previously, commit ed129ec9057f ("KVM: x86: forcibly leave nested mode |
| on vCPU reset") addressed an issue where a triple fault occurring in |
| nested mode could lead to use-after-free scenarios. However, the commit |
| did not handle the analogous situation for System Management Mode (SMM). |
| |
| This omission results in triggering a WARN when KVM forces a vCPU INIT |
| after SHUTDOWN interception while the vCPU is in SMM. This situation was |
| reprodused using Syzkaller by: |
| |
| 1) Creating a KVM VM and vCPU |
| 2) Sending a KVM_SMI ioctl to explicitly enter SMM |
| 3) Executing invalid instructions causing consecutive exceptions and |
| eventually a triple fault |
| |
| The issue manifests as follows: |
| |
| WARNING: CPU: 0 PID: 25506 at arch/x86/kvm/x86.c:12112 |
| kvm_vcpu_reset+0x1d2/0x1530 arch/x86/kvm/x86.c:12112 |
| Modules linked in: |
| CPU: 0 PID: 25506 Comm: syz-executor.0 Not tainted |
| 6.1.130-syzkaller-00157-g164fe5dde9b6 #0 |
| Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), |
| BIOS 1.12.0-1 04/01/2014 |
| RIP: 0010:kvm_vcpu_reset+0x1d2/0x1530 arch/x86/kvm/x86.c:12112 |
| Call Trace: |
| <TASK> |
| shutdown_interception+0x66/0xb0 arch/x86/kvm/svm/svm.c:2136 |
| svm_invoke_exit_handler+0x110/0x530 arch/x86/kvm/svm/svm.c:3395 |
| svm_handle_exit+0x424/0x920 arch/x86/kvm/svm/svm.c:3457 |
| vcpu_enter_guest arch/x86/kvm/x86.c:10959 [inline] |
| vcpu_run+0x2c43/0x5a90 arch/x86/kvm/x86.c:11062 |
| kvm_arch_vcpu_ioctl_run+0x50f/0x1cf0 arch/x86/kvm/x86.c:11283 |
| kvm_vcpu_ioctl+0x570/0xf00 arch/x86/kvm/../../../virt/kvm/kvm_main.c:4122 |
| vfs_ioctl fs/ioctl.c:51 [inline] |
| __do_sys_ioctl fs/ioctl.c:870 [inline] |
| __se_sys_ioctl fs/ioctl.c:856 [inline] |
| __x64_sys_ioctl+0x19a/0x210 fs/ioctl.c:856 |
| do_syscall_x64 arch/x86/entry/common.c:51 [inline] |
| do_syscall_64+0x35/0x80 arch/x86/entry/common.c:81 |
| entry_SYSCALL_64_after_hwframe+0x6e/0xd8 |
| |
| Architecturally, INIT is blocked when the CPU is in SMM, hence KVM's WARN() |
| in kvm_vcpu_reset() to guard against KVM bugs, e.g. to detect improper |
| emulation of INIT. SHUTDOWN on SVM is a weird edge case where KVM needs to |
| do _something_ sane with the VMCB, since it's technically undefined, and |
| INIT is the least awful choice given KVM's ABI. |
| |
| So, double down on stuffing INIT on SHUTDOWN, and force the vCPU out of |
| SMM to avoid any weirdness (and the WARN). |
| |
| Found by Linux Verification Center (linuxtesting.org) with Syzkaller. |
| |
| Fixes: ed129ec9057f ("KVM: x86: forcibly leave nested mode on vCPU reset") |
| Cc: stable@vger.kernel.org |
| Suggested-by: Sean Christopherson <seanjc@google.com> |
| Signed-off-by: Mikhail Lobanov <m.lobanov@rosa.ru> |
| Link: https://lore.kernel.org/r/20250414171207.155121-1-m.lobanov@rosa.ru |
| [sean: massage changelog, make it clear this isn't architectural behavior] |
| Signed-off-by: Sean Christopherson <seanjc@google.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kvm/smm.c | 1 + |
| arch/x86/kvm/svm/svm.c | 4 ++++ |
| 2 files changed, 5 insertions(+) |
| |
| diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c |
| index b42111a24cc28..8e38c51359d05 100644 |
| --- a/arch/x86/kvm/smm.c |
| +++ b/arch/x86/kvm/smm.c |
| @@ -131,6 +131,7 @@ void kvm_smm_changed(struct kvm_vcpu *vcpu, bool entering_smm) |
| |
| kvm_mmu_reset_context(vcpu); |
| } |
| +EXPORT_SYMBOL_GPL(kvm_smm_changed); |
| |
| void process_smi(struct kvm_vcpu *vcpu) |
| { |
| diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c |
| index 0c01887124b6c..c84a1451f194c 100644 |
| --- a/arch/x86/kvm/svm/svm.c |
| +++ b/arch/x86/kvm/svm/svm.c |
| @@ -2225,6 +2225,10 @@ static int shutdown_interception(struct kvm_vcpu *vcpu) |
| */ |
| if (!sev_es_guest(vcpu->kvm)) { |
| clear_page(svm->vmcb); |
| +#ifdef CONFIG_KVM_SMM |
| + if (is_smm(vcpu)) |
| + kvm_smm_changed(vcpu, false); |
| +#endif |
| kvm_vcpu_reset(vcpu, true); |
| } |
| |
| -- |
| 2.39.5 |
| |