This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[1/5][AArch64] Return address protection on AArch64
- From: Jiong Wang <jiong dot wang at foss dot arm dot com>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>
- Cc: James Greenhalgh <james dot greenhalgh at arm dot com>, "Richard Earnshaw (lists)" <Richard dot Earnshaw at arm dot com>
- Date: Fri, 6 Jan 2017 11:47:07 +0000
- Subject: [1/5][AArch64] Return address protection on AArch64
- Authentication-results: sourceware.org; auth=none
- References: <c9da17a6-c3de-4466-c023-4e4ddbe38efb@foss.arm.com> <4cf21d03-0a88-c6fa-df37-59ec4edf1d89@foss.arm.com> <ac547390-abfc-3d6a-f10b-dbb9e4bad5b2@foss.arm.com> <ae7c1f2a-06e4-55d1-d3be-e43ae7ec76df@foss.arm.com> <6f8e65e0-643d-d0b0-26ad-4a20c3daf421@foss.arm.com>
On 11/11/16 18:22, Jiong Wang wrote:
As described in the cover letter, this patch implements return address signing
for AArch64, it's controlled by the new option:
-msign-return-address=[none | non-leaf | all]
"none" means don't do return address signing at all on any function. "non-leaf"
means only sign non-leaf function. "all" means sign all functions. Return
address signing is currently disabled on ILP32. I haven't tested it.
The instructions added in the architecture are of 2 kinds.
* In the NOP instruction space, which allows binaries to run without any traps
on older versions of the architecture. This doesn't give any additional
protection on older hardware but allows for the same binary to be used on
earlier versions of the architecture and newer versions of the architecture.
* New instructions that are only valid for v8.3 and will trap if used on earlier
versions of the architecture.
At default, once return address signing is enabled, it will only generates NOP
instruction.
While if -march=armv8.3-a specified, GCC will try to use the most efficient
pointer authentication instruction as it can.
The architecture has 2 user invisible system keys for signing and creating
signed addresses as part of these instructions. For some use case, the user
might want to use difference key for different functions. The new option
"-msign-return-address-key=key_name" let GCC select the key used for return
address signing. Permissible values are "a_key" for A key and "b_key" for B
key, and this option are supported by function target attribute and LTO will
hopefully just work.
gcc/
2016-11-09 Jiong Wang<jiong.wang@arm.com>
* config/aarch64/aarch64-opts.h (aarch64_pauth_key_index): New enum.
(aarch64_function_type): New enum.
* config/aarch64/aarch64-protos.h (aarch64_output_sign_auth_reg): New
declaration.
* config/aarch64/aarch64.c (aarch64_expand_prologue): Sign return
address before it's pushed onto stack.
(aarch64_expand_epilogue): Authenticate return address fetched from
stack.
(aarch64_output_sign_auth_reg): New function.
(aarch64_override_options): Sanity check for ILP32 and ISA level.
(aarch64_attributes): New function attributes for "sign-return-address",
"pauth-key".
* config/aarch64/aarch64.md (UNSPEC_AUTH_REG, UNSPEC_AUTH_REG1716,
UNSPEC_SIGN_REG, UNSPEC_SIGN_REG1716, UNSPEC_STRIP_REG_SIGN,
UNSPEC_STRIP_X30_SIGN): New unspecs.
("*do_return"): Generate combined instructions according to key index.
("sign_reg", "sign_reg1716", "auth_reg", "auth_reg1716",
"strip_reg_sign", "strip_lr_sign"): New.
* config/aarch64/aarch64.opt (msign-return-address, mpauth-key): New.
* config/aarch64/predicates.md (aarch64_const0_const1): New predicate.
* doc/extend.texi (AArch64 Function Attributes): Documents
"sign-return-address=", "pauth-key".
* doc/invoke.texi (AArch64 Options): Documents "-msign-return-address=",
"-pauth-key".
gcc/testsuite/
2016-11-09 Jiong Wang<jiong.wang@arm.com>
* gcc.target/aarch64/return_address_sign_1.c: New testcase.
* gcc.target/aarch64/return_address_sign_scope_1.c: New testcase.
Update the patchset according to new DWARF proposal described at
https://gcc.gnu.org/ml/gcc-patches/2016-11/msg03010.html
While A key support for return address signing using DW_CFA_GNU_window_save only
needs simple modifications on code and associated DWARF generation, B key
support is complexer, it needs multiple CIE support in GCC and Binutils, so
currently we fall back to DWARF value expression which fully works although
requires longer encodings. Value expression also requires a few changes on
AArch64 prologue and epilogue hooks that code review will not be easy.
Therefore I have removed all B key support code in the initial support patch set,
and will organize them into a seperate follow up patchset so that we can do A key
code review first.
This patch is an update on the return address signing code generation.
gcc/
2017-01-06 Jiong Wang <jiong.wang@arm.com>
* config/aarch64/aarch64-opts.h (aarch64_function_type): New enum.
* config/aarch64/aarch64-protos.h
(aarch64_return_address_signing_enabled): New declaration.
* config/aarch64/aarch64.c (aarch64_return_address_signing_enabled):
New function.
(aarch64_expand_prologue): Sign return address before it's pushed onto
stack.
(aarch64_expand_epilogue): Authenticate return address fetched from
stack.
(aarch64_override_options): Sanity check for ILP32 and ISA level.
(aarch64_attributes): New function attributes for "sign-return-address".
* config/aarch64/aarch64.md (UNSPEC_AUTI1716, UNSPEC_AUTISP,
UNSPEC_PACI1716, UNSPEC_PACISP, UNSPEC_XPACLRI): New unspecs.
("*do_return"): Generate combined instructions according to key index.
("<pauth_mnem_prefix>sp", "<pauth_mnem_prefix1716", "xpaclri"): New.
* config/aarch64/iterators.md (PAUTH_LR_SP, PAUTH_17_16): New integer
iterators.
(pauth_mnem_prefix, pauth_hint_num_a): New integer attributes.
* config/aarch64/aarch64.opt (msign-return-address=): New.
* doc/extend.texi (AArch64 Function Attributes): Documents
"sign-return-address=".
* doc/invoke.texi (AArch64 Options): Documents "-msign-return-address=".
gcc/testsuite/
2017-01-06 Jiong Wang <jiong.wang@arm.com>
* gcc.target/aarch64/return_address_sign_1.c: New testcase.
* gcc.target/aarch64/return_address_sign_scope_1.c: New testcase.
diff --git a/gcc/config/aarch64/aarch64-opts.h b/gcc/config/aarch64/aarch64-opts.h
index 9f37b9b432e5ed978032fa3356b138e2c7433760..ba5d052e9b694289574a68f156ca68b66ad6e2ee 100644
--- a/gcc/config/aarch64/aarch64-opts.h
+++ b/gcc/config/aarch64/aarch64-opts.h
@@ -71,4 +71,14 @@ enum aarch64_code_model {
AARCH64_CMODEL_LARGE
};
+/* Function types -msign-return-address should sign. */
+enum aarch64_function_type {
+ /* Don't sign any function. */
+ AARCH64_FUNCTION_NONE,
+ /* Non-leaf functions. */
+ AARCH64_FUNCTION_NON_LEAF,
+ /* All functions. */
+ AARCH64_FUNCTION_ALL
+};
+
#endif
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 29a3bd71151aa4fb7c6728f0fb52e2f3f233f41d..632dd4768d82c340ae4e9b4a93206743756c06e7 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -386,6 +386,7 @@ void aarch64_emit_call_insn (rtx);
void aarch64_register_pragmas (void);
void aarch64_relayout_simd_types (void);
void aarch64_reset_previous_fndecl (void);
+bool aarch64_return_address_signing_enabled (void);
void aarch64_save_restore_target_globals (tree);
/* Initialize builtins for SIMD intrinsics. */
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 9dd75b07498871db3e37b335dc8a3e1d66ec1d8b..002895a167ce0deb45a5c1726527651af18bb4df 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -3117,6 +3117,22 @@ aarch64_gen_load_pair (machine_mode mode, rtx reg1, rtx mem1, rtx reg2,
}
}
+/* Return TRUE if return address signing should be enabled for one function,
+ otherwise return FALSE. */
+
+bool
+aarch64_return_address_signing_enabled (void)
+{
+ /* This function should only be called after frame laid out. */
+ gcc_assert (cfun->machine->frame.laid_out);
+
+ /* If signing scope is AARCH64_FUNCTION_NON_LEAF, we only sign a leaf function
+ if it's LR is pushed onto stack. */
+ return (aarch64_ra_sign_scope == AARCH64_FUNCTION_ALL
+ || (aarch64_ra_sign_scope == AARCH64_FUNCTION_NON_LEAF
+ && cfun->machine->frame.reg_offset[LR_REGNUM] >= 0));
+}
+
/* Emit code to save the callee-saved registers from register number START
to LIMIT to the stack at the location starting at offset START_OFFSET,
skipping any write-back candidates if SKIP_WB is true. */
@@ -3535,6 +3551,10 @@ aarch64_expand_prologue (void)
unsigned reg2 = cfun->machine->frame.wb_candidate2;
rtx_insn *insn;
+ /* Sign return address for functions. */
+ if (aarch64_return_address_signing_enabled ())
+ emit_insn (gen_pacisp ());
+
if (flag_stack_usage_info)
current_function_static_stack_size = frame_size;
@@ -3670,6 +3690,16 @@ aarch64_expand_epilogue (bool for_sibcall)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* sibcall won't generate normally return, therefore we need to authenticate
+ at here. !TARGET_ARMV8_3 disallows GCC to use combined authentication
+ instruction which we prefer, so we need to generate a seperate
+ authentication. eh_return path can't do combined authentication, as it will
+ do extra stack adjustment which updates CFA to EH handler's CFA while we
+ want to use the CFA of the function which calls eh_return. */
+ if (aarch64_return_address_signing_enabled ()
+ && (for_sibcall || !TARGET_ARMV8_3 || crtl->calls_eh_return))
+ emit_insn (gen_autisp ());
+
/* Stack adjustment for exception handler. */
if (crtl->calls_eh_return)
{
@@ -8894,6 +8924,9 @@ aarch64_override_options (void)
error ("Assembler does not support -mabi=ilp32");
#endif
+ if (aarch64_ra_sign_scope != AARCH64_FUNCTION_NONE && TARGET_ILP32)
+ error ("Return address signing is only supported on LP64");
+
/* Make sure we properly set up the explicit options. */
if ((aarch64_cpu_string && valid_cpu)
|| (aarch64_tune_string && valid_tune))
@@ -9277,6 +9310,8 @@ static const struct aarch64_attribute_info aarch64_attributes[] =
{ "cpu", aarch64_attr_custom, false, aarch64_handle_attr_cpu, OPT_mcpu_ },
{ "tune", aarch64_attr_custom, false, aarch64_handle_attr_tune,
OPT_mtune_ },
+ { "sign-return-address", aarch64_attr_enum, false, NULL,
+ OPT_msign_return_address_ },
{ NULL, aarch64_attr_custom, false, NULL, OPT____ }
};
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index bde42317f1bfd91a9d38a4cfa94d4cedd5246003..9b6f99dee3cecc0f15f24ec2375d3641fe03892d 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -67,6 +67,8 @@
)
(define_c_enum "unspec" [
+ UNSPEC_AUTI1716
+ UNSPEC_AUTISP
UNSPEC_CASESI
UNSPEC_CRC32B
UNSPEC_CRC32CB
@@ -106,6 +108,8 @@
UNSPEC_LD4_LANE
UNSPEC_MB
UNSPEC_NOP
+ UNSPEC_PACI1716
+ UNSPEC_PACISP
UNSPEC_PRLG_STK
UNSPEC_RBIT
UNSPEC_SCVTF
@@ -135,6 +139,7 @@
UNSPEC_RSQRTE
UNSPEC_RSQRTS
UNSPEC_NZCV
+ UNSPEC_XPACLRI
])
(define_c_enum "unspecv" [
@@ -575,7 +580,14 @@
(define_insn "*do_return"
[(return)]
""
- "ret"
+ {
+ if (aarch64_return_address_signing_enabled ()
+ && TARGET_ARMV8_3
+ && !crtl->calls_eh_return)
+ return "retaa";
+
+ return "ret";
+ }
[(set_attr "type" "branch")]
)
@@ -5341,6 +5353,31 @@
[(set_attr "length" "0")]
)
+;; Pointer authentication patterns are available for all architectures. On
+;; pre-ARMv8.3-A architectures they are NOP. This let the user write portable
+;; software which can get pointer authentication on new hardware while still
+;; runs correctly on old hardware though no pointer authentication protection.
+
+(define_insn "<pauth_mnem_prefix>sp"
+ [(set (reg:DI R30_REGNUM)
+ (unspec:DI [(reg:DI R30_REGNUM) (reg:DI SP_REGNUM)] PAUTH_LR_SP))]
+ ""
+ "hint\t<pauth_hint_num_a> // <pauth_mnem_prefix>asp";
+)
+
+(define_insn "<pauth_mnem_prefix>1716"
+ [(set (reg:DI R17_REGNUM)
+ (unspec:DI [(reg:DI R17_REGNUM) (reg:DI R16_REGNUM)] PAUTH_17_16))]
+ ""
+ "hint\t<pauth_hint_num_a> // <pauth_mnem_prefix>a1716";
+)
+
+(define_insn "xpaclri"
+ [(set (reg:DI R30_REGNUM) (unspec:DI [(reg:DI R30_REGNUM)] UNSPEC_XPACLRI))]
+ ""
+ "hint\t7 // xpaclri"
+)
+
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt
index 56b920dfe5abda1349c441c2188e5ac3ca05ab5a..54368848bbb249949921a3018d927c4bd61b1fbd 100644
--- a/gcc/config/aarch64/aarch64.opt
+++ b/gcc/config/aarch64/aarch64.opt
@@ -149,6 +149,23 @@ mpc-relative-literal-loads
Target Report Save Var(pcrelative_literal_loads) Init(2) Save
PC relative literal loads.
+msign-return-address=
+Target RejectNegative Report Joined Enum(aarch64_ra_sign_scope_t) Var(aarch64_ra_sign_scope) Init(AARCH64_FUNCTION_NONE) Save
+Select return address signing scope.
+
+Enum
+Name(aarch64_ra_sign_scope_t) Type(enum aarch64_function_type)
+Supported AArch64 return address signing scope (for use with -msign-return-address= option):
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(none) Value(AARCH64_FUNCTION_NONE)
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(non-leaf) Value(AARCH64_FUNCTION_NON_LEAF)
+
+EnumValue
+Enum(aarch64_ra_sign_scope_t) String(all) Value(AARCH64_FUNCTION_ALL)
+
mlow-precision-recip-sqrt
Common Var(flag_mrecip_low_precision_sqrt) Optimization
Enable the reciprocal square root approximation. Enabling this reduces
diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md
index e2377c1cb2161c52ccadec3804294c494819cbb8..c59d31e5053a28fa31259341e82b6696f6ac8781 100644
--- a/gcc/config/aarch64/iterators.md
+++ b/gcc/config/aarch64/iterators.md
@@ -1032,6 +1032,10 @@
(define_int_iterator FMAXMIN_UNS [UNSPEC_FMAX UNSPEC_FMIN
UNSPEC_FMAXNM UNSPEC_FMINNM])
+(define_int_iterator PAUTH_LR_SP [UNSPEC_PACISP UNSPEC_AUTISP])
+
+(define_int_iterator PAUTH_17_16 [UNSPEC_PACI1716 UNSPEC_AUTI1716])
+
(define_int_iterator VQDMULH [UNSPEC_SQDMULH UNSPEC_SQRDMULH])
(define_int_iterator USSUQADD [UNSPEC_SUQADD UNSPEC_USQADD])
@@ -1218,6 +1222,18 @@
(UNSPEC_FCVTZS "fcvtzs")
(UNSPEC_FCVTZU "fcvtzu")])
+;; Pointer authentication mnemonic prefix.
+(define_int_attr pauth_mnem_prefix [(UNSPEC_PACISP "paci")
+ (UNSPEC_AUTISP "auti")
+ (UNSPEC_PACI1716 "paci")
+ (UNSPEC_AUTI1716 "auti")])
+
+;; Pointer authentication HINT number for NOP space instructions using A Key.
+(define_int_attr pauth_hint_num_a [(UNSPEC_PACISP "25")
+ (UNSPEC_AUTISP "29")
+ (UNSPEC_PACI1716 "8")
+ (UNSPEC_AUTI1716 "12")])
+
(define_int_attr perm_insn [(UNSPEC_ZIP1 "zip") (UNSPEC_ZIP2 "zip")
(UNSPEC_TRN1 "trn") (UNSPEC_TRN2 "trn")
(UNSPEC_UZP1 "uzp") (UNSPEC_UZP2 "uzp")])
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 30bdcf07ad8b1bd086bbb87acb36fb0333944087..47250ebb149bbde021ef64eba8e96e35c2623284 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3513,6 +3513,12 @@ Specifies the core for which to tune the performance of this function and also
whose architectural features to use. The behavior and valid arguments are the
same as for the @option{-mcpu=} command-line option.
+@item sign-return-address
+@cindex @code{sign-return-address} function attribute, AArch64
+Select the function scope we want to do return address signing on. The behavior
+and permissible arguments are the same as for the command-line option
+@option{-msign-return-address=}. The default value is @code{none}
+
@end table
The above target attributes can be specified as follows:
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index aa0ac8b41128483be53645b91bdcead096582e23..2792dbd0a2d0ed9e3f46237685f8ae65a5d8bcf5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -14044,6 +14044,13 @@ accessed using a single instruction and emitted after each function. This
limits the maximum size of functions to 1MB. This is enabled by default for
@option{-mcmodel=tiny}.
+@item -msign-return-address=@var{scope}
+@opindex msign-return-address
+Select the function scope we want to do return address signing on. Permissible
+values are @samp{none}, @samp{none-leaf} and @samp{all}. @samp{none} means
+return address signing is disabled. @samp{non-leaf} enables it for non-leaf
+functions. @samp{all} for all functions and is the default value.
+
@end table
@subsubsection @option{-march} and @option{-mcpu} Feature Modifiers
diff --git a/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c b/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c
new file mode 100644
index 0000000000000000000000000000000000000000..9a0332abd41ca6ae994e6ed72771fca8ee532cd0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/return_address_sign_1.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msign-return-address=all" } */
+
+int foo (int);
+int bar (int, int);
+
+/* sibcall only. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func1 (int a, int b)
+{
+ /* paciasp */
+ return foo (a + b);
+ /* autiasp */
+}
+
+/* non-leaf function with sibcall. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func2 (int a, int b)
+{
+ /* paciasp */
+ if (a < b)
+ return b;
+
+ a = foo (b);
+
+ return foo (a);
+ /* autiasp */
+}
+
+/* non-leaf function, legacy arch. */
+int __attribute__ ((target ("arch=armv8.2-a")))
+func3 (int a, int b, int c)
+{
+ /* paciasp */
+ return a + foo (b) + c;
+ /* autiasp */
+}
+
+/* non-leaf function. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func4 (int a, int b, int c)
+{
+ /* paciasp */
+ return a + foo (b) + c;
+ /* retab */
+}
+
+int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=none")))
+func4_disable (int a, int b, int c, int d)
+{
+ return c + bar (a, b) + d;
+}
+
+/* { dg-final { scan-assembler-times "autiasp" 3 } } */
+/* { dg-final { scan-assembler-times "paciasp" 4 } } */
+/* { dg-final { scan-assembler-times "retaa" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/return_address_sign_scope_1.c b/gcc/testsuite/gcc.target/aarch64/return_address_sign_scope_1.c
new file mode 100644
index 0000000000000000000000000000000000000000..6876e26a6a38eb670f8676f8f06dd74dfc18f41d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/return_address_sign_scope_1.c
@@ -0,0 +1,55 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -msign-return-address=non-leaf" } */
+
+int foo (int);
+int bar (int, int);
+
+/* sibcall only. */
+int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=non-leaf")))
+func1 (int a, int b)
+{
+ /* No authentication. */
+ return foo (a + b);
+}
+
+/* non-leaf function with sibcall. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func2 (int a, int b)
+{
+ /* paciasp */
+ if (a < b)
+ return b;
+
+ a = foo (b);
+
+ return foo (a);
+ /* autiasp */
+}
+
+/* non-leaf function, legacy arch. */
+int __attribute__ ((target ("arch=armv8.2-a")))
+func3 (int a, int b, int c)
+{
+ /* pacibsp */
+ return a + foo (b) + c;
+ /* autibsp */
+}
+
+/* non-leaf function. */
+int __attribute__ ((target ("arch=armv8.3-a")))
+func4 (int a, int b, int c)
+{
+ /* pacibsp */
+ return a + foo (b) + c;
+ /* retab */
+}
+
+int __attribute__ ((target ("arch=armv8.3-a, sign-return-address=none")))
+func4_disable (int a, int b, int c, int d)
+{
+ return c + bar (a, b) + d;
+}
+
+/* { dg-final { scan-assembler-times "paciasp" 3 } } */
+/* { dg-final { scan-assembler-times "autiasp" 2 } } */
+/* { dg-final { scan-assembler-times "retaa" 1 } } */