Don't modify the set of allowed secondary execution controls, i.e. the virtual MSR_IA32_VMX_PROCBASED_CTLS2, in response to guest CPUID changes. To avoid breaking old userspace that never sets the VMX MSRs, i.e. relies on KVM to provide a consistent vCPU model, keep the existing behavior if userspace has never written MSR_IA32_VMX_PROCBASED_CTLS2. KVM should not modify the VMX capabilities presented to L1 based on CPUID as doing so may discard explicit settings provided by userspace. E.g. if userspace does KVM_SET_MSRS => KVM_SET_CPUID and disables a feature in the VMX MSRs but not CPUID (to prevent exposing the feature to L2), then stuffing the VMX MSRs during KVM_SET_CPUID will expose the feature to L2 against userspace's wishes. Alternatively, KVM could add a quirk, but that's less than ideal as a VMM that is affected by the bug would need to be updated in order to opt out of the buggy behavior. The "has the MSR ever been written" logic handles both the care where an enlightened userspace sets the MSR during setup, and the case where userspace blindly migrates the MSR, as the migrated value will already have been sanitized by the source KVM. Reported-by: Yu Zhang <yu.c.zhang@xxxxxxxxxxxxxxx> Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> --- arch/x86/kvm/vmx/capabilities.h | 1 + arch/x86/kvm/vmx/nested.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index cd2ac9536c99..7b08d6006f52 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -51,6 +51,7 @@ struct nested_vmx_msrs { u64 cr4_fixed1; u64 vmcs_enum; u64 vmfunc_controls; + bool secondary_set_by_userspace; }; struct vmcs_config { diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index d131375f347a..0140893412b7 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -1271,6 +1271,9 @@ vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) if (!is_bitwise_subset(supported, data, GENMASK_ULL(63, 32))) return -EINVAL; + if (msr_index == MSR_IA32_VMX_PROCBASED_CTLS2) + vmx->nested.msrs.secondary_set_by_userspace = true; + vmx_get_control_msr(&vmx->nested.msrs, msr_index, &lowp, &highp); *lowp = data; *highp = data >> 32; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 13d3f5eb4c32..dd0247bc7193 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4456,9 +4456,12 @@ vmx_adjust_secondary_exec_control(struct vcpu_vmx *vmx, u32 *exec_control, /* * Update the nested MSR settings so that a nested VMM can/can't set - * controls for features that are/aren't exposed to the guest. + * controls for features that are/aren't exposed to the guest. Stuff + * the MSR if and only if userspace hasn't explicitly set the MSR, i.e. + * to avoid ABI breakage if userspace might be relying on KVM's flawed + * behavior to expose features to L1. */ - if (nested) { + if (nested && !vmx->nested.msrs.secondary_set_by_userspace) { /* * All features that got grandfathered into KVM's flawed CPUID- * induced manipulation of VMX MSRs are unconditionally exposed -- 2.39.0.rc1.256.g54fd8350bd-goog