diff mbox series

[v3,05/22] kvm: x86: Check permitted dynamic xfeatures at KVM_SET_CPUID2

Message ID 20211222124052.644626-6-jing2.liu@intel.com (mailing list archive)
State New
Headers show
Series AMX Support in KVM | expand

Commit Message

Jing Liu Dec. 22, 2021, 12:40 p.m. UTC
Guest xstate permissions should be set by userspace VMM before vcpu
creation. Extend KVM_SET_CPUID2 to verify that every feature reported
in CPUID[0xD] has proper permission set.

Signed-off-by: Jing Liu <jing2.liu@intel.com>
---
 arch/x86/kvm/cpuid.c | 36 ++++++++++++++++++++++++------------
 1 file changed, 24 insertions(+), 12 deletions(-)

Comments

Sean Christopherson Dec. 28, 2021, 11:38 p.m. UTC | #1
On Wed, Dec 22, 2021, Jing Liu wrote:
> Guest xstate permissions should be set by userspace VMM before vcpu
> creation. Extend KVM_SET_CPUID2 to verify that every feature reported
> in CPUID[0xD] has proper permission set.

Why?  Nothing in the changelog, code, or comments explains why KVM _needs_ to prevent
userspace from advertising bogus features to the guest.  E.g. the virtual address
width check exists because _KVM_ will do the wrong thing given a width other than 48
or 57, and explicity says as much in a comment.
Tian, Kevin Dec. 29, 2021, 2:18 a.m. UTC | #2
> From: Sean Christopherson <seanjc@google.com>
> Sent: Wednesday, December 29, 2021 7:39 AM
> 
> On Wed, Dec 22, 2021, Jing Liu wrote:
> > Guest xstate permissions should be set by userspace VMM before vcpu
> > creation. Extend KVM_SET_CPUID2 to verify that every feature reported
> > in CPUID[0xD] has proper permission set.
> 
> Why?  Nothing in the changelog, code, or comments explains why KVM
> _needs_ to prevent
> userspace from advertising bogus features to the guest.  E.g. the virtual
> address
> width check exists because _KVM_ will do the wrong thing given a width
> other than 48
> or 57, and explicity says as much in a comment.

Advertising a known bogus feature due to lacking of permission does
no good compared to failing it early even before the guest is running. This
also avoids tons of complexity at run-time to deal with permission
violation (e.g. introducing new kvm exit reason if you tracked the v2
discussion).

But yes, we should add a clear comment here.

Thanks
Kevin
diff mbox series

Patch

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 4855344091b8..a068373a7fbd 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -81,7 +81,9 @@  static inline struct kvm_cpuid_entry2 *cpuid_entry2_find(
 	return NULL;
 }
 
-static int kvm_check_cpuid(struct kvm_cpuid_entry2 *entries, int nent)
+static int kvm_check_cpuid(struct kvm_vcpu *vcpu,
+			   struct kvm_cpuid_entry2 *entries,
+			   int nent)
 {
 	struct kvm_cpuid_entry2 *best;
 
@@ -97,6 +99,16 @@  static int kvm_check_cpuid(struct kvm_cpuid_entry2 *entries, int nent)
 			return -EINVAL;
 	}
 
+	/* Check guest permissions for dynamically-enabled xfeatures */
+	best = cpuid_entry2_find(entries, nent, 0xd, 0);
+	if (best) {
+		u64 xfeatures;
+
+		xfeatures = best->eax | ((u64)best->edx << 32);
+		if (xfeatures & ~vcpu->arch.guest_fpu.perm)
+			return -ENXIO;
+	}
+
 	return 0;
 }
 
@@ -277,21 +289,21 @@  u64 kvm_vcpu_reserved_gpa_bits_raw(struct kvm_vcpu *vcpu)
 static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *e2,
                         int nent)
 {
-    int r;
+	int r;
 
-    r = kvm_check_cpuid(e2, nent);
-    if (r)
-        return r;
+	r = kvm_check_cpuid(vcpu, e2, nent);
+	if (r)
+		return r;
 
-    kvfree(vcpu->arch.cpuid_entries);
-    vcpu->arch.cpuid_entries = e2;
-    vcpu->arch.cpuid_nent = nent;
+	kvfree(vcpu->arch.cpuid_entries);
+	vcpu->arch.cpuid_entries = e2;
+	vcpu->arch.cpuid_nent = nent;
 
-    kvm_update_kvm_cpuid_base(vcpu);
-    kvm_update_cpuid_runtime(vcpu);
-    kvm_vcpu_after_set_cpuid(vcpu);
+	kvm_update_kvm_cpuid_base(vcpu);
+	kvm_update_cpuid_runtime(vcpu);
+	kvm_vcpu_after_set_cpuid(vcpu);
 
-    return 0;
+	return 0;
 }
 
 /* when an old userspace process fills a new kernel module */