| From 96b4af5813f326ee64c737838505c47bf0323d39 Mon Sep 17 00:00:00 2001 |
| From: Marc Zyngier <maz@kernel.org> |
| Date: Wed, 29 Apr 2020 11:21:55 +0100 |
| Subject: [PATCH] KVM: arm64: Fix 32bit PC wrap-around |
| |
| commit 0225fd5e0a6a32af7af0aefac45c8ebf19dc5183 upstream. |
| |
| In the unlikely event that a 32bit vcpu traps into the hypervisor |
| on an instruction that is located right at the end of the 32bit |
| range, the emulation of that instruction is going to increment |
| PC past the 32bit range. This isn't great, as userspace can then |
| observe this value and get a bit confused. |
| |
| Conversly, userspace can do things like (in the context of a 64bit |
| guest that is capable of 32bit EL0) setting PSTATE to AArch64-EL0, |
| set PC to a 64bit value, change PSTATE to AArch32-USR, and observe |
| that PC hasn't been truncated. More confusion. |
| |
| Fix both by: |
| - truncating PC increments for 32bit guests |
| - sanitizing all 32bit regs every time a core reg is changed by |
| userspace, and that PSTATE indicates a 32bit mode. |
| |
| Cc: stable@vger.kernel.org |
| Acked-by: Will Deacon <will@kernel.org> |
| Signed-off-by: Marc Zyngier <maz@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c |
| index dfd626447482..5271ab366bee 100644 |
| --- a/arch/arm64/kvm/guest.c |
| +++ b/arch/arm64/kvm/guest.c |
| @@ -202,6 +202,13 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) |
| } |
| |
| memcpy((u32 *)regs + off, valp, KVM_REG_SIZE(reg->id)); |
| + |
| + if (*vcpu_cpsr(vcpu) & PSR_MODE32_BIT) { |
| + int i; |
| + |
| + for (i = 0; i < 16; i++) |
| + *vcpu_reg32(vcpu, i) = (u32)*vcpu_reg32(vcpu, i); |
| + } |
| out: |
| return err; |
| } |
| diff --git a/virt/kvm/arm/hyp/aarch32.c b/virt/kvm/arm/hyp/aarch32.c |
| index d31f267961e7..25c0e47d57cb 100644 |
| --- a/virt/kvm/arm/hyp/aarch32.c |
| +++ b/virt/kvm/arm/hyp/aarch32.c |
| @@ -125,12 +125,16 @@ static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu) |
| */ |
| void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr) |
| { |
| + u32 pc = *vcpu_pc(vcpu); |
| bool is_thumb; |
| |
| is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_AA32_T_BIT); |
| if (is_thumb && !is_wide_instr) |
| - *vcpu_pc(vcpu) += 2; |
| + pc += 2; |
| else |
| - *vcpu_pc(vcpu) += 4; |
| + pc += 4; |
| + |
| + *vcpu_pc(vcpu) = pc; |
| + |
| kvm_adjust_itstate(vcpu); |
| } |
| -- |
| 2.7.4 |
| |