[PATCH v2 12/20] KVM: arm64: vgic-v3: Consolidate userspace access for MMIO registers
Marc Zyngier
maz at kernel.org
Thu Jul 14 08:20:16 PDT 2022
For userspace accesses to GICv3 MMIO registers (and related data),
vgic_v3_{get,set}_attr are littered with {get,put}_user() calls,
making it hard to audit and reason about.
Consolidate all userspace accesses in vgic_v3_attr_regs_access(),
making the code far simpler to audit.
Reviewed-by: Reiji Watanabe <reijiw at google.com>
Signed-off-by: Marc Zyngier <maz at kernel.org>
---
arch/arm64/kvm/vgic/vgic-kvm-device.c | 103 +++++++++-----------------
1 file changed, 37 insertions(+), 66 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c
index f02294b9aef1..e9db6795fb90 100644
--- a/arch/arm64/kvm/vgic/vgic-kvm-device.c
+++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c
@@ -512,18 +512,18 @@ int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
*
* @dev: kvm device handle
* @attr: kvm device attribute
- * @reg: address the value is read or written, NULL for sysregs
* @is_write: true if userspace is writing a register
*/
static int vgic_v3_attr_regs_access(struct kvm_device *dev,
struct kvm_device_attr *attr,
- u64 *reg, bool is_write)
+ bool is_write)
{
struct vgic_reg_attr reg_attr;
gpa_t addr;
struct kvm_vcpu *vcpu;
+ bool uaccess;
+ u32 val;
int ret;
- u32 tmp32;
ret = vgic_v3_parse_attr(dev, attr, ®_attr);
if (ret)
@@ -532,6 +532,21 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
vcpu = reg_attr.vcpu;
addr = reg_attr.addr;
+ switch (attr->group) {
+ case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
+ /* Sysregs uaccess is performed by the sysreg handling code */
+ uaccess = false;
+ break;
+ default:
+ uaccess = true;
+ }
+
+ if (uaccess && is_write) {
+ u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr;
+ if (get_user(val, uaddr))
+ return -EFAULT;
+ }
+
mutex_lock(&dev->kvm->lock);
if (unlikely(!vgic_initialized(dev->kvm))) {
@@ -546,20 +561,10 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
switch (attr->group) {
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
- if (is_write)
- tmp32 = *reg;
-
- ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &tmp32);
- if (!is_write)
- *reg = tmp32;
+ ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &val);
break;
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
- if (is_write)
- tmp32 = *reg;
-
- ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &tmp32);
- if (!is_write)
- *reg = tmp32;
+ ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &val);
break;
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
ret = vgic_v3_cpu_sysregs_uaccess(vcpu, attr, is_write);
@@ -570,14 +575,10 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
info = (attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK) >>
KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT;
if (info == VGIC_LEVEL_INFO_LINE_LEVEL) {
- if (is_write)
- tmp32 = *reg;
intid = attr->attr &
KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK;
ret = vgic_v3_line_level_info_uaccess(vcpu, is_write,
- intid, &tmp32);
- if (!is_write)
- *reg = tmp32;
+ intid, &val);
} else {
ret = -EINVAL;
}
@@ -591,6 +592,12 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
unlock_all_vcpus(dev->kvm);
out:
mutex_unlock(&dev->kvm->lock);
+
+ if (!ret && uaccess && !is_write) {
+ u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr;
+ ret = put_user(val, uaddr);
+ }
+
return ret;
}
@@ -605,30 +612,12 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
switch (attr->group) {
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
- case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
- u32 __user *uaddr = (u32 __user *)(long)attr->addr;
- u32 tmp32;
- u64 reg;
-
- if (get_user(tmp32, uaddr))
- return -EFAULT;
-
- reg = tmp32;
- return vgic_v3_attr_regs_access(dev, attr, ®, true);
- }
+ case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
+ return vgic_v3_attr_regs_access(dev, attr, true);
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
- return vgic_v3_attr_regs_access(dev, attr, NULL, true);
- case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
- u32 __user *uaddr = (u32 __user *)(long)attr->addr;
- u64 reg;
- u32 tmp32;
-
- if (get_user(tmp32, uaddr))
- return -EFAULT;
-
- reg = tmp32;
- return vgic_v3_attr_regs_access(dev, attr, ®, true);
- }
+ return vgic_v3_attr_regs_access(dev, attr, true);
+ case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
+ return vgic_v3_attr_regs_access(dev, attr, true);
case KVM_DEV_ARM_VGIC_GRP_CTRL: {
int ret;
@@ -662,30 +651,12 @@ static int vgic_v3_get_attr(struct kvm_device *dev,
switch (attr->group) {
case KVM_DEV_ARM_VGIC_GRP_DIST_REGS:
- case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: {
- u32 __user *uaddr = (u32 __user *)(long)attr->addr;
- u64 reg;
- u32 tmp32;
-
- ret = vgic_v3_attr_regs_access(dev, attr, ®, false);
- if (ret)
- return ret;
- tmp32 = reg;
- return put_user(tmp32, uaddr);
- }
+ case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
+ return vgic_v3_attr_regs_access(dev, attr, false);
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
- return vgic_v3_attr_regs_access(dev, attr, NULL, false);
- case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: {
- u32 __user *uaddr = (u32 __user *)(long)attr->addr;
- u64 reg;
- u32 tmp32;
-
- ret = vgic_v3_attr_regs_access(dev, attr, ®, false);
- if (ret)
- return ret;
- tmp32 = reg;
- return put_user(tmp32, uaddr);
- }
+ return vgic_v3_attr_regs_access(dev, attr, false);
+ case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
+ return vgic_v3_attr_regs_access(dev, attr, false);
}
return -ENXIO;
}
--
2.34.1
More information about the linux-arm-kernel
mailing list