aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Martin <Dave.Martin@arm.com>2019-06-12 08:44:49 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2019-06-12 10:53:55 -0400
commitdf205b5c63281e4f32caac22adda18fd68795e80 (patch)
treebf327a1ffc6a58b42eb69de47188941cff155c80
parent0c529ff789bc7a3efbc732753e0b0fd9f4d9a4a4 (diff)
KVM: arm64: Filter out invalid core register IDs in KVM_GET_REG_LIST
Since commit d26c25a9d19b ("arm64: KVM: Tighten guest core register access from userspace"), KVM_{GET,SET}_ONE_REG rejects register IDs that do not correspond to a single underlying architectural register. KVM_GET_REG_LIST was not changed to match however: instead, it simply yields a list of 32-bit register IDs that together cover the whole kvm_regs struct. This means that if userspace tries to use the resulting list of IDs directly to drive calls to KVM_*_ONE_REG, some of those calls will now fail. This was not the intention. Instead, iterating KVM_*_ONE_REG over the list of IDs returned by KVM_GET_REG_LIST should be guaranteed to work. This patch fixes the problem by splitting validate_core_offset() into a backend core_reg_size_from_offset() which does all of the work except for checking that the size field in the register ID matches, and kvm_arm_copy_reg_indices() and num_core_regs() are converted to use this to enumerate the valid offsets. kvm_arm_copy_reg_indices() now also sets the register ID size field appropriately based on the value returned, so the register ID supplied to userspace is fully qualified for use with the register access ioctls. Cc: stable@vger.kernel.org Fixes: d26c25a9d19b ("arm64: KVM: Tighten guest core register access from userspace") Signed-off-by: Dave Martin <Dave.Martin@arm.com> Reviewed-by: Andrew Jones <drjones@redhat.com> Tested-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--arch/arm64/kvm/guest.c53
1 files changed, 40 insertions, 13 deletions
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index ae734fcfd4ea..c8aa00179363 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -70,10 +70,8 @@ static u64 core_reg_offset_from_id(u64 id)
70 return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE); 70 return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
71} 71}
72 72
73static int validate_core_offset(const struct kvm_vcpu *vcpu, 73static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
74 const struct kvm_one_reg *reg)
75{ 74{
76 u64 off = core_reg_offset_from_id(reg->id);
77 int size; 75 int size;
78 76
79 switch (off) { 77 switch (off) {
@@ -103,8 +101,7 @@ static int validate_core_offset(const struct kvm_vcpu *vcpu,
103 return -EINVAL; 101 return -EINVAL;
104 } 102 }
105 103
106 if (KVM_REG_SIZE(reg->id) != size || 104 if (!IS_ALIGNED(off, size / sizeof(__u32)))
107 !IS_ALIGNED(off, size / sizeof(__u32)))
108 return -EINVAL; 105 return -EINVAL;
109 106
110 /* 107 /*
@@ -115,6 +112,21 @@ static int validate_core_offset(const struct kvm_vcpu *vcpu,
115 if (vcpu_has_sve(vcpu) && core_reg_offset_is_vreg(off)) 112 if (vcpu_has_sve(vcpu) && core_reg_offset_is_vreg(off))
116 return -EINVAL; 113 return -EINVAL;
117 114
115 return size;
116}
117
118static int validate_core_offset(const struct kvm_vcpu *vcpu,
119 const struct kvm_one_reg *reg)
120{
121 u64 off = core_reg_offset_from_id(reg->id);
122 int size = core_reg_size_from_offset(vcpu, off);
123
124 if (size < 0)
125 return -EINVAL;
126
127 if (KVM_REG_SIZE(reg->id) != size)
128 return -EINVAL;
129
118 return 0; 130 return 0;
119} 131}
120 132
@@ -447,19 +459,34 @@ static int copy_core_reg_indices(const struct kvm_vcpu *vcpu,
447{ 459{
448 unsigned int i; 460 unsigned int i;
449 int n = 0; 461 int n = 0;
450 const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE;
451 462
452 for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) { 463 for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) {
453 /* 464 u64 reg = KVM_REG_ARM64 | KVM_REG_ARM_CORE | i;
454 * The KVM_REG_ARM64_SVE regs must be used instead of 465 int size = core_reg_size_from_offset(vcpu, i);
455 * KVM_REG_ARM_CORE for accessing the FPSIMD V-registers on 466
456 * SVE-enabled vcpus: 467 if (size < 0)
457 */ 468 continue;
458 if (vcpu_has_sve(vcpu) && core_reg_offset_is_vreg(i)) 469
470 switch (size) {
471 case sizeof(__u32):
472 reg |= KVM_REG_SIZE_U32;
473 break;
474
475 case sizeof(__u64):
476 reg |= KVM_REG_SIZE_U64;
477 break;
478
479 case sizeof(__uint128_t):
480 reg |= KVM_REG_SIZE_U128;
481 break;
482
483 default:
484 WARN_ON(1);
459 continue; 485 continue;
486 }
460 487
461 if (uindices) { 488 if (uindices) {
462 if (put_user(core_reg | i, uindices)) 489 if (put_user(reg, uindices))
463 return -EFAULT; 490 return -EFAULT;
464 uindices++; 491 uindices++;
465 } 492 }