Re: [PATCH 1/2] arm64: efi: Execute runtime services from a dedicated stack

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Ard,

One drive-by comment below...

On Mon, Dec 05, 2022 at 09:12:09PM +0100, Ard Biesheuvel wrote:
> With the introduction of PRMT in the ACPI subsystem, the EFI rts
> workqueue is no longer the only caller of efi_call_virt_pointer() in the
> kernel. This means the EFI runtime services lock is no longer sufficient
> to manage concurrent calls into firmware, but also that firmware calls
> may occur that are not marshalled via the workqueue mechanism, but
> originate directly from the caller context.
> 
> For added robustness, and to ensure that the runtime services have 8 KiB
> of stack space available as per the EFI spec, introduce a spinlock
> protected EFI runtime stack of 8 KiB, where the spinlock also ensures
> serialization between the EFI rts workqueue (which itself serializes EFI
> runtime calls) and other callers of efi_call_virt_pointer().
> 
> While at it, use the stack pivot to avoid reloading the shadow call
> stack pointer from the ordinary stack, as doing so could produce a
> gadget to defeat it.
> 
> Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx>
> ---
>  arch/arm64/include/asm/efi.h       |  3 +++
>  arch/arm64/kernel/efi-rt-wrapper.S | 13 +++++++++-
>  arch/arm64/kernel/efi.c            | 25 ++++++++++++++++++++

We'll need to teach the stack unwinder about this, or if we take an exception
from the EFI stack, the backtrace will terminate as soon as it hits a frame
record on the EFI stack.

In arch/arm64/kernel/stacktrace.c's arch_stack_walk(), that'll need to be added
to the array of stack bounds. Ideally we'd only add that when a thread is
making an EFI call.

Thanks,
Mark.

>  3 files changed, 40 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
> index 7c12e01c2b312e7b..1c408ec3c8b3a883 100644
> --- a/arch/arm64/include/asm/efi.h
> +++ b/arch/arm64/include/asm/efi.h
> @@ -25,6 +25,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
>  ({									\
>  	efi_virtmap_load();						\
>  	__efi_fpsimd_begin();						\
> +	spin_lock(&efi_rt_lock);					\
>  })
>  
>  #undef arch_efi_call_virt
> @@ -33,10 +34,12 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
>  
>  #define arch_efi_call_virt_teardown()					\
>  ({									\
> +	spin_unlock(&efi_rt_lock);					\
>  	__efi_fpsimd_end();						\
>  	efi_virtmap_unload();						\
>  })
>  
> +extern spinlock_t efi_rt_lock;
>  efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...);
>  
>  #define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
> diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S
> index 75691a2641c1c0f8..b2786b968fee68dd 100644
> --- a/arch/arm64/kernel/efi-rt-wrapper.S
> +++ b/arch/arm64/kernel/efi-rt-wrapper.S
> @@ -16,6 +16,12 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
>  	 */
>  	stp	x1, x18, [sp, #16]
>  
> +	ldr_l	x16, efi_rt_stack_top
> +	mov	sp, x16
> +#ifdef CONFIG_SHADOW_CALL_STACK
> +	str	x18, [sp, #-16]!
> +#endif
> +
>  	/*
>  	 * We are lucky enough that no EFI runtime services take more than
>  	 * 5 arguments, so all are passed in registers rather than via the
> @@ -29,6 +35,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
>  	mov	x4, x6
>  	blr	x8
>  
> +	mov	sp, x29
>  	ldp	x1, x2, [sp, #16]
>  	cmp	x2, x18
>  	ldp	x29, x30, [sp], #32
> @@ -42,6 +49,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
>  	 * called with preemption disabled and a separate shadow stack is used
>  	 * for interrupts.
>  	 */
> -	mov	x18, x2
> +#ifdef CONFIG_SHADOW_CALL_STACK
> +	ldr_l	x18, efi_rt_stack_top
> +	ldr	x18, [x18, #-16]
> +#endif
> +
>  	b	efi_handle_corrupted_x18	// tail call
>  SYM_FUNC_END(__efi_rt_asm_wrapper)
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> index a908a37f03678b6b..8cb2e005f8aca589 100644
> --- a/arch/arm64/kernel/efi.c
> +++ b/arch/arm64/kernel/efi.c
> @@ -144,3 +144,28 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)
>  	pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f);
>  	return s;
>  }
> +
> +DEFINE_SPINLOCK(efi_rt_lock);
> +
> +asmlinkage u64 *efi_rt_stack_top __ro_after_init;
> +
> +/* required by the EFI spec */
> +static_assert(THREAD_SIZE >= SZ_8K);
> +
> +int __init arm64_efi_rt_init(void)
> +{
> +	void *p = __vmalloc_node_range(THREAD_SIZE, THREAD_ALIGN,
> +				       VMALLOC_START, VMALLOC_END, GFP_KERNEL,
> +				       PAGE_KERNEL, 0, NUMA_NO_NODE,
> +				       __builtin_return_address(0));
> +
> +	if (!p) {
> +		pr_warn("Failed to allocate EFI runtime stack\n");
> +		clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
> +		return -ENOMEM;
> +	}
> +
> +	efi_rt_stack_top = p + THREAD_SIZE;
> +	return 0;
> +}
> +core_initcall(arm64_efi_rt_init);
> -- 
> 2.35.1
> 



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux