diff mbox series

[2/3] KVM: x86: add system attribute to retrieve full set of supported xsave states

Message ID 20220126152210.3044876-3-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show
Series KVM: x86: export supported_xcr0 via UAPI | expand

Commit Message

Paolo Bonzini Jan. 26, 2022, 3:22 p.m. UTC
Because KVM_GET_SUPPORTED_CPUID is meant to be passed (by simple-minded
VMMs) to KVM_SET_CPUID2, it cannot include any dynamic xsave states that
have not been enabled.  Probing those, for example so that they can be
passed to ARCH_REQ_XCOMP_GUEST_PERM, requires a new ioctl or arch_prctl.
The latter is in fact worse, even though that is what the rest of the
API uses, because it would require supported_xcr0 to be moved from the
KVM module to the kernel just for this use.  In addition, the value
would be nonsensical (or an error would have to be returned) until
the KVM module is loaded in.

KVM_CHECK_EXTENSION cannot be used because it only has 32 bits of
output; in order to limit the growth of capabilities and ioctls, the
series adds a /dev/kvm variant of KVM_{GET,HAS}_DEVICE_ATTR that
can be used in the future and by other architectures.  It then
implements it in x86 with just one group (0) and attribute
(KVM_X86_XCOMP_GUEST_SUPP).

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 Documentation/virt/kvm/api.rst  |  4 ++-
 arch/x86/include/uapi/asm/kvm.h |  3 +++
 arch/x86/kvm/x86.c              | 45 +++++++++++++++++++++++++++++++++
 include/uapi/linux/kvm.h        |  1 +
 4 files changed, 52 insertions(+), 1 deletion(-)

Comments

Sean Christopherson Jan. 27, 2022, 3:35 p.m. UTC | #1
On Wed, Jan 26, 2022, Paolo Bonzini wrote:
> +static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
> +{
> +	if (attr->group)
> +		return -ENXIO;
> +
> +	switch (attr->attr) {
> +	case KVM_X86_XCOMP_GUEST_SUPP:
> +		if (put_user(supported_xcr0, (u64 __user *)attr->addr))

Deja vu[*].

  arch/x86/kvm/x86.c: In function ‘kvm_x86_dev_get_attr’:
  arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
        |                                              ^
  arch/x86/include/asm/uaccess.h:221:31: note: in definition of macro ‘do_put_user_call’
    221 |         register __typeof__(*(ptr)) __val_pu asm("%"_ASM_AX);           \
        |                               ^~~
  arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
        |                     ^~~~~~~~
  arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
        |                                              ^
  arch/x86/include/asm/uaccess.h:223:21: note: in definition of macro ‘do_put_user_call’
    223 |         __ptr_pu = (ptr);                                               \
        |                     ^~~
  arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
        |                     ^~~~~~~~
  arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
        |                                              ^
  arch/x86/include/asm/uaccess.h:230:45: note: in definition of macro ‘do_put_user_call’
    230 |                        [size] "i" (sizeof(*(ptr)))                      \
        |                                             ^~~
  arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
   4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))

Given that we're collectively 2 for 2 in mishandling {g,s}et_attr(), what about
a prep pacth like so?  Compile tested only...

From: Sean Christopherson <seanjc@google.com>
Date: Thu, 27 Jan 2022 07:31:53 -0800
Subject: [PATCH] KVM: x86: Add a helper to retrieve userspace address from
 kvm_device_attr

Add a helper to handle converting the u64 userspace address embedded in
struct kvm_device_attr into a userspace pointer, it's all too easy to
forget the intermediate "unsigned long" cast as well as the truncation
check.

No functional change intended.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/x86.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 8033eca6f3a1..67836f7c71f5 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4335,14 +4335,28 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	return r;
 }

+static inline void __user *kvm_get_attr_addr(struct kvm_device_attr *attr)
+{
+	void __user *uaddr = (void __user*)(unsigned long)attr->addr;
+
+	if ((u64)(unsigned long)uaddr != attr->addr)
+		return ERR_PTR(-EFAULT);
+	return uaddr;
+}
+
 static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
 {
+	u64 __user *uaddr = kvm_get_attr_addr(attr);
+
 	if (attr->group)
 		return -ENXIO;

+	if (IS_ERR(uaddr))
+		return PTR_ERR(uaddr);
+
 	switch (attr->attr) {
 	case KVM_X86_XCOMP_GUEST_SUPP:
-		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
+		if (put_user(supported_xcr0, uaddr))
 			return -EFAULT;
 		return 0;
 	default:
@@ -5070,11 +5084,11 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,
 static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
 				 struct kvm_device_attr *attr)
 {
-	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
+	u64 __user *uaddr = kvm_get_attr_addr(attr);
 	int r;

-	if ((u64)(unsigned long)uaddr != attr->addr)
-		return -EFAULT;
+	if (IS_ERR(uaddr))
+		return PTR_ERR(uaddr);

 	switch (attr->attr) {
 	case KVM_VCPU_TSC_OFFSET:
@@ -5093,12 +5107,12 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
 static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
 				 struct kvm_device_attr *attr)
 {
-	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
+	u64 __user *uaddr = kvm_get_attr_addr(attr);
 	struct kvm *kvm = vcpu->kvm;
 	int r;

-	if ((u64)(unsigned long)uaddr != attr->addr)
-		return -EFAULT;
+	if (IS_ERR(uaddr))
+		return PTR_ERR(uaddr);

 	switch (attr->attr) {
 	case KVM_VCPU_TSC_OFFSET: {
--



[*] https://lore.kernel.org/all/20211007231647.3553604-1-seanjc@google.com


> +			return -EFAULT;
> +		return 0;
> +	default:
> +		return -ENXIO;
> +		break;
> +	}
> +}
> +
Yang Zhong Jan. 28, 2022, 2:41 a.m. UTC | #2
On Thu, Jan 27, 2022 at 03:35:50PM +0000, Sean Christopherson wrote:
> On Wed, Jan 26, 2022, Paolo Bonzini wrote:
> > +static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
> > +{
> > +	if (attr->group)
> > +		return -ENXIO;
> > +
> > +	switch (attr->attr) {
> > +	case KVM_X86_XCOMP_GUEST_SUPP:
> > +		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Deja vu[*].
> 
>   arch/x86/kvm/x86.c: In function ‘kvm_x86_dev_get_attr’:
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:221:31: note: in definition of macro ‘do_put_user_call’
>     221 |         register __typeof__(*(ptr)) __val_pu asm("%"_ASM_AX);           \
>         |                               ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                     ^~~~~~~~
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:223:21: note: in definition of macro ‘do_put_user_call’
>     223 |         __ptr_pu = (ptr);                                               \
>         |                     ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                     ^~~~~~~~
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:230:45: note: in definition of macro ‘do_put_user_call’
>     230 |                        [size] "i" (sizeof(*(ptr)))                      \
>         |                                             ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Given that we're collectively 2 for 2 in mishandling {g,s}et_attr(), what about
> a prep pacth like so?  Compile tested only...
>

  Sean, It's strange that I could not find those issues in my last day's build.
  
  My build environment:
  #make -v
  GNU Make 4.3
  
  # gcc -v
  gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)

  I will apply your extra patch to check again, thanks!
  

  Yang

 
> From: Sean Christopherson <seanjc@google.com>
> Date: Thu, 27 Jan 2022 07:31:53 -0800
> Subject: [PATCH] KVM: x86: Add a helper to retrieve userspace address from
>  kvm_device_attr
> 
> Add a helper to handle converting the u64 userspace address embedded in
> struct kvm_device_attr into a userspace pointer, it's all too easy to
> forget the intermediate "unsigned long" cast as well as the truncation
> check.
> 
> No functional change intended.
> 
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/x86.c | 28 +++++++++++++++++++++-------
>  1 file changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 8033eca6f3a1..67836f7c71f5 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4335,14 +4335,28 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>  	return r;
>  }
> 
> +static inline void __user *kvm_get_attr_addr(struct kvm_device_attr *attr)
> +{
> +	void __user *uaddr = (void __user*)(unsigned long)attr->addr;
> +
> +	if ((u64)(unsigned long)uaddr != attr->addr)
> +		return ERR_PTR(-EFAULT);
> +	return uaddr;
> +}
> +
>  static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
>  {
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
> +
>  	if (attr->group)
>  		return -ENXIO;
> 
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> +
>  	switch (attr->attr) {
>  	case KVM_X86_XCOMP_GUEST_SUPP:
> -		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> +		if (put_user(supported_xcr0, uaddr))
>  			return -EFAULT;
>  		return 0;
>  	default:
> @@ -5070,11 +5084,11 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,
>  static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>  				 struct kvm_device_attr *attr)
>  {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>  	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>  	switch (attr->attr) {
>  	case KVM_VCPU_TSC_OFFSET:
> @@ -5093,12 +5107,12 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>  static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
>  				 struct kvm_device_attr *attr)
>  {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>  	struct kvm *kvm = vcpu->kvm;
>  	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>  	switch (attr->attr) {
>  	case KVM_VCPU_TSC_OFFSET: {
> --
> 
> 
> 
> [*] https://lore.kernel.org/all/20211007231647.3553604-1-seanjc@google.com
> 
> 
> > +			return -EFAULT;
> > +		return 0;
> > +	default:
> > +		return -ENXIO;
> > +		break;
> > +	}
> > +}
> > +
Yang Zhong Jan. 28, 2022, 6:07 a.m. UTC | #3
On Thu, Jan 27, 2022 at 03:35:50PM +0000, Sean Christopherson wrote:
> On Wed, Jan 26, 2022, Paolo Bonzini wrote:
> > +static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
> > +{
> > +	if (attr->group)
> > +		return -ENXIO;
> > +
> > +	switch (attr->attr) {
> > +	case KVM_X86_XCOMP_GUEST_SUPP:
> > +		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Deja vu[*].
> 
>   arch/x86/kvm/x86.c: In function ‘kvm_x86_dev_get_attr’:
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:221:31: note: in definition of macro ‘do_put_user_call’
>     221 |         register __typeof__(*(ptr)) __val_pu asm("%"_ASM_AX);           \
>         |                               ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                     ^~~~~~~~
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:223:21: note: in definition of macro ‘do_put_user_call’
>     223 |         __ptr_pu = (ptr);                                               \
>         |                     ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                     ^~~~~~~~
>   arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>         |                                              ^
>   arch/x86/include/asm/uaccess.h:230:45: note: in definition of macro ‘do_put_user_call’
>     230 |                        [size] "i" (sizeof(*(ptr)))                      \
>         |                                             ^~~
>   arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>    4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Given that we're collectively 2 for 2 in mishandling {g,s}et_attr(), what about
> a prep pacth like so?  Compile tested only...
>

  Just found those issues are from 32bit kernel build, thanks!
  
  I applied below patch, and did AMX functions test, this patch work well with AMX.

  Thanks!
  Yang

 
> From: Sean Christopherson <seanjc@google.com>
> Date: Thu, 27 Jan 2022 07:31:53 -0800
> Subject: [PATCH] KVM: x86: Add a helper to retrieve userspace address from
>  kvm_device_attr
> 
> Add a helper to handle converting the u64 userspace address embedded in
> struct kvm_device_attr into a userspace pointer, it's all too easy to
> forget the intermediate "unsigned long" cast as well as the truncation
> check.
> 
> No functional change intended.
> 
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>  arch/x86/kvm/x86.c | 28 +++++++++++++++++++++-------
>  1 file changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 8033eca6f3a1..67836f7c71f5 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4335,14 +4335,28 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>  	return r;
>  }
> 
> +static inline void __user *kvm_get_attr_addr(struct kvm_device_attr *attr)
> +{
> +	void __user *uaddr = (void __user*)(unsigned long)attr->addr;
> +
> +	if ((u64)(unsigned long)uaddr != attr->addr)
> +		return ERR_PTR(-EFAULT);
> +	return uaddr;
> +}
> +
>  static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
>  {
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
> +
>  	if (attr->group)
>  		return -ENXIO;
> 
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> +
>  	switch (attr->attr) {
>  	case KVM_X86_XCOMP_GUEST_SUPP:
> -		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> +		if (put_user(supported_xcr0, uaddr))
>  			return -EFAULT;
>  		return 0;
>  	default:
> @@ -5070,11 +5084,11 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,
>  static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>  				 struct kvm_device_attr *attr)
>  {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>  	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>  	switch (attr->attr) {
>  	case KVM_VCPU_TSC_OFFSET:
> @@ -5093,12 +5107,12 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>  static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
>  				 struct kvm_device_attr *attr)
>  {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>  	struct kvm *kvm = vcpu->kvm;
>  	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>  	switch (attr->attr) {
>  	case KVM_VCPU_TSC_OFFSET: {
> --
> 
> 
> 
> [*] https://lore.kernel.org/all/20211007231647.3553604-1-seanjc@google.com
> 
> 
> > +			return -EFAULT;
> > +		return 0;
> > +	default:
> > +		return -ENXIO;
> > +		break;
> > +	}
> > +}
> > +
Paolo Bonzini Jan. 28, 2022, 12:33 p.m. UTC | #4
On 1/27/22 16:35, Sean Christopherson wrote:
> On Wed, Jan 26, 2022, Paolo Bonzini wrote:
>> +static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
>> +{
>> +	if (attr->group)
>> +		return -ENXIO;
>> +
>> +	switch (attr->attr) {
>> +	case KVM_X86_XCOMP_GUEST_SUPP:
>> +		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Deja vu[*].
> 
>    arch/x86/kvm/x86.c: In function ‘kvm_x86_dev_get_attr’:
>    arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>          |                                              ^
>    arch/x86/include/asm/uaccess.h:221:31: note: in definition of macro ‘do_put_user_call’
>      221 |         register __typeof__(*(ptr)) __val_pu asm("%"_ASM_AX);           \
>          |                               ^~~
>    arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>          |                     ^~~~~~~~
>    arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>          |                                              ^
>    arch/x86/include/asm/uaccess.h:223:21: note: in definition of macro ‘do_put_user_call’
>      223 |         __ptr_pu = (ptr);                                               \
>          |                     ^~~
>    arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>          |                     ^~~~~~~~
>    arch/x86/kvm/x86.c:4345:46: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
>          |                                              ^
>    arch/x86/include/asm/uaccess.h:230:45: note: in definition of macro ‘do_put_user_call’
>      230 |                        [size] "i" (sizeof(*(ptr)))                      \
>          |                                             ^~~
>    arch/x86/kvm/x86.c:4345:21: note: in expansion of macro ‘put_user’
>     4345 |                 if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> 
> Given that we're collectively 2 for 2 in mishandling {g,s}et_attr(), what about
> a prep pacth like so?  Compile tested only...
> 
> From: Sean Christopherson <seanjc@google.com>
> Date: Thu, 27 Jan 2022 07:31:53 -0800
> Subject: [PATCH] KVM: x86: Add a helper to retrieve userspace address from
>   kvm_device_attr
> 
> Add a helper to handle converting the u64 userspace address embedded in
> struct kvm_device_attr into a userspace pointer, it's all too easy to
> forget the intermediate "unsigned long" cast as well as the truncation
> check.
> 
> No functional change intended.
> 
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>   arch/x86/kvm/x86.c | 28 +++++++++++++++++++++-------
>   1 file changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 8033eca6f3a1..67836f7c71f5 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4335,14 +4335,28 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>   	return r;
>   }
> 
> +static inline void __user *kvm_get_attr_addr(struct kvm_device_attr *attr)
> +{
> +	void __user *uaddr = (void __user*)(unsigned long)attr->addr;
> +
> +	if ((u64)(unsigned long)uaddr != attr->addr)
> +		return ERR_PTR(-EFAULT);
> +	return uaddr;
> +}
> +
>   static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
>   {
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
> +
>   	if (attr->group)
>   		return -ENXIO;
> 
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> +
>   	switch (attr->attr) {
>   	case KVM_X86_XCOMP_GUEST_SUPP:
> -		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
> +		if (put_user(supported_xcr0, uaddr))
>   			return -EFAULT;
>   		return 0;
>   	default:
> @@ -5070,11 +5084,11 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,
>   static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>   				 struct kvm_device_attr *attr)
>   {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>   	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>   	switch (attr->attr) {
>   	case KVM_VCPU_TSC_OFFSET:
> @@ -5093,12 +5107,12 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
>   static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
>   				 struct kvm_device_attr *attr)
>   {
> -	u64 __user *uaddr = (u64 __user *)(unsigned long)attr->addr;
> +	u64 __user *uaddr = kvm_get_attr_addr(attr);
>   	struct kvm *kvm = vcpu->kvm;
>   	int r;
> 
> -	if ((u64)(unsigned long)uaddr != attr->addr)
> -		return -EFAULT;
> +	if (IS_ERR(uaddr))
> +		return PTR_ERR(uaddr);
> 
>   	switch (attr->attr) {
>   	case KVM_VCPU_TSC_OFFSET: {
> --
> 

Nice, I applied it.

Paolo
diff mbox series

Patch

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index bb8cfddbb22d..a4267104db50 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -3268,6 +3268,7 @@  number.
 
 :Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device,
              KVM_CAP_VCPU_ATTRIBUTES for vcpu device
+             KVM_CAP_SYS_ATTRIBUTES for system (/dev/kvm) device (no set)
 :Type: device ioctl, vm ioctl, vcpu ioctl
 :Parameters: struct kvm_device_attr
 :Returns: 0 on success, -1 on error
@@ -3302,7 +3303,8 @@  transferred is defined by the particular attribute.
 ------------------------
 
 :Capability: KVM_CAP_DEVICE_CTRL, KVM_CAP_VM_ATTRIBUTES for vm device,
-	     KVM_CAP_VCPU_ATTRIBUTES for vcpu device
+             KVM_CAP_VCPU_ATTRIBUTES for vcpu device
+             KVM_CAP_SYS_ATTRIBUTES for system (/dev/kvm) device
 :Type: device ioctl, vm ioctl, vcpu ioctl
 :Parameters: struct kvm_device_attr
 :Returns: 0 on success, -1 on error
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 2da3316bb559..bf6e96011dfe 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -452,6 +452,9 @@  struct kvm_sync_regs {
 
 #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE	0x00000001
 
+/* attributes for system fd (group 0) */
+#define KVM_X86_XCOMP_GUEST_SUPP	0
+
 struct kvm_vmx_nested_state_data {
 	__u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
 	__u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE];
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 056e30f85424..b533301af95a 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4229,6 +4229,7 @@  int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_SREGS2:
 	case KVM_CAP_EXIT_ON_EMULATION_FAILURE:
 	case KVM_CAP_VCPU_ATTRIBUTES:
+	case KVM_CAP_SYS_ATTRIBUTES:
 		r = 1;
 		break;
 	case KVM_CAP_EXIT_HYPERCALL:
@@ -4331,7 +4332,35 @@  int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 		break;
 	}
 	return r;
+}
+
+static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr)
+{
+	if (attr->group)
+		return -ENXIO;
+
+	switch (attr->attr) {
+	case KVM_X86_XCOMP_GUEST_SUPP:
+		if (put_user(supported_xcr0, (u64 __user *)attr->addr))
+			return -EFAULT;
+		return 0;
+	default:
+		return -ENXIO;
+		break;
+	}
+}
+
+static int kvm_x86_dev_has_attr(struct kvm_device_attr *attr)
+{
+	if (attr->group)
+		return -ENXIO;
 
+	switch (attr->attr) {
+	case KVM_X86_XCOMP_GUEST_SUPP:
+		return 0;
+	default:
+		return -ENXIO;
+	}
 }
 
 long kvm_arch_dev_ioctl(struct file *filp,
@@ -4422,6 +4451,22 @@  long kvm_arch_dev_ioctl(struct file *filp,
 	case KVM_GET_SUPPORTED_HV_CPUID:
 		r = kvm_ioctl_get_supported_hv_cpuid(NULL, argp);
 		break;
+	case KVM_GET_DEVICE_ATTR: {
+		struct kvm_device_attr attr;
+		r = -EFAULT;
+		if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+			break;
+		r = kvm_x86_dev_get_attr(&attr);
+		break;
+	}
+	case KVM_HAS_DEVICE_ATTR: {
+		struct kvm_device_attr attr;
+		r = -EFAULT;
+		if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
+			break;
+		r = kvm_x86_dev_has_attr(&attr);
+		break;
+	}
 	default:
 		r = -EINVAL;
 		break;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 9563d294f181..b46bcdb0cab1 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1133,6 +1133,7 @@  struct kvm_ppc_resize_hpt {
 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
 #define KVM_CAP_VM_GPA_BITS 207
 #define KVM_CAP_XSAVE2 208
+#define KVM_CAP_SYS_ATTRIBUTES 209
 
 #ifdef KVM_CAP_IRQ_ROUTING