diff mbox series

[2/4] arm64: assembler: Add macros for return address protection

Message ID 20221129141803.1746898-3-ardb@kernel.org (mailing list archive)
State New, archived
Headers show
Series arm64: Add return address protection to asm code | expand

Commit Message

Ard Biesheuvel Nov. 29, 2022, 2:18 p.m. UTC
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@kernel.org>
---
 arch/arm64/include/asm/assembler.h | 81 ++++++++++++++++++++
 1 file changed, 81 insertions(+)

Comments

Mark Rutland Nov. 30, 2022, 2:15 p.m. UTC | #1
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@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
>
Ard Biesheuvel Nov. 30, 2022, 2:33 p.m. UTC | #2
On Wed, 30 Nov 2022 at 15:16, Mark Rutland <mark.rutland@arm.com> wrote:
>
> 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@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.
>

Yes, we can. I actually have the patches ready to go to emit the CFI
directives etc.

The only problem is that it won't apply to PACIA instructions with
different registers, so we won't be able to use it for signing x9 in
the ftrace code, for instance. Also, it is a bit fiddly to get the CFI
directives right in general (so that unwinding will work through
functions that manipulate the stack) and I am reluctant to emit CFI
metadata with only PAC annotations which will confuse the unwinder
(either at runtime if we end up using it for any reason, or maybe just
GDB when people use it on a vmlinux that has dynamic SCS enabled)


> 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.
>

Agreed. Happy to code this up as a callback alternative but I didn't
want to burn too much time on that before giving the series some
review exposure.

> > +      */
> > +     .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
> >
diff mbox series

Patch

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.
+	 */
+	.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