KVM: VMX: Drop bits 31:16 when shoving exception error code into VMCS
authorSean Christopherson <seanjc@google.com>
Tue, 30 Aug 2022 23:15:49 +0000 (23:15 +0000)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 26 Sep 2022 16:03:04 +0000 (12:03 -0400)
Deliberately truncate the exception error code when shoving it into the
VMCS (VM-Entry field for vmcs01 and vmcs02, VM-Exit field for vmcs12).
Intel CPUs are incapable of handling 32-bit error codes and will never
generate an error code with bits 31:16, but userspace can provide an
arbitrary error code via KVM_SET_VCPU_EVENTS.  Failure to drop the bits
on exception injection results in failed VM-Entry, as VMX disallows
setting bits 31:16.  Setting the bits on VM-Exit would at best confuse
L1, and at worse induce a nested VM-Entry failure, e.g. if L1 decided to
reinject the exception back into L2.

Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Link: https://lore.kernel.org/r/20220830231614.3580124-3-seanjc@google.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c

index e6218b7f8843e76aaed83ae6bd127f86d491d108..90d97ae2ef06f4f7134b9b73dafb4a8ec7460e5b 100644 (file)
@@ -3873,7 +3873,16 @@ static void nested_vmx_inject_exception_vmexit(struct kvm_vcpu *vcpu,
        u32 intr_info = nr | INTR_INFO_VALID_MASK;
 
        if (vcpu->arch.exception.has_error_code) {
-               vmcs12->vm_exit_intr_error_code = vcpu->arch.exception.error_code;
+               /*
+                * Intel CPUs do not generate error codes with bits 31:16 set,
+                * and more importantly VMX disallows setting bits 31:16 in the
+                * injected error code for VM-Entry.  Drop the bits to mimic
+                * hardware and avoid inducing failure on nested VM-Entry if L1
+                * chooses to inject the exception back to L2.  AMD CPUs _do_
+                * generate "full" 32-bit error codes, so KVM allows userspace
+                * to inject exception error codes with bits 31:16 set.
+                */
+               vmcs12->vm_exit_intr_error_code = (u16)vcpu->arch.exception.error_code;
                intr_info |= INTR_INFO_DELIVER_CODE_MASK;
        }
 
index e624f4c53021b9a3be526abdd5c84babe5d3b866..497d14e515ed002837276ed9c938c2d2e7592a04 100644 (file)
@@ -1695,7 +1695,17 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu)
        kvm_deliver_exception_payload(vcpu);
 
        if (has_error_code) {
-               vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
+               /*
+                * Despite the error code being architecturally defined as 32
+                * bits, and the VMCS field being 32 bits, Intel CPUs and thus
+                * VMX don't actually supporting setting bits 31:16.  Hardware
+                * will (should) never provide a bogus error code, but AMD CPUs
+                * do generate error codes with bits 31:16 set, and so KVM's
+                * ABI lets userspace shove in arbitrary 32-bit values.  Drop
+                * the upper bits to avoid VM-Fail, losing information that
+                * does't really exist is preferable to killing the VM.
+                */
+               vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, (u16)error_code);
                intr_info |= INTR_INFO_DELIVER_CODE_MASK;
        }