All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marc Zyngier <maz@kernel.org>
To: "Pierre-Clément Tosi" <ptosi@google.com>
Cc: kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	kvm@vger.kernel.org, James Morse <james.morse@arm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Oliver Upton <oliver.upton@linux.dev>,
	Zenghui Yu <yuzenghui@huawei.com>,
	Andrew Scull <ascull@google.com>
Subject: Re: [PATCH 01/10] KVM: arm64: Fix clobbered ELR in sync abort
Date: Sun, 17 Mar 2024 12:26:22 +0000	[thread overview]
Message-ID: <86a5mx11u9.wl-maz@kernel.org> (raw)
In-Reply-To: <0dfcc4c5c898941147723ba530c81ddc8399ef55.1710446682.git.ptosi@google.com>

On Thu, 14 Mar 2024 20:23:22 +0000,
Pierre-Clément Tosi <ptosi@google.com> wrote:
> 
> When the hypervisor receives a SError or synchronous exception (EL2h)

nit: since this also affects SError, $subject seems wrong.

> while running with the __kvm_hyp_vector and if ELR_EL2 doesn't point to
> an extable entry, it panics indirectly by overwriting ELR with the
> address of a panic handler in order for the asm routine it returns to to
> ERET into the handler. This is done (instead of a simple function call)
> to ensure that the panic handler runs with the SPSel that was in use
> when the exception was triggered, necessary to support features such as
> the shadow call stack.

I don't understand this. Who changes SPsel? At any given point, SP_EL0
is that of either the host or the guest, but never the hypervisor's.
If something was touching SP_EL0 behind our back, it would result in
corruption (see 6e977984f6d8 for a bit of rationale about it).

>
> However, this clobbers ELR_EL2 for the handler itself. As a result,
> hyp_panic(), when retrieving what it believes to be the PC where the
> exception happened, actually ends up reading the address of the panic
> handler that called it! This results in an erroneous and confusing panic
> message where the source of any synchronous exception (e.g. BUG() or
> kCFI) appears to be __guest_exit_panic, making it hard to locate the
> actual BRK instruction.
> 
> Therefore, store the original ELR_EL2 in a per-CPU struct and point the

Can you elaborate on *which* per-CPU structure you are using?

> sysreg to a routine that first restores it to its previous value before
> running __guest_exit_panic.
> 
> Fixes: 7db21530479f ("KVM: arm64: Restore hyp when panicking in guest context")
> Signed-off-by: Pierre-Clément Tosi <ptosi@google.com>
> ---
>  arch/arm64/kernel/asm-offsets.c         | 1 +
>  arch/arm64/kvm/hyp/entry.S              | 9 +++++++++
>  arch/arm64/kvm/hyp/include/hyp/switch.h | 6 ++++--
>  3 files changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 5a7dbbe0ce63..e62353168a57 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -128,6 +128,7 @@ int main(void)
>    DEFINE(VCPU_FAULT_DISR,	offsetof(struct kvm_vcpu, arch.fault.disr_el1));
>    DEFINE(VCPU_HCR_EL2,		offsetof(struct kvm_vcpu, arch.hcr_el2));
>    DEFINE(CPU_USER_PT_REGS,	offsetof(struct kvm_cpu_context, regs));
> +  DEFINE(CPU_ELR_EL2,		offsetof(struct kvm_cpu_context, sys_regs[ELR_EL2]));
>    DEFINE(CPU_RGSR_EL1,		offsetof(struct kvm_cpu_context, sys_regs[RGSR_EL1]));
>    DEFINE(CPU_GCR_EL1,		offsetof(struct kvm_cpu_context, sys_regs[GCR_EL1]));
>    DEFINE(CPU_APIAKEYLO_EL1,	offsetof(struct kvm_cpu_context, sys_regs[APIAKEYLO_EL1]));
> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> index f3aa7738b477..9cdf46da3051 100644
> --- a/arch/arm64/kvm/hyp/entry.S
> +++ b/arch/arm64/kvm/hyp/entry.S
> @@ -83,6 +83,15 @@ alternative_else_nop_endif
>  	eret
>  	sb
>  
> +SYM_INNER_LABEL(__guest_exit_panic_with_restored_elr, SYM_L_GLOBAL)

The name of the function isn't great. Crucially, 'with_restored_elr'
implies that ELR is already restored, while it is the hunk below that
does the 'restore' part.

Something like '__guest_exit_restore_elr_and_panic' seems more
appropriate.

> +	// x0-x29,lr: hyp regs
> +
> +	stp	x0, x1, [sp, #-16]!
> +	adr_this_cpu x0, kvm_hyp_ctxt, x1
> +	ldr	x0, [x0, #CPU_ELR_EL2]
> +	msr	elr_el2, x0
> +	ldp	x0, x1, [sp], #16
> +
>  SYM_INNER_LABEL(__guest_exit_panic, SYM_L_GLOBAL)
>  	// x2-x29,lr: vcpu regs
>  	// vcpu x0-x1 on the stack
> diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
> index a038320cdb08..6a8dc8d3c193 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/switch.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
> @@ -747,7 +747,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
>  
>  static inline void __kvm_unexpected_el2_exception(void)
>  {
> -	extern char __guest_exit_panic[];
> +	extern char __guest_exit_panic_with_restored_elr[];
>  	unsigned long addr, fixup;
>  	struct kvm_exception_table_entry *entry, *end;
>  	unsigned long elr_el2 = read_sysreg(elr_el2);
> @@ -769,7 +769,9 @@ static inline void __kvm_unexpected_el2_exception(void)
>  	}
>  
>  	/* Trigger a panic after restoring the hyp context. */
> -	write_sysreg(__guest_exit_panic, elr_el2);
> +	write_sysreg(__guest_exit_panic_with_restored_elr, elr_el2);
> +
> +	this_cpu_ptr(&kvm_hyp_ctxt)->sys_regs[ELR_EL2] = elr_el2;

nit: placing the saving of ELR_EL2 before overriding the sysreg makes
the whole thing a bit more readable, specially given the poor choice
of 'elr_el2' as a variable (visually clashing with 'elr_el2' as a
system register).

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

WARNING: multiple messages have this Message-ID (diff)
From: Marc Zyngier <maz@kernel.org>
To: "Pierre-Clément Tosi" <ptosi@google.com>
Cc: kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	kvm@vger.kernel.org, James Morse <james.morse@arm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Oliver Upton <oliver.upton@linux.dev>,
	Zenghui Yu <yuzenghui@huawei.com>,
	Andrew Scull <ascull@google.com>
Subject: Re: [PATCH 01/10] KVM: arm64: Fix clobbered ELR in sync abort
Date: Sun, 17 Mar 2024 12:26:22 +0000	[thread overview]
Message-ID: <86a5mx11u9.wl-maz@kernel.org> (raw)
In-Reply-To: <0dfcc4c5c898941147723ba530c81ddc8399ef55.1710446682.git.ptosi@google.com>

On Thu, 14 Mar 2024 20:23:22 +0000,
Pierre-Clément Tosi <ptosi@google.com> wrote:
> 
> When the hypervisor receives a SError or synchronous exception (EL2h)

nit: since this also affects SError, $subject seems wrong.

> while running with the __kvm_hyp_vector and if ELR_EL2 doesn't point to
> an extable entry, it panics indirectly by overwriting ELR with the
> address of a panic handler in order for the asm routine it returns to to
> ERET into the handler. This is done (instead of a simple function call)
> to ensure that the panic handler runs with the SPSel that was in use
> when the exception was triggered, necessary to support features such as
> the shadow call stack.

I don't understand this. Who changes SPsel? At any given point, SP_EL0
is that of either the host or the guest, but never the hypervisor's.
If something was touching SP_EL0 behind our back, it would result in
corruption (see 6e977984f6d8 for a bit of rationale about it).

>
> However, this clobbers ELR_EL2 for the handler itself. As a result,
> hyp_panic(), when retrieving what it believes to be the PC where the
> exception happened, actually ends up reading the address of the panic
> handler that called it! This results in an erroneous and confusing panic
> message where the source of any synchronous exception (e.g. BUG() or
> kCFI) appears to be __guest_exit_panic, making it hard to locate the
> actual BRK instruction.
> 
> Therefore, store the original ELR_EL2 in a per-CPU struct and point the

Can you elaborate on *which* per-CPU structure you are using?

> sysreg to a routine that first restores it to its previous value before
> running __guest_exit_panic.
> 
> Fixes: 7db21530479f ("KVM: arm64: Restore hyp when panicking in guest context")
> Signed-off-by: Pierre-Clément Tosi <ptosi@google.com>
> ---
>  arch/arm64/kernel/asm-offsets.c         | 1 +
>  arch/arm64/kvm/hyp/entry.S              | 9 +++++++++
>  arch/arm64/kvm/hyp/include/hyp/switch.h | 6 ++++--
>  3 files changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
> index 5a7dbbe0ce63..e62353168a57 100644
> --- a/arch/arm64/kernel/asm-offsets.c
> +++ b/arch/arm64/kernel/asm-offsets.c
> @@ -128,6 +128,7 @@ int main(void)
>    DEFINE(VCPU_FAULT_DISR,	offsetof(struct kvm_vcpu, arch.fault.disr_el1));
>    DEFINE(VCPU_HCR_EL2,		offsetof(struct kvm_vcpu, arch.hcr_el2));
>    DEFINE(CPU_USER_PT_REGS,	offsetof(struct kvm_cpu_context, regs));
> +  DEFINE(CPU_ELR_EL2,		offsetof(struct kvm_cpu_context, sys_regs[ELR_EL2]));
>    DEFINE(CPU_RGSR_EL1,		offsetof(struct kvm_cpu_context, sys_regs[RGSR_EL1]));
>    DEFINE(CPU_GCR_EL1,		offsetof(struct kvm_cpu_context, sys_regs[GCR_EL1]));
>    DEFINE(CPU_APIAKEYLO_EL1,	offsetof(struct kvm_cpu_context, sys_regs[APIAKEYLO_EL1]));
> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> index f3aa7738b477..9cdf46da3051 100644
> --- a/arch/arm64/kvm/hyp/entry.S
> +++ b/arch/arm64/kvm/hyp/entry.S
> @@ -83,6 +83,15 @@ alternative_else_nop_endif
>  	eret
>  	sb
>  
> +SYM_INNER_LABEL(__guest_exit_panic_with_restored_elr, SYM_L_GLOBAL)

The name of the function isn't great. Crucially, 'with_restored_elr'
implies that ELR is already restored, while it is the hunk below that
does the 'restore' part.

Something like '__guest_exit_restore_elr_and_panic' seems more
appropriate.

> +	// x0-x29,lr: hyp regs
> +
> +	stp	x0, x1, [sp, #-16]!
> +	adr_this_cpu x0, kvm_hyp_ctxt, x1
> +	ldr	x0, [x0, #CPU_ELR_EL2]
> +	msr	elr_el2, x0
> +	ldp	x0, x1, [sp], #16
> +
>  SYM_INNER_LABEL(__guest_exit_panic, SYM_L_GLOBAL)
>  	// x2-x29,lr: vcpu regs
>  	// vcpu x0-x1 on the stack
> diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
> index a038320cdb08..6a8dc8d3c193 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/switch.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
> @@ -747,7 +747,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
>  
>  static inline void __kvm_unexpected_el2_exception(void)
>  {
> -	extern char __guest_exit_panic[];
> +	extern char __guest_exit_panic_with_restored_elr[];
>  	unsigned long addr, fixup;
>  	struct kvm_exception_table_entry *entry, *end;
>  	unsigned long elr_el2 = read_sysreg(elr_el2);
> @@ -769,7 +769,9 @@ static inline void __kvm_unexpected_el2_exception(void)
>  	}
>  
>  	/* Trigger a panic after restoring the hyp context. */
> -	write_sysreg(__guest_exit_panic, elr_el2);
> +	write_sysreg(__guest_exit_panic_with_restored_elr, elr_el2);
> +
> +	this_cpu_ptr(&kvm_hyp_ctxt)->sys_regs[ELR_EL2] = elr_el2;

nit: placing the saving of ELR_EL2 before overriding the sysreg makes
the whole thing a bit more readable, specially given the poor choice
of 'elr_el2' as a variable (visually clashing with 'elr_el2' as a
system register).

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2024-03-17 12:26 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-14 20:23 [PATCH 00/10] KVM: arm64: Add support for hypervisor kCFI Pierre-Clément Tosi
2024-03-14 20:23 ` Pierre-Clément Tosi
2024-03-14 20:23 ` [PATCH 01/10] KVM: arm64: Fix clobbered ELR in sync abort Pierre-Clément Tosi
2024-03-14 20:23   ` Pierre-Clément Tosi
2024-03-17 12:26   ` Marc Zyngier [this message]
2024-03-17 12:26     ` Marc Zyngier
2024-03-14 20:23 ` [PATCH 02/10] KVM: arm64: Fix __pkvm_init_switch_pgd C signature Pierre-Clément Tosi
2024-03-14 20:23   ` Pierre-Clément Tosi
2024-03-14 20:23 ` [PATCH 03/10] KVM: arm64: Pass pointer to __pkvm_init_switch_pgd Pierre-Clément Tosi
2024-03-14 20:23   ` Pierre-Clément Tosi
2024-03-14 20:24 ` [PATCH 04/10] KVM: arm64: nVHE: Simplify __guest_exit_panic path Pierre-Clément Tosi
2024-03-14 20:24   ` Pierre-Clément Tosi
2024-03-14 20:24 ` [PATCH 05/10] KVM: arm64: nVHE: Add EL2 sync exception handler Pierre-Clément Tosi
2024-03-14 20:24   ` Pierre-Clément Tosi
2024-03-17 11:42   ` Marc Zyngier
2024-03-17 11:42     ` Marc Zyngier
2024-04-10 14:44     ` Pierre-Clément Tosi
2024-04-10 14:44       ` Pierre-Clément Tosi
2024-03-14 20:24 ` [PATCH 06/10] KVM: arm64: nVHE: gen-hyprel: Skip R_AARCH64_ABS32 Pierre-Clément Tosi
2024-03-14 20:24   ` Pierre-Clément Tosi
2024-03-14 20:25 ` [PATCH 07/10] KVM: arm64: VHE: Mark __hyp_call_panic __noreturn Pierre-Clément Tosi
2024-03-14 20:25   ` Pierre-Clément Tosi
2024-03-14 20:25 ` [PATCH 08/10] arm64: Move esr_comment() to <asm/esr.h> Pierre-Clément Tosi
2024-03-14 20:25   ` Pierre-Clément Tosi
2024-03-17 12:50   ` Marc Zyngier
2024-03-17 12:50     ` Marc Zyngier
2024-03-14 20:25 ` [PATCH 09/10] KVM: arm64: nVHE: Support CONFIG_CFI_CLANG at EL2 Pierre-Clément Tosi
2024-03-14 20:25   ` Pierre-Clément Tosi
2024-03-17 13:09   ` Marc Zyngier
2024-03-17 13:09     ` Marc Zyngier
2024-04-10 14:58     ` Pierre-Clément Tosi
2024-04-10 14:58       ` Pierre-Clément Tosi
2024-03-14 20:26 ` [PATCH 10/10] KVM: arm64: Improve CONFIG_CFI_CLANG error message Pierre-Clément Tosi
2024-03-14 20:26   ` Pierre-Clément Tosi
2024-03-14 22:40 ` [PATCH 00/10] KVM: arm64: Add support for hypervisor kCFI Marc Zyngier
2024-03-14 22:40   ` Marc Zyngier
2024-03-15 10:22   ` Pierre-Clément Tosi
2024-03-15 10:22     ` Pierre-Clément Tosi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86a5mx11u9.wl-maz@kernel.org \
    --to=maz@kernel.org \
    --cc=ascull@google.com \
    --cc=james.morse@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=oliver.upton@linux.dev \
    --cc=ptosi@google.com \
    --cc=suzuki.poulose@arm.com \
    --cc=yuzenghui@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.