This is a note to let you know that I've just added the patch titled KVM: SVM: Don't inject #UD if KVM attempts to skip SEV guest insn to the 6.1-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: kvm-svm-don-t-inject-ud-if-kvm-attempts-to-skip-sev-guest-insn.patch and it can be found in the queue-6.1 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. >From cb49631ad111570f1bad37702c11c2ae07fa2e3c Mon Sep 17 00:00:00 2001 From: Sean Christopherson <seanjc@xxxxxxxxxx> Date: Thu, 24 Aug 2023 18:36:18 -0700 Subject: KVM: SVM: Don't inject #UD if KVM attempts to skip SEV guest insn From: Sean Christopherson <seanjc@xxxxxxxxxx> commit cb49631ad111570f1bad37702c11c2ae07fa2e3c upstream. Don't inject a #UD if KVM attempts to "emulate" to skip an instruction for an SEV guest, and instead resume the guest and hope that it can make forward progress. When commit 04c40f344def ("KVM: SVM: Inject #UD on attempted emulation for SEV guest w/o insn buffer") added the completely arbitrary #UD behavior, there were no known scenarios where a well-behaved guest would induce a VM-Exit that triggered emulation, i.e. it was thought that injecting #UD would be helpful. However, now that KVM (correctly) attempts to re-inject INT3/INTO, e.g. if a #NPF is encountered when attempting to deliver the INT3/INTO, an SEV guest can trigger emulation without a buffer, through no fault of its own. Resuming the guest and retrying the INT3/INTO is architecturally wrong, e.g. the vCPU will incorrectly re-hit code #DBs, but for SEV guests there is literally no other option that has a chance of making forward progress. Drop the #UD injection for all "skip" emulation, not just those related to INT3/INTO, even though that means that the guest will likely end up in an infinite loop instead of getting a #UD (the vCPU may also crash, e.g. if KVM emulated everything about an instruction except for advancing RIP). There's no evidence that suggests that an unexpected #UD is actually better than hanging the vCPU, e.g. a soft-hung vCPU can still respond to IRQs and NMIs to generate a backtrace. Reported-by: Wu Zongyo <wuzongyo@xxxxxxxxxxxxxxxx> Closes: https://lore.kernel.org/all/8eb933fd-2cf3-d7a9-32fe-2a1d82eac42a@xxxxxxxxxxxxxxxx Fixes: 6ef88d6e36c2 ("KVM: SVM: Re-inject INT3/INTO instead of retrying the instruction") Cc: stable@xxxxxxxxxxxxxxx Cc: Tom Lendacky <thomas.lendacky@xxxxxxx> Link: https://lore.kernel.org/r/20230825013621.2845700-2-seanjc@xxxxxxxxxx Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- arch/x86/kvm/svm/svm.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -366,6 +366,8 @@ static void svm_set_interrupt_shadow(str svm->vmcb->control.int_state |= SVM_INTERRUPT_SHADOW_MASK; } +static bool svm_can_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, + void *insn, int insn_len); static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, bool commit_side_effects) @@ -386,6 +388,14 @@ static int __svm_skip_emulated_instructi } if (!svm->next_rip) { + /* + * FIXME: Drop this when kvm_emulate_instruction() does the + * right thing and treats "can't emulate" as outright failure + * for EMULTYPE_SKIP. + */ + if (!svm_can_emulate_instruction(vcpu, EMULTYPE_SKIP, NULL, 0)) + return 0; + if (unlikely(!commit_side_effects)) old_rflags = svm->vmcb->save.rflags; @@ -4592,16 +4602,25 @@ static bool svm_can_emulate_instruction( * and cannot be decrypted by KVM, i.e. KVM would read cyphertext and * decode garbage. * - * Inject #UD if KVM reached this point without an instruction buffer. - * In practice, this path should never be hit by a well-behaved guest, - * e.g. KVM doesn't intercept #UD or #GP for SEV guests, but this path - * is still theoretically reachable, e.g. via unaccelerated fault-like - * AVIC access, and needs to be handled by KVM to avoid putting the - * guest into an infinite loop. Injecting #UD is somewhat arbitrary, - * but its the least awful option given lack of insight into the guest. + * If KVM is NOT trying to simply skip an instruction, inject #UD if + * KVM reached this point without an instruction buffer. In practice, + * this path should never be hit by a well-behaved guest, e.g. KVM + * doesn't intercept #UD or #GP for SEV guests, but this path is still + * theoretically reachable, e.g. via unaccelerated fault-like AVIC + * access, and needs to be handled by KVM to avoid putting the guest + * into an infinite loop. Injecting #UD is somewhat arbitrary, but + * its the least awful option given lack of insight into the guest. + * + * If KVM is trying to skip an instruction, simply resume the guest. + * If a #NPF occurs while the guest is vectoring an INT3/INTO, then KVM + * will attempt to re-inject the INT3/INTO and skip the instruction. + * In that scenario, retrying the INT3/INTO and hoping the guest will + * make forward progress is the only option that has a chance of + * success (and in practice it will work the vast majority of the time). */ if (unlikely(!insn)) { - kvm_queue_exception(vcpu, UD_VECTOR); + if (!(emul_type & EMULTYPE_SKIP)) + kvm_queue_exception(vcpu, UD_VECTOR); return false; } Patches currently in stable-queue which might be from seanjc@xxxxxxxxxx are queue-6.1/kvm-svm-don-t-inject-ud-if-kvm-attempts-to-skip-sev-guest-insn.patch queue-6.1/kvm-nsvm-check-instead-of-asserting-on-nested-tsc-scaling-support.patch queue-6.1/drm-i915-gvt-verify-pfn-is-valid-before-dereferencin.patch queue-6.1/kvm-nsvm-load-l1-s-tsc-multiplier-based-on-l1-state-not-l2-state.patch queue-6.1/x86-virt-drop-unnecessary-check-on-extended-cpuid-le.patch queue-6.1/kvm-svm-get-source-vcpus-from-source-vm-for-sev-es-intrahost-migration.patch queue-6.1/kvm-svm-skip-vmsa-init-in-sev_es_init_vmcb-if-pointer-is-null.patch queue-6.1/kvm-svm-take-and-hold-ir_list_lock-when-updating-vcpu-s-physical-id-entry.patch queue-6.1/kvm-svm-set-target-pcpu-during-irte-update-if-target-vcpu-is-running.patch queue-6.1/drm-i915-gvt-put-the-page-reference-obtained-by-kvm-.patch queue-6.1/drm-i915-gvt-drop-unused-helper-intel_vgpu_reset_gtt.patch