kvm: arm64: Restrict {GET,SET}_ONE_REG for SPCI partitions
User space is only allowed to access x0-7 for arguments before the first
run and after that point is not permitted to modify or observe any
register state.
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 57ae016..5ebb5ca 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -401,6 +401,11 @@
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs)
+static inline u64 core_reg_offset_from_id(u64 id)
+{
+ return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
+}
+
/*
* Only use __vcpu_sys_reg if you know you want the memory backed version of a
* register, and not the one most recently accessed by a running VCPU. For
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index f958a61..d2cff69 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1120,6 +1120,10 @@
if (copy_from_user(®, argp, sizeof(reg)))
break;
+ r = kvm_spci_check_vcpu_access_reg(vcpu, ®);
+ if (r)
+ break;
+
if (ioctl == KVM_SET_ONE_REG)
r = kvm_arm_set_reg(vcpu, ®);
else
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 23ebe51..fae63c3 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -52,11 +52,6 @@
off < KVM_REG_ARM_CORE_REG(fp_regs.fpsr);
}
-static u64 core_reg_offset_from_id(u64 id)
-{
- return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
-}
-
static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
{
int size;
diff --git a/arch/arm64/kvm/spci.c b/arch/arm64/kvm/spci.c
index 90e178b..4a1d998 100644
--- a/arch/arm64/kvm/spci.c
+++ b/arch/arm64/kvm/spci.c
@@ -541,6 +541,35 @@
return 0;
}
+/*
+ * Checks the registers can be accessed by user space. If the vCPU is part of an
+ * SPCI partition, the only registers that can be accessed are x0-7 and,
+ * further, they can only be accessed before the first run. This allows user
+ * space to pass in some initial arguments but does not allow it the
+ * subsequently modify or observe the registers state of the vCPU.
+ */
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+ struct kvm_one_reg *reg)
+{
+ const struct kvm_spci_partition *part = part_get_linked(vcpu->kvm);
+
+ if (!part)
+ return 0;
+
+ if (likely(vcpu->arch.has_run_once))
+ return -EPERM;
+
+ switch (core_reg_offset_from_id(reg->id)) {
+ case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
+ KVM_REG_ARM_CORE_REG(regs.regs[7]):
+ break;
+ default:
+ return -EPERM;
+ }
+
+ return 0;
+}
+
/* Early memory reservation parsing. */
static int __init spci_rmem_err(const char *type, struct reserved_mem *rmem,
const char *reason)
diff --git a/include/kvm/arm_spci.h b/include/kvm/arm_spci.h
index 872e34b..5bd7071 100644
--- a/include/kvm/arm_spci.h
+++ b/include/kvm/arm_spci.h
@@ -45,6 +45,8 @@
int kvm_spci_check_vcpu_init_features(const struct kvm_vcpu *vcpu,
const struct kvm_vcpu_init *init);
int kvm_spci_vcpu_first_run_init(struct kvm_vcpu *vcpu);
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+ struct kvm_one_reg *reg);
#else
@@ -64,6 +66,11 @@
{
return 0;
}
+int kvm_spci_check_vcpu_access_reg(struct kvm_vcpu *vcpu,
+ struct kvm_one_reg *reg)
+{
+ return 0;
+}
#endif /* CONFIG_KVM_ARM_SPCI */
#endif /* __KVM_ARM_SPCI_H */