Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: x86: Forbid KVM_SET_CPUID{,2} after KVM_RUN

Commit 63f5a1909f9e ("KVM: x86: Alert userspace that KVM_SET_CPUID{,2}
after KVM_RUN is broken") officially deprecated KVM_SET_CPUID{,2} ioctls
after first successful KVM_RUN and promissed to make this sequence forbiden
in 5.16. It's time to fulfil the promise.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Message-Id: <20211122175818.608220-3-vkuznets@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Vitaly Kuznetsov and committed by
Paolo Bonzini
feb627e8 6c118643

+30 -17
+11 -17
arch/x86/kvm/mmu/mmu.c
··· 5025 5025 /* 5026 5026 * Invalidate all MMU roles to force them to reinitialize as CPUID 5027 5027 * information is factored into reserved bit calculations. 5028 + * 5029 + * Correctly handling multiple vCPU models with respect to paging and 5030 + * physical address properties) in a single VM would require tracking 5031 + * all relevant CPUID information in kvm_mmu_page_role. That is very 5032 + * undesirable as it would increase the memory requirements for 5033 + * gfn_track (see struct kvm_mmu_page_role comments). For now that 5034 + * problem is swept under the rug; KVM's CPUID API is horrific and 5035 + * it's all but impossible to solve it without introducing a new API. 5028 5036 */ 5029 5037 vcpu->arch.root_mmu.mmu_role.ext.valid = 0; 5030 5038 vcpu->arch.guest_mmu.mmu_role.ext.valid = 0; ··· 5040 5032 kvm_mmu_reset_context(vcpu); 5041 5033 5042 5034 /* 5043 - * KVM does not correctly handle changing guest CPUID after KVM_RUN, as 5044 - * MAXPHYADDR, GBPAGES support, AMD reserved bit behavior, etc.. aren't 5045 - * tracked in kvm_mmu_page_role. As a result, KVM may miss guest page 5046 - * faults due to reusing SPs/SPTEs. Alert userspace, but otherwise 5047 - * sweep the problem under the rug. 5048 - * 5049 - * KVM's horrific CPUID ABI makes the problem all but impossible to 5050 - * solve, as correctly handling multiple vCPU models (with respect to 5051 - * paging and physical address properties) in a single VM would require 5052 - * tracking all relevant CPUID information in kvm_mmu_page_role. That 5053 - * is very undesirable as it would double the memory requirements for 5054 - * gfn_track (see struct kvm_mmu_page_role comments), and in practice 5055 - * no sane VMM mucks with the core vCPU model on the fly. 5035 + * Changing guest CPUID after KVM_RUN is forbidden, see the comment in 5036 + * kvm_arch_vcpu_ioctl(). 5056 5037 */ 5057 - if (vcpu->arch.last_vmentry_cpu != -1) { 5058 - pr_warn_ratelimited("KVM: KVM_SET_CPUID{,2} after KVM_RUN may cause guest instability\n"); 5059 - pr_warn_ratelimited("KVM: KVM_SET_CPUID{,2} will fail after KVM_RUN starting with Linux 5.16\n"); 5060 - } 5038 + KVM_BUG_ON(vcpu->arch.last_vmentry_cpu != -1, vcpu->kvm); 5061 5039 } 5062 5040 5063 5041 void kvm_mmu_reset_context(struct kvm_vcpu *vcpu)
+19
arch/x86/kvm/x86.c
··· 5148 5148 struct kvm_cpuid __user *cpuid_arg = argp; 5149 5149 struct kvm_cpuid cpuid; 5150 5150 5151 + /* 5152 + * KVM does not correctly handle changing guest CPUID after KVM_RUN, as 5153 + * MAXPHYADDR, GBPAGES support, AMD reserved bit behavior, etc.. aren't 5154 + * tracked in kvm_mmu_page_role. As a result, KVM may miss guest page 5155 + * faults due to reusing SPs/SPTEs. In practice no sane VMM mucks with 5156 + * the core vCPU model on the fly, so fail. 5157 + */ 5158 + r = -EINVAL; 5159 + if (vcpu->arch.last_vmentry_cpu != -1) 5160 + goto out; 5161 + 5151 5162 r = -EFAULT; 5152 5163 if (copy_from_user(&cpuid, cpuid_arg, sizeof(cpuid))) 5153 5164 goto out; ··· 5168 5157 case KVM_SET_CPUID2: { 5169 5158 struct kvm_cpuid2 __user *cpuid_arg = argp; 5170 5159 struct kvm_cpuid2 cpuid; 5160 + 5161 + /* 5162 + * KVM_SET_CPUID{,2} after KVM_RUN is forbidded, see the comment in 5163 + * KVM_SET_CPUID case above. 5164 + */ 5165 + r = -EINVAL; 5166 + if (vcpu->arch.last_vmentry_cpu != -1) 5167 + goto out; 5171 5168 5172 5169 r = -EFAULT; 5173 5170 if (copy_from_user(&cpuid, cpuid_arg, sizeof(cpuid)))