[1/3] arm64: KVM: Tighten guest core register access from userspace
diff mbox series

Message ID 1550581355-7068-2-git-send-email-paolo.pisati@canonical.com
State New
Headers show
Series
  • CVE-2018-18021 - arm64 KVM DoS/privesc
Related show

Commit Message

Paolo Pisati Feb. 19, 2019, 1:02 p.m. UTC
From: Dave Martin <Dave.Martin@arm.com>

We currently allow userspace to access the core register file
in about any possible way, including straddling multiple
registers and doing unaligned accesses.

This is not the expected use of the ABI, and nobody is actually
using it that way. Let's tighten it by explicitly checking
the size and alignment for each field of the register file.

Cc: <stable@vger.kernel.org>
Fixes: 2f4a07c5f9fe ("arm64: KVM: guest one-reg interface")
Reviewed-by: Christoffer Dall <christoffer.dall@arm.com>
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
[maz: rewrote Dave's initial patch to be more easily backported]
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>

(cherry picked from commit d26c25a9d19b5976b319af528886f89cf455692d)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
---
 arch/arm64/kvm/guest.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

Comments

Tyler Hicks Feb. 20, 2019, 8:57 a.m. UTC | #1
On 2019-02-19 14:02:33, Paolo Pisati wrote:
> From: Dave Martin <Dave.Martin@arm.com>
> 
> We currently allow userspace to access the core register file
> in about any possible way, including straddling multiple
> registers and doing unaligned accesses.
> 
> This is not the expected use of the ABI, and nobody is actually
> using it that way. Let's tighten it by explicitly checking
> the size and alignment for each field of the register file.
> 
> Cc: <stable@vger.kernel.org>
> Fixes: 2f4a07c5f9fe ("arm64: KVM: guest one-reg interface")
> Reviewed-by: Christoffer Dall <christoffer.dall@arm.com>
> Reviewed-by: Mark Rutland <mark.rutland@arm.com>
> Signed-off-by: Dave Martin <Dave.Martin@arm.com>
> [maz: rewrote Dave's initial patch to be more easily backported]
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> Signed-off-by: Will Deacon <will.deacon@arm.com>

When applying this patch, the CVE id needs to be added to the commit
message:

CVE-2018-18021

With that change,

Acked-by: Tyler Hicks <tyhicks@canonical.com>

Tyler

> 
> (cherry picked from commit d26c25a9d19b5976b319af528886f89cf455692d)
> Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
> ---
>  arch/arm64/kvm/guest.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 45 insertions(+)
> 
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 811f04c..3f4817d 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -57,6 +57,45 @@ 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 validate_core_offset(const struct kvm_one_reg *reg)
> +{
> +	u64 off = core_reg_offset_from_id(reg->id);
> +	int size;
> +
> +	switch (off) {
> +	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
> +	     KVM_REG_ARM_CORE_REG(regs.regs[30]):
> +	case KVM_REG_ARM_CORE_REG(regs.sp):
> +	case KVM_REG_ARM_CORE_REG(regs.pc):
> +	case KVM_REG_ARM_CORE_REG(regs.pstate):
> +	case KVM_REG_ARM_CORE_REG(sp_el1):
> +	case KVM_REG_ARM_CORE_REG(elr_el1):
> +	case KVM_REG_ARM_CORE_REG(spsr[0]) ...
> +	     KVM_REG_ARM_CORE_REG(spsr[KVM_NR_SPSR - 1]):
> +		size = sizeof(__u64);
> +		break;
> +
> +	case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...
> +	     KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]):
> +		size = sizeof(__uint128_t);
> +		break;
> +
> +	case KVM_REG_ARM_CORE_REG(fp_regs.fpsr):
> +	case KVM_REG_ARM_CORE_REG(fp_regs.fpcr):
> +		size = sizeof(__u32);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (KVM_REG_SIZE(reg->id) == size &&
> +	    IS_ALIGNED(off, size / sizeof(__u32)))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
>  static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
>  	/*
> @@ -76,6 +115,9 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	    (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
>  		return -ENOENT;
>  
> +	if (validate_core_offset(reg))
> +		return -EINVAL;
> +
>  	if (copy_to_user(uaddr, ((u32 *)regs) + off, KVM_REG_SIZE(reg->id)))
>  		return -EFAULT;
>  
> @@ -98,6 +140,9 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  	    (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
>  		return -ENOENT;
>  
> +	if (validate_core_offset(reg))
> +		return -EINVAL;
> +
>  	if (KVM_REG_SIZE(reg->id) > sizeof(tmp))
>  		return -EINVAL;
>  
> -- 
> 2.7.4
> 
> 
> -- 
> kernel-team mailing list
> kernel-team@lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team

Patch
diff mbox series

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 811f04c..3f4817d 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -57,6 +57,45 @@  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 validate_core_offset(const struct kvm_one_reg *reg)
+{
+	u64 off = core_reg_offset_from_id(reg->id);
+	int size;
+
+	switch (off) {
+	case KVM_REG_ARM_CORE_REG(regs.regs[0]) ...
+	     KVM_REG_ARM_CORE_REG(regs.regs[30]):
+	case KVM_REG_ARM_CORE_REG(regs.sp):
+	case KVM_REG_ARM_CORE_REG(regs.pc):
+	case KVM_REG_ARM_CORE_REG(regs.pstate):
+	case KVM_REG_ARM_CORE_REG(sp_el1):
+	case KVM_REG_ARM_CORE_REG(elr_el1):
+	case KVM_REG_ARM_CORE_REG(spsr[0]) ...
+	     KVM_REG_ARM_CORE_REG(spsr[KVM_NR_SPSR - 1]):
+		size = sizeof(__u64);
+		break;
+
+	case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ...
+	     KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]):
+		size = sizeof(__uint128_t);
+		break;
+
+	case KVM_REG_ARM_CORE_REG(fp_regs.fpsr):
+	case KVM_REG_ARM_CORE_REG(fp_regs.fpcr):
+		size = sizeof(__u32);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (KVM_REG_SIZE(reg->id) == size &&
+	    IS_ALIGNED(off, size / sizeof(__u32)))
+		return 0;
+
+	return -EINVAL;
+}
+
 static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 {
 	/*
@@ -76,6 +115,9 @@  static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	    (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
 		return -ENOENT;
 
+	if (validate_core_offset(reg))
+		return -EINVAL;
+
 	if (copy_to_user(uaddr, ((u32 *)regs) + off, KVM_REG_SIZE(reg->id)))
 		return -EFAULT;
 
@@ -98,6 +140,9 @@  static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
 	    (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs)
 		return -ENOENT;
 
+	if (validate_core_offset(reg))
+		return -EINVAL;
+
 	if (KVM_REG_SIZE(reg->id) > sizeof(tmp))
 		return -EINVAL;