]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: VMX: check validity of VMCS controls when returning from SMM
authorPaolo Bonzini <pbonzini@redhat.com>
Mon, 9 Mar 2026 11:40:40 +0000 (12:40 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 11 Mar 2026 17:41:11 +0000 (18:41 +0100)
The VMCS12 is not available while in SMM.  However, it can be overwritten
if userspace manages to trigger copy_enlightened_to_vmcs12() - for example
via KVM_GET_NESTED_STATE.

Because of this, the VMCS12 has to be checked for validity before it is
used to generate the VMCS02.  Move the check code out of vmx_set_nested_state()
(the other "not a VMLAUNCH/VMRESUME" path that emulates a nested vmentry)
and reuse it in vmx_leave_smm().

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/nested.h
arch/x86/kvm/vmx/vmx.c

index 603c98de2cc8d9522d9ff06757bd6d1b16ce4545..937aeb474af7dc18630213fcb922bc5f0281d1a9 100644 (file)
@@ -6856,13 +6856,34 @@ void vmx_leave_nested(struct kvm_vcpu *vcpu)
        free_nested(vcpu);
 }
 
+int nested_vmx_check_restored_vmcs12(struct kvm_vcpu *vcpu)
+{
+       enum vm_entry_failure_code ignored;
+       struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+
+       if (nested_cpu_has_shadow_vmcs(vmcs12) &&
+           vmcs12->vmcs_link_pointer != INVALID_GPA) {
+               struct vmcs12 *shadow_vmcs12 = get_shadow_vmcs12(vcpu);
+
+               if (shadow_vmcs12->hdr.revision_id != VMCS12_REVISION ||
+                   !shadow_vmcs12->hdr.shadow_vmcs)
+                       return -EINVAL;
+       }
+
+       if (nested_vmx_check_controls(vcpu, vmcs12) ||
+           nested_vmx_check_host_state(vcpu, vmcs12) ||
+           nested_vmx_check_guest_state(vcpu, vmcs12, &ignored))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
                                struct kvm_nested_state __user *user_kvm_nested_state,
                                struct kvm_nested_state *kvm_state)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        struct vmcs12 *vmcs12;
-       enum vm_entry_failure_code ignored;
        struct kvm_vmx_nested_state_data __user *user_vmx_nested_state =
                &user_kvm_nested_state->data.vmx[0];
        int ret;
@@ -6993,25 +7014,20 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
        vmx->nested.mtf_pending =
                !!(kvm_state->flags & KVM_STATE_NESTED_MTF_PENDING);
 
-       ret = -EINVAL;
        if (nested_cpu_has_shadow_vmcs(vmcs12) &&
            vmcs12->vmcs_link_pointer != INVALID_GPA) {
                struct vmcs12 *shadow_vmcs12 = get_shadow_vmcs12(vcpu);
 
+               ret = -EINVAL;
                if (kvm_state->size <
                    sizeof(*kvm_state) +
                    sizeof(user_vmx_nested_state->vmcs12) + sizeof(*shadow_vmcs12))
                        goto error_guest_mode;
 
+               ret = -EFAULT;
                if (copy_from_user(shadow_vmcs12,
                                   user_vmx_nested_state->shadow_vmcs12,
-                                  sizeof(*shadow_vmcs12))) {
-                       ret = -EFAULT;
-                       goto error_guest_mode;
-               }
-
-               if (shadow_vmcs12->hdr.revision_id != VMCS12_REVISION ||
-                   !shadow_vmcs12->hdr.shadow_vmcs)
+                                  sizeof(*shadow_vmcs12)))
                        goto error_guest_mode;
        }
 
@@ -7022,9 +7038,8 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
                        kvm_state->hdr.vmx.preemption_timer_deadline;
        }
 
-       if (nested_vmx_check_controls(vcpu, vmcs12) ||
-           nested_vmx_check_host_state(vcpu, vmcs12) ||
-           nested_vmx_check_guest_state(vcpu, vmcs12, &ignored))
+       ret = nested_vmx_check_restored_vmcs12(vcpu);
+       if (ret < 0)
                goto error_guest_mode;
 
        vmx->nested.dirty_vmcs12 = true;
index b844c5d59025bf9aee126a3e9ba9a922a01486e6..213a448104aff0931eb55ca5e6235c1bc26d986b 100644 (file)
@@ -22,6 +22,7 @@ void nested_vmx_setup_ctls_msrs(struct vmcs_config *vmcs_conf, u32 ept_caps);
 void nested_vmx_hardware_unsetup(void);
 __init int nested_vmx_hardware_setup(int (*exit_handlers[])(struct kvm_vcpu *));
 void nested_vmx_set_vmcs_shadowing_bitmap(void);
+int nested_vmx_check_restored_vmcs12(struct kvm_vcpu *vcpu);
 void nested_vmx_free_vcpu(struct kvm_vcpu *vcpu);
 enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
                                                     bool from_vmentry);
index 83d057cfa8164937194918f84b36349ac1fa4a35..bccc91a999d9f4c3aa427291b2143cf5bfd1a520 100644 (file)
@@ -8528,6 +8528,10 @@ int vmx_leave_smm(struct kvm_vcpu *vcpu, const union kvm_smram *smram)
        }
 
        if (vmx->nested.smm.guest_mode) {
+               /* Triple fault if the state is invalid.  */
+               if (nested_vmx_check_restored_vmcs12(vcpu) < 0)
+                       return 1;
+
                ret = nested_vmx_enter_non_root_mode(vcpu, false);
                if (ret)
                        return ret;