[PATCH 2/4] arm64: assembler: Add macros for return address protection
Mark Rutland
mark.rutland at arm.com
Wed Nov 30 06:15:54 PST 2022
On Tue, Nov 29, 2022 at 03:18:01PM +0100, Ard Biesheuvel wrote:
> When in-kernel pointer authentication is configured, emit PACIASP and
> AUTIASP instructions as well as shadow call stack pushes and pops,
> depending on the configuration.
>
> Note that dynamic shadow call stack makes this slightly tricky, as it
> depends on in-kernel BTI as well. The resulting code will never contain
> both PAC and shadow call stack operations, even if shadow call stack
> support is not configured as dynamic.
>
> Signed-off-by: Ard Biesheuvel <ardb at kernel.org>
> ---
> arch/arm64/include/asm/assembler.h | 81 ++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
> index 3d1714a7eb6411ba..99d74c29ab3cbe05 100644
> --- a/arch/arm64/include/asm/assembler.h
> +++ b/arch/arm64/include/asm/assembler.h
> @@ -692,6 +692,85 @@ alternative_endif
> #endif
> .endm
>
> + /*
> + * protect_return_address - protect the return address value in
> + * register @reg, either by signing it using PAC and/or by storing it
> + * on the shadow call stack.
> + *
> + * The sequence below emits a shadow call stack push if the feature is
> + * enabled, and if in-kernel PAC is enabled as well, the instruction
> + * will be patched into a PACIA instruction involving the same register
> + * address (and SP as the modifier) if PAC is detected at runtime.
> + *
> + * If in-kernel BTI and dynamic shadow call stacks are also configured,
> + * it becomes a bit more tricky, because then, shadow call stacks will
> + * only be enabled on non-BTI hardware, regardless of the PAUTH state.
> + * In that case, we emit one of the following sequences.
> + *
> + * PAC+BTI enabled No PAC or BTI BTI without PAC PAC without BTI
> + *
> + * B 0f NOP B 0f NOP
> + * NOP SCS push SCS push NOP
> + * 0: PACIA NOP NOP PACIA
> + *
> + * Note that, due to the code patching occuring at function entry and
> + * exit, these macros must not be used in code that may execute before
> + * the boot CPU feature based code patching has completed.
I'm a bit worried about that, since there's some stuff like early ftrace that
might set up some state before this runs.
Is there no way we can have scs_patch() handle this the same as other PACIASP /
AUTIASP sequences? That would mean we do all SCS patching in one go, so there's
less risk of error, and we'd only require a single instruction rather than
three.
If we're happy doing this late, I think we could instead use a callback
alternative to align with the regular SCS patching logic -- default to
{PAC,AUT}IASP, and have the callback have the same checks as the SCS patching
to determine when to patch with LDR/STR.
Thanks,
Mark.
> + */
> + .macro protect_return_address, reg=x30
> +#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
> +#if defined(CONFIG_DYNAMIC_SCS) && defined(CONFIG_ARM64_BTI_KERNEL)
> +alternative_if ARM64_BTI
> + b .L0_\@
> +alternative_else_nop_endif
> +#endif
> +alternative_if_not ARM64_HAS_ADDRESS_AUTH
> +#endif
> +#ifdef CONFIG_SHADOW_CALL_STACK
> + str \reg, [x18], #8
> +#endif
> +#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
> +#if !defined(CONFIG_SHADOW_CALL_STACK) || \
> + (defined(CONFIG_DYNAMIC_SCS) && defined(CONFIG_ARM64_BTI_KERNEL))
> +.L0_\@: nop
> +#endif
> +alternative_else
> +#if defined(CONFIG_DYNAMIC_SCS) && defined(CONFIG_ARM64_BTI_KERNEL)
> + nop
> +#endif
> + .arch_extension pauth
> + pacia \reg, sp
> +alternative_endif
> +#endif
> + .endm
> +
> + /*
> + * restore_return_address - restore the return address value in
> + * register @reg, either by authenticating it using PAC and/or
> + * reloading it from the shadow call stack.
> + */
> + .macro restore_return_address, reg=x30
> +#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
> +alternative_if ARM64_HAS_ADDRESS_AUTH
> + .arch_extension pauth
> + autia \reg, sp
> +alternative_else_nop_endif
> +#if defined(CONFIG_DYNAMIC_SCS) && defined(CONFIG_ARM64_BTI_KERNEL)
> +alternative_if ARM64_BTI
> + b .L0_\@
> +alternative_else_nop_endif
> +#endif
> +alternative_if_not ARM64_HAS_ADDRESS_AUTH
> +#endif
> +#ifdef CONFIG_SHADOW_CALL_STACK
> + ldr \reg, [x18, #-8]!
> +#endif
> +#ifdef CONFIG_ARM64_PTR_AUTH_KERNEL
> +alternative_else_nop_endif
> +.L0_\@:
> +#endif
> + .endm
> +
> /*
> * frame_push - Push @regcount callee saved registers to the stack,
> * starting at x19, as well as x29/x30, and set x29 to
> @@ -699,6 +778,7 @@ alternative_endif
> * for locals.
> */
> .macro frame_push, regcount:req, extra
> + protect_return_address
> __frame st, \regcount, \extra
> .endm
>
> @@ -710,6 +790,7 @@ alternative_endif
> */
> .macro frame_pop
> __frame ld
> + restore_return_address
> .endm
>
> .macro __frame_regs, reg1, reg2, op, num
> --
> 2.35.1
>
More information about the linux-arm-kernel
mailing list