[PATCH 10/15] arm: Implement cortex-M return signing address codegen

Hi all,

this patch enables address return signature and verification based on
Armv8.1-M Pointer Authentication [1].

To sign the return address, we use the PAC R12, LR, SP instruction
upon function entry.  This is signing LR using SP and storing the
result in R12.  R12 will be pushed into the stack.

During function epilogue R12 will be popped and AUT R12, LR, SP will
be used to verify that the content of LR is still valid before return.

Here an example of PAC instrumented function prologue and epilogue:

void foo (void);

int main()
{
  foo ();
  return 0;
}

Compiled with '-march=armv8.1-m.main -mbranch-protection=pac-ret
-mthumb' translates into:

main:
	pac	ip, lr, sp
	push	{r3, r7, ip, lr}
	add	r7, sp, #0
	bl	foo
	movs	r3, #0
	mov	r0, r3
	pop	{r3, r7, ip, lr}
	aut	ip, lr, sp
	bx	lr

The patch also takes care of generating a PACBTI instruction in place
of the sequence BTI+PAC when Branch Target Identification is enabled
contextually.

Ex. the previous example compiled with '-march=armv8.1-m.main
-mbranch-protection=pac-ret+bti -mthumb' translates into:

main:
	pacbti	ip, lr, sp
	push	{r3, r7, ip, lr}
	add	r7, sp, #0
	bl	foo
	movs	r3, #0
	mov	r0, r3
	pop	{r3, r7, ip, lr}
	aut	ip, lr, sp
	bx	lr

As part of previous upstream suggestions a test for varargs has been
added and '-mtpcs-frame' is deemed being incompatible with this return
signing address feature being introduced.

[1] <https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension>

gcc/
	* config/arm/arm.h (arm_arch8m_main): Declare it.
	* config/arm/arm-protos.h (arm_current_function_pac_enabled_p):
	Declare it.
	* config/arm/arm.cc (arm_arch8m_main): Define it.
	(arm_option_reconfigure_globals): Set arm_arch8m_main.
	(arm_compute_frame_layout, arm_expand_prologue)
	(thumb2_expand_return, arm_expand_epilogue)
	(arm_conditional_register_usage): Update for pac codegen.
	(arm_current_function_pac_enabled_p): New function.
	(aarch_bti_enabled) New function.
	(use_return_insn): Return zero when pac is enabled.
	* config/arm/arm.md (pac_ip_lr_sp, pacbti_ip_lr_sp, aut_ip_lr_sp):
	Add new patterns.
	* config/arm/unspecs.md (UNSPEC_PAC_NOP)
	(VUNSPEC_PACBTI_NOP, VUNSPEC_AUT_NOP): Add unspecs.

gcc/testsuite/

	* gcc.target/arm/pac.h : New file.
	* gcc.target/arm/pac-1.c : New test case.
	* gcc.target/arm/pac-2.c : Likewise.
	* gcc.target/arm/pac-3.c : Likewise.
	* gcc.target/arm/pac-4.c : Likewise.
	* gcc.target/arm/pac-5.c : Likewise.
	* gcc.target/arm/pac-6.c : Likewise.
	* gcc.target/arm/pac-7.c : Likewise.
	* gcc.target/arm/pac-8.c : Likewise.
	* gcc.target/arm/pac-9.c : Likewise.
	* gcc.target/arm/pac-10.c : Likewise.
	* gcc.target/arm/pac-11.c : Likewise.
This commit is contained in:
Andrea Corallo 2022-01-20 15:36:23 +01:00
parent cea85c6697
commit 651460b452
17 changed files with 305 additions and 9 deletions

View File

@ -379,6 +379,7 @@ extern int vfp3_const_double_for_bits (rtx);
extern void arm_emit_coreregs_64bit_shift (enum rtx_code, rtx, rtx, rtx, rtx,
rtx);
extern bool arm_fusion_enabled_p (tune_params::fuse_ops);
extern bool arm_current_function_pac_enabled_p (void);
extern bool arm_valid_symbolic_address_p (rtx);
extern bool arm_validize_comparison (rtx *, rtx *, rtx *);
extern bool arm_expand_vector_compare (rtx, rtx_code, rtx, rtx, bool);

View File

@ -923,6 +923,11 @@ int arm_arch8_3 = 0;
/* Nonzero if this chip supports the ARM Architecture 8.4 extensions. */
int arm_arch8_4 = 0;
/* Nonzero if this chip supports the ARM Architecture 8-M Mainline
extensions. */
int arm_arch8m_main = 0;
/* Nonzero if this chip supports the ARM Architecture 8.1-M Mainline
extensions. */
int arm_arch8_1m_main = 0;
@ -3205,6 +3210,15 @@ arm_option_override_internal (struct gcc_options *opts,
arm_stack_protector_guard_offset = offs;
}
if (arm_current_function_pac_enabled_p ())
{
if (!arm_arch8m_main)
error ("This architecture does not support branch protection "
"instructions");
if (TARGET_TPCS_FRAME)
sorry ("Return address signing is not supported with %<-mtpcs-frame%>.");
}
#ifdef SUBTARGET_OVERRIDE_INTERNAL_OPTIONS
SUBTARGET_OVERRIDE_INTERNAL_OPTIONS;
#endif
@ -3851,6 +3865,7 @@ arm_option_reconfigure_globals (void)
arm_arch_arm_hwdiv = bitmap_bit_p (arm_active_target.isa, isa_bit_adiv);
arm_arch_crc = bitmap_bit_p (arm_active_target.isa, isa_bit_crc32);
arm_arch_cmse = bitmap_bit_p (arm_active_target.isa, isa_bit_cmse);
arm_arch8m_main = arm_arch7 && arm_arch_cmse;
arm_arch_lpae = bitmap_bit_p (arm_active_target.isa, isa_bit_lpae);
arm_arch_i8mm = bitmap_bit_p (arm_active_target.isa, isa_bit_i8mm);
arm_arch_bf16 = bitmap_bit_p (arm_active_target.isa, isa_bit_bf16);
@ -4350,6 +4365,12 @@ use_return_insn (int iscond, rtx sibling)
if (!reload_completed)
return 0;
/* Never use a return instruction when return address signing
mechanism is enabled as it requires more than one
instruction. */
if (arm_current_function_pac_enabled_p ())
return 0;
func_type = arm_current_func_type ();
/* Naked, volatile and stack alignment functions need special
@ -21245,6 +21266,9 @@ arm_compute_save_core_reg_mask (void)
save_reg_mask |= arm_compute_save_reg0_reg12_mask ();
if (arm_current_function_pac_enabled_p ())
save_reg_mask |= 1 << IP_REGNUM;
/* Decide if we need to save the link register.
Interrupt routines have their own banked link register,
so they never need to save it.
@ -23536,12 +23560,13 @@ arm_expand_prologue (void)
/* The static chain register is the same as the IP register. If it is
clobbered when creating the frame, we need to save and restore it. */
clobber_ip = IS_NESTED (func_type)
&& ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
|| ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
|| flag_stack_clash_protection)
&& !df_regs_ever_live_p (LR_REGNUM)
&& arm_r3_live_at_start_p ()));
clobber_ip = (IS_NESTED (func_type)
&& (((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
|| ((flag_stack_check == STATIC_BUILTIN_STACK_CHECK
|| flag_stack_clash_protection)
&& !df_regs_ever_live_p (LR_REGNUM)
&& arm_r3_live_at_start_p ()))
|| arm_current_function_pac_enabled_p ()));
/* Find somewhere to store IP whilst the frame is being created.
We try the following places in order:
@ -23566,7 +23591,6 @@ arm_expand_prologue (void)
{
rtx addr, dwarf;
gcc_assert(arm_compute_static_chain_stack_bytes() == 4);
saved_regs += 4;
addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
@ -23617,6 +23641,17 @@ arm_expand_prologue (void)
}
}
if (arm_current_function_pac_enabled_p ())
{
/* If IP was clobbered we only emit a PAC instruction as the BTI
one will be added before the push of the clobbered IP (if
necessary) by the bti pass. */
if (aarch_bti_enabled () && !clobber_ip)
emit_insn (gen_pacbti_nop ());
else
emit_insn (gen_pac_nop ());
}
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
{
if (IS_INTERRUPT (func_type))
@ -27428,7 +27463,14 @@ thumb2_expand_return (bool simple_return)
to assert it for now to ensure that future code changes do not silently
change this behavior. */
gcc_assert (!IS_CMSE_ENTRY (arm_current_func_type ()));
if (num_regs == 1)
if (arm_current_function_pac_enabled_p ())
{
gcc_assert (!(saved_regs_mask & (1 << PC_REGNUM)));
arm_emit_multi_reg_pop (saved_regs_mask);
emit_insn (gen_aut_nop ());
emit_jump_insn (simple_return_rtx);
}
else if (num_regs == 1)
{
rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2));
rtx reg = gen_rtx_REG (SImode, PC_REGNUM);
@ -27852,7 +27894,8 @@ arm_expand_epilogue (bool really_return)
&& really_return
&& crtl->args.pretend_args_size == 0
&& saved_regs_mask & (1 << LR_REGNUM)
&& !crtl->calls_eh_return)
&& !crtl->calls_eh_return
&& !arm_current_function_pac_enabled_p ())
{
saved_regs_mask &= ~(1 << LR_REGNUM);
saved_regs_mask |= (1 << PC_REGNUM);
@ -27966,6 +28009,9 @@ arm_expand_epilogue (bool really_return)
}
}
if (arm_current_function_pac_enabled_p ())
emit_insn (gen_aut_nop ());
if (!really_return)
return;
@ -33067,6 +33113,22 @@ arm_fusion_enabled_p (tune_params::fuse_ops op)
return current_tune->fusible_ops & op;
}
/* Return TRUE if return address signing mechanism is enabled. */
bool
arm_current_function_pac_enabled_p (void)
{
return (aarch_ra_sign_scope == AARCH_FUNCTION_ALL
|| (aarch_ra_sign_scope == AARCH_FUNCTION_NON_LEAF
&& !crtl->is_leaf));
}
/* Return TRUE if Branch Target Identification Mechanism is enabled. */
static bool
aarch_bti_enabled ()
{
return false;
}
/* Implement TARGET_SCHED_CAN_SPECULATE_INSN. Return true if INSN can be
scheduled for speculative execution. Reject the long-running division
and square-root instructions. */

View File

@ -506,6 +506,10 @@ extern int arm_arch8_3;
/* Nonzero if this chip supports the ARM Architecture 8.4 extensions. */
extern int arm_arch8_4;
/* Nonzero if this chip supports the ARM Architecture 8-M Mainline
extensions. */
extern int arm_arch8m_main;
/* Nonzero if this chip supports the ARM Architecture 8.1-M Mainline
extensions. */
extern int arm_arch8_1m_main;

View File

@ -12986,6 +12986,29 @@
(set_attr "length" "8")]
)
(define_insn "pac_nop"
[(set (reg:SI IP_REGNUM)
(unspec:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
UNSPEC_PAC_NOP))]
"arm_arch8m_main"
"pac\t%|ip, %|lr, %|sp"
[(set_attr "conds" "unconditional")])
(define_insn "pacbti_nop"
[(set (reg:SI IP_REGNUM)
(unspec_volatile:SI [(reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
VUNSPEC_PACBTI_NOP))]
"arm_arch8m_main"
"pacbti\t%|ip, %|lr, %|sp"
[(set_attr "conds" "unconditional")])
(define_insn "aut_nop"
[(unspec_volatile:SI [(reg:SI IP_REGNUM) (reg:SI SP_REGNUM) (reg:SI LR_REGNUM)]
VUNSPEC_AUT_NOP)]
"arm_arch8m_main"
"aut\t%|ip, %|lr, %|sp"
[(set_attr "conds" "unconditional")])
;; Vector bits common to IWMMXT, Neon and MVE
(include "vec-common.md")
;; Load the Intel Wireless Multimedia Extension patterns

View File

@ -159,6 +159,7 @@
UNSPEC_VCDE ; Custom Datapath Extension instruction.
UNSPEC_VCDEA ; Custom Datapath Extension instruction.
UNSPEC_DLS ; Used for DLS (Do Loop Start), Armv8.1-M Mainline instruction
UNSPEC_PAC_NOP ; Represents PAC signing LR
])
@ -254,6 +255,8 @@
; instruction.
VUNSPEC_VLLDM ; Represent the lazy load multiple with vlldm
; instruction.
VUNSPEC_PACBTI_NOP ; Represents PAC signing LR + valid landing pad
VUNSPEC_AUT_NOP ; Represents PAC verifying LR
])
;; Enumerators for NEON unspecs.

View File

@ -0,0 +1,11 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include "pac.h"
/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,10 @@
/* Testing return address signing. */
/* { dg-do compile } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include "pac.h"
/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,10 @@
/* Testing return address signing. */
/* { dg-do compile } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=bti+pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O2" } */
#include "pac.h"
/* { dg-final { scan-assembler-times "pacbti\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,11 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include "pac.h"
/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,11 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=bti+pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O2" } */
#include "pac.h"
/* { dg-final { scan-assembler-times "pacbti\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,10 @@
/* Testing return address signing. */
/* { dg-do compile } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mthumb -mfloat-abi=hard --save-temps -O2" } */
#include "pac.h"
/* { dg-final { scan-assembler-not "\tbti\t" } } */
/* { dg-final { scan-assembler-not "\tpac\t" } } */
/* { dg-final { scan-assembler-not "\tpacbti\t" } } */

View File

@ -0,0 +1,28 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include <stdlib.h>
int
__attribute__((noinline))
foo1 (int a, int b)
{
int square (int z) { return z * z; }
return square (a) + square (b);
}
int
main (void)
{
if (foo1 (1, 2) != 5)
abort ();
return 0;
}
/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,18 @@
/* Check that GCC does .save and .cfi_offset directives with RA_AUTH_CODE pseudo hard-register. */
/* { dg-do compile } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-options "-march=armv8.1-m.main+fp -mbranch-protection=pac-ret+leaf -mthumb --save-temps -O0 -g" } */
int i;
void foo (int);
int bar()
{
foo (i);
return 0;
}
/* { dg-final { scan-assembler "pac\tip, lr, sp" } } */
/* { dg-final { scan-assembler "aut\tip, lr, sp" } } */
/* { dg-final { scan-assembler-not "bti" } } */

View File

@ -0,0 +1,32 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include <stdlib.h>
int
__attribute__((noinline))
foo1 (int a, int b)
{
int x = 4;
int foo2 (int a, int b)
{
return a + b + x;
}
return foo2 (a, b);
}
int
main (void)
{
if (foo1 (1, 2) != 7)
abort ();
return 0;
}
/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 3 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 3 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,34 @@
/* Testing return address signing. */
/* { dg-do run } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-require-effective-target arm_pacbti_hw } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include <stdarg.h>
#include <stdlib.h>
int acc (int n, ...)
{
int sum = 0;
va_list ptr;
va_start (ptr, n);
for (int i = 0; i < n; i++)
sum += va_arg (ptr, int);
va_end (ptr);
return sum;
}
int main()
{
if (acc (3, 1, 2, 3) != 6)
abort ();
return 0;
}
/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,11 @@
/* Testing return address signing. */
/* { dg-do compile } */
/* { dg-require-effective-target mbranch_protection_ok } */
/* { dg-options "-march=armv8.1-m.main+pacbti+fp -mbranch-protection=pac-ret+leaf -mthumb -mfloat-abi=hard --save-temps -O0" } */
#include "pac.h"
/* { dg-final { scan-assembler-times "pac\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-times "aut\tip, lr, sp" 2 } } */
/* { dg-final { scan-assembler-not "\tbti" } } */

View File

@ -0,0 +1,17 @@
#include <stdlib.h>
int
__attribute__((noinline))
foo1 (int a, int b)
{
return a + b;
}
int
main (void)
{
if (foo1 (1, 2) != 3)
abort ();
return 0;
}