Instructions following RET or BR are not necessarily executed. In order to avoid speculation past RET and BR we can simply append a speculation barrier.
Since these speculation barriers will not be architecturally executed, they are not expected to add a high performance penalty. The speculation barrier is to be SB when targeting architectures which have this enabled, and DSB SY + ISB otherwise. We add tests for each of the cases where such an instruction was seen. This is implemented by modifying each machine description pattern that emits either a RET or a BR instruction. We choose not to use something like `TARGET_ASM_FUNCTION_EPILOGUE` since it does not affect the `indirect_jump`, `jump`, `sibcall_insn` and `sibcall_value_insn` patterns and we find it preferable to implement the functionality in the same way for every pattern. There is one particular case which is slightly tricky. The implementation of TARGET_ASM_TRAMPOLINE_TEMPLATE uses a BR which needs to be mitigated against. The trampoline template is used *once* per compilation unit, and the TRAMPOLINE_SIZE is exposed to the user via the builtin macro __LIBGCC_TRAMPOLINE_SIZE__. In the future we may implement function specific attributes to turn on and off hardening on a per-function basis. The fixed nature of the trampoline described above implies it will be safer to ensure this speculation barrier is always used. Testing: Bootstrap and regtest done on aarch64-none-linux Used a temporary hack(1) to use these options on every test in the testsuite and a script to check that the output never emitted an unmitigated RET or BR. 1) Temporary hack was a change to the testsuite to always use `-save-temps` and run a script on the assembly output of those compilations which produced one to ensure every RET or BR is immediately followed by a speculation barrier. gcc/ChangeLog: 2020-06-08 Matthew Malcomson <matthew.malcom...@arm.com> * config/aarch64/aarch64-protos.h (aarch64_sls_barrier): New. * config/aarch64/aarch64.c (aarch64_output_casesi): Emit speculation barrier after BR instruction if needs be. (aarch64_sls_barrier): New. (aarch64_asm_trampoline_template): Add needed barriers. * config/aarch64/aarch64.h (AARCH64_ISA_SB): New. (TARGET_SB): New. (TRAMPOLINE_SIZE): Account for barrier. * config/aarch64/aarch64.md (indirect_jump, *casesi_dispatch, *do_return, simple_return, *sibcall_insn, *sibcall_value_insn): Emit barrier if needs be, also account for possible barrier in "length" attribute. * config/aarch64/aarch64.opt (-mharden-sls-retbr): Introduce new option. gcc/testsuite/ChangeLog: 2020-06-08 Matthew Malcomson <matthew.malcom...@arm.com> * gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c: New test. * gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c: New test. * gcc.target/aarch64/sls-mitigation/sls-mitigation.exp: New file. * lib/target-supports.exp (check_effective_target_aarch64_asm_sb_ok): New proc. ############### Attachment also inlined for ease of reply ############### diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index 8ca67d7e69edaf73c84f079e7e1c483009ad10c0..d2eb739bc89ecd9d0212416b8dc3ee4ba236a271 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -780,6 +780,7 @@ extern const atomic_ool_names aarch64_ool_ldeor_names; tree aarch64_resolve_overloaded_builtin_general (location_t, tree, void *); +const char * aarch64_sls_barrier (int); extern bool aarch64_harden_sls_retbr_p (void); extern bool aarch64_harden_sls_blr_p (void); diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h index 24767c747bab0d711627c5c646937c42f210d70b..5da3d94e335fc315e1d90e6a674f2f09cf1a4529 100644 --- a/gcc/config/aarch64/aarch64.h +++ b/gcc/config/aarch64/aarch64.h @@ -281,6 +281,7 @@ extern unsigned aarch64_architecture_version; #define AARCH64_ISA_F32MM (aarch64_isa_flags & AARCH64_FL_F32MM) #define AARCH64_ISA_F64MM (aarch64_isa_flags & AARCH64_FL_F64MM) #define AARCH64_ISA_BF16 (aarch64_isa_flags & AARCH64_FL_BF16) +#define AARCH64_ISA_SB (aarch64_isa_flags & AARCH64_FL_SB) /* Crypto is an optional extension to AdvSIMD. */ #define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO) @@ -378,6 +379,9 @@ extern unsigned aarch64_architecture_version; #define TARGET_FIX_ERR_A53_835769_DEFAULT 1 #endif +/* SB instruction is enabled through +sb. */ +#define TARGET_SB (AARCH64_ISA_SB) + /* Apply the workaround for Cortex-A53 erratum 835769. */ #define TARGET_FIX_ERR_A53_835769 \ ((aarch64_fix_a53_err835769 == 2) \ @@ -1058,8 +1062,11 @@ typedef struct #define RETURN_ADDR_RTX aarch64_return_addr -/* BTI c + 3 insns + 2 pointer-sized entries. */ -#define TRAMPOLINE_SIZE (TARGET_ILP32 ? 24 : 32) +/* BTI c + 3 insns + + sls barrier of DSB + ISB. + + 2 pointer-sized entries. */ +#define TRAMPOLINE_SIZE (24 \ + + (TARGET_ILP32 ? 8 : 16)) /* Trampolines contain dwords, so must be dword aligned. */ #define TRAMPOLINE_ALIGNMENT 64 diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 775f49991e5f599a843d3ef490b8cd044acfe78f..9356937fe266c68196392a1589b3cf96607de104 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -10822,8 +10822,8 @@ aarch64_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) static void aarch64_asm_trampoline_template (FILE *f) { - int offset1 = 16; - int offset2 = 20; + int offset1 = 24; + int offset2 = 28; if (aarch64_bti_enabled ()) { @@ -10846,6 +10846,17 @@ aarch64_asm_trampoline_template (FILE *f) } asm_fprintf (f, "\tbr\t%s\n", reg_names [IP1_REGNUM]); + /* We always emit a speculation barrier. + This is because the same trampoline template is used for every nested + function. Since nested functions are not particularly common or + performant we don't worry too much about the extra instructions to copy + around. + This is not yet a problem, since we have not yet implemented function + specific attributes to choose between hardening against straight line + speculation or not, but such function specific attributes are likely to + happen in the future. */ + output_asm_insn ("dsb\tsy\n\tisb", NULL); + /* The trampoline needs an extra padding instruction. In case if BTI is enabled the padding instruction is replaced by the BTI instruction at the beginning. */ @@ -10860,7 +10871,7 @@ static void aarch64_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) { rtx fnaddr, mem, a_tramp; - const int tramp_code_sz = 16; + const int tramp_code_sz = 24; /* Don't need to copy the trailing D-words, we fill those in below. */ emit_block_move (m_tramp, assemble_trampoline_template (), @@ -11054,6 +11065,7 @@ aarch64_output_casesi (rtx *operands) output_asm_insn (buf, operands); output_asm_insn (patterns[index][1], operands); output_asm_insn ("br\t%3", operands); + output_asm_insn (aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()), operands); assemble_label (asm_out_file, label); return ""; } @@ -22895,6 +22907,22 @@ aarch64_file_end_indicate_exec_stack () #undef GNU_PROPERTY_AARCH64_FEATURE_1_BTI #undef GNU_PROPERTY_AARCH64_FEATURE_1_AND +/* Helper function for straight line speculation. + Return what barrier should be emitted for straight line speculation + mitigation. + When not mitigating against straight line speculation this function returns + an empty string. + When mitigating against straight line speculation, use: + * SB when the v8.5-A SB extension is enabled. + * DSB+ISB otherwise. */ +const char * +aarch64_sls_barrier (int mitigation_required) +{ + return mitigation_required + ? (TARGET_SB ? "sb" : "dsb\tsy\n\tisb") + : ""; +} + /* Target-specific selftests. */ #if CHECKING_P diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index ff15505d45546124868d2531b7f4e5b0f1f5bebc..75ef87a3b4674cc73cb42cc82cfb8e782acf77f6 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -447,8 +447,15 @@ (define_insn "indirect_jump" [(set (pc) (match_operand:DI 0 "register_operand" "r"))] "" - "br\\t%0" - [(set_attr "type" "branch")] + { + output_asm_insn ("br\\t%0", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_insn "jump" @@ -765,7 +772,10 @@ "* return aarch64_output_casesi (operands); " - [(set_attr "length" "16") + [(set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 16) + (match_test "TARGET_SB") (const_int 20)] + (const_int 24))) (set_attr "type" "branch")] ) @@ -844,18 +854,26 @@ [(return)] "" { + const char *ret = NULL; if (aarch64_return_address_signing_enabled () && TARGET_ARMV8_3 && !crtl->calls_eh_return) { if (aarch64_ra_sign_key == AARCH64_KEY_B) - return "retab"; + ret = "retab"; else - return "retaa"; + ret = "retaa"; } - return "ret"; + else + ret = "ret"; + output_asm_insn (ret, operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); } - [(set_attr "type" "branch")] + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_expand "return" @@ -867,8 +885,15 @@ (define_insn "simple_return" [(simple_return)] "" - "ret" - [(set_attr "type" "branch")] + { + output_asm_insn ("ret", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_insn "*cb<optab><mode>1" @@ -1066,10 +1091,20 @@ (unspec:DI [(match_operand:DI 2 "const_int_operand")] UNSPEC_CALLEE_ABI) (return)] "SIBLING_CALL_P (insn)" - "@ - br\\t%0 - b\\t%c0" - [(set_attr "type" "branch, branch")] + { + if (which_alternative == 0) + { + output_asm_insn ("br\\t%0", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + return "b\\t%c0"; + } + [(set_attr "type" "branch, branch") + (set_attr_alternative "length" + [(cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)) + (const_int 4)])] ) (define_insn "*sibcall_value_insn" @@ -1080,10 +1115,20 @@ (unspec:DI [(match_operand:DI 3 "const_int_operand")] UNSPEC_CALLEE_ABI) (return)] "SIBLING_CALL_P (insn)" - "@ - br\\t%1 - b\\t%c1" - [(set_attr "type" "branch, branch")] + { + if (which_alternative == 0) + { + output_asm_insn ("br\\t%1", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + return "b\\t%c1"; + } + [(set_attr "type" "branch, branch") + (set_attr_alternative "length" + [(cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)) + (const_int 4)])] ) ;; Call subroutine returning any type. diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c new file mode 100644 index 0000000000000000000000000000000000000000..11f614b4ef2eb0fa3707cb46a55583d6685b89d0 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-mharden-sls=retbr -mbranch-protection=pac-ret -march=armv8.3-a" } */ + +/* Testing the do_return pattern for retaa and retab. */ +long retbr_subcall(void); +long retbr_do_return_retaa(void) +{ + return retbr_subcall()+1; +} +__attribute__((target("branch-protection=pac-ret+b-key"))) +long retbr_do_return_retab(void) +{ + return retbr_subcall()+1; +} + +/* Ensure there are no BR or RET instructions which are not directly followed + by a speculation barrier. */ +/* { dg-final { scan-assembler-not "\t(br|ret|retaa|retab)\tx\[0-9\]\[0-9\]?\n\t(?!dsb\tsy\n\tisb|sb)" } } */ +/* { dg-final { scan-assembler-not "ret\t" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c new file mode 100644 index 0000000000000000000000000000000000000000..5cd4da6bbb719a5135faa2c9818dc873e3d5af70 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c @@ -0,0 +1,121 @@ +/* We ensure that -Wpedantic is off since it complains about the trampolines + we explicitly want to test. */ +/* { dg-additional-options "-mharden-sls=retbr -Wno-pedantic " } */ +/* + Ensure that the SLS hardening of RET and BR leaves no unprotected RET/BR + instructions. + */ +typedef int (foo) (int, int); +typedef void (bar) (int, int); +struct sls_testclass { + foo *x; + bar *y; + int left; + int right; +}; + +int +retbr_sibcall_value_insn (struct sls_testclass x) +{ + return x.x(x.left, x.right); +} + +void +retbr_sibcall_insn (struct sls_testclass x) +{ + x.y(x.left, x.right); +} + +/* Aim to test two different returns. + One that introduces a tail call in the middle of the function, and one that + has a normal return. */ +int +retbr_multiple_returns (struct sls_testclass x) +{ + int temp; + if (x.left % 10) + return x.x(x.left, 100); + else if (x.right % 20) + { + return x.x(x.left * x.right, 100); + } + temp = x.left % x.right; + temp *= 100; + temp /= 2; + return temp % 3; +} + +void +retbr_multiple_returns_void (struct sls_testclass x) +{ + if (x.left % 10) + { + x.y(x.left, 100); + } + else if (x.right % 20) + { + x.y(x.left * x.right, 100); + } + return; +} + +/* Testing the casesi jump via register. */ +__attribute__ ((optimize ("Os"))) +int +retbr_casesi_dispatch (struct sls_testclass x) +{ + switch (x.left) + { + case -5: + return -2; + case -3: + return -1; + case 0: + return 0; + case 3: + return 1; + case 5: + break; + default: + __builtin_unreachable (); + } + return x.right; +} + +/* Testing the BR in trampolines is mitigated against. */ +void f1 (void *); +void f3 (void *, void (*)(void *)); +void f2 (void *); + +int +retbr_trampolines (void *a, int b) +{ + if (!b) + { + f1 (a); + return 1; + } + if (b) + { + void retbr_tramp_internal (void *c) + { + if (c == a) + f2 (c); + } + f3 (a, retbr_tramp_internal); + } + return 0; +} + +/* Testing the indirect_jump pattern. */ +typedef signed __attribute__((mode(DI))) intptr_t; +intptr_t BUF[5]; +void +retbr_indirect_jump (intptr_t *buf) +{ + __builtin_longjmp(buf, 1); +} + +/* Ensure there are no BR or RET instructions which are not directly followed + by a speculation barrier. */ +/* { dg-final { scan-assembler-not "\t(br|ret|retaa|retab)\tx\[0-9\]\[0-9\]?\n\t(?!dsb\tsy\n\tisb|sb)" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp new file mode 100644 index 0000000000000000000000000000000000000000..fb63c6dfe230e64b11919381c30a3a05eee52e16 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp @@ -0,0 +1,73 @@ +# Regression driver for SLS mitigation on AArch64. +# Copyright (C) 2020-2020 Free Software Foundation, Inc. +# Contributed by ARM Ltd. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. */ + +# Exit immediately if this isn't an AArch64 target. +if {![istarget aarch64*-*-*] } then { + return +} + +# Load support procs. +load_lib gcc-dg.exp +load_lib torture-options.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " " +} + +# Initialize `dg'. +dg-init +torture-init + +# Use different architectures as well as the normal optimisation options. +# (i.e. use both SB and DSB+ISB barriers). + +set save-dg-do-what-default ${dg-do-what-default} +# Main loop. +# Run with torture tests (i.e. a bunch of different optimisation levels) just +# to increase test coverage. +set dg-do-what-default assemble +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "-save-temps" $DEFAULT_CFLAGS + +# Run the same tests but this time with SB extension. +# Since not all supported assemblers will support that extension we decide +# whether to assemble or just compile based on whether the extension is +# supported for the available assembler. + +set templist {} +foreach x $DG_TORTURE_OPTIONS { + lappend templist "$x -march=armv8.3-a+sb " + lappend templist "$x -march=armv8-a+sb " +} +set-torture-options $templist +if { [check_effective_target_aarch64_asm_sb_ok] } { + set dg-do-what-default assemble +} else { + set dg-do-what-default compile +} +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "-save-temps" $DEFAULT_CFLAGS +set dg-do-what-default ${save-dg-do-what-default} + +# All done. +torture-finish +dg-finish diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index a96b0525ba902b4d39e21123186171d951bd4e9d..6018a3ce4069d462087102b6d267d8e25b6f04dd 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -9349,7 +9349,7 @@ proc check_effective_target_aarch64_tiny { } { # various architecture extensions via the .arch_extension pseudo-op. foreach { aarch64_ext } { "fp" "simd" "crypto" "crc" "lse" "dotprod" "sve" - "i8mm" "f32mm" "f64mm" "bf16" } { + "i8mm" "f32mm" "f64mm" "bf16" "sb" } { eval [string map [list FUNC $aarch64_ext] { proc check_effective_target_aarch64_asm_FUNC_ok { } { if { [istarget aarch64*-*-*] } {
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index 8ca67d7e69edaf73c84f079e7e1c483009ad10c0..d2eb739bc89ecd9d0212416b8dc3ee4ba236a271 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -780,6 +780,7 @@ extern const atomic_ool_names aarch64_ool_ldeor_names; tree aarch64_resolve_overloaded_builtin_general (location_t, tree, void *); +const char * aarch64_sls_barrier (int); extern bool aarch64_harden_sls_retbr_p (void); extern bool aarch64_harden_sls_blr_p (void); diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h index 24767c747bab0d711627c5c646937c42f210d70b..5da3d94e335fc315e1d90e6a674f2f09cf1a4529 100644 --- a/gcc/config/aarch64/aarch64.h +++ b/gcc/config/aarch64/aarch64.h @@ -281,6 +281,7 @@ extern unsigned aarch64_architecture_version; #define AARCH64_ISA_F32MM (aarch64_isa_flags & AARCH64_FL_F32MM) #define AARCH64_ISA_F64MM (aarch64_isa_flags & AARCH64_FL_F64MM) #define AARCH64_ISA_BF16 (aarch64_isa_flags & AARCH64_FL_BF16) +#define AARCH64_ISA_SB (aarch64_isa_flags & AARCH64_FL_SB) /* Crypto is an optional extension to AdvSIMD. */ #define TARGET_CRYPTO (TARGET_SIMD && AARCH64_ISA_CRYPTO) @@ -378,6 +379,9 @@ extern unsigned aarch64_architecture_version; #define TARGET_FIX_ERR_A53_835769_DEFAULT 1 #endif +/* SB instruction is enabled through +sb. */ +#define TARGET_SB (AARCH64_ISA_SB) + /* Apply the workaround for Cortex-A53 erratum 835769. */ #define TARGET_FIX_ERR_A53_835769 \ ((aarch64_fix_a53_err835769 == 2) \ @@ -1058,8 +1062,11 @@ typedef struct #define RETURN_ADDR_RTX aarch64_return_addr -/* BTI c + 3 insns + 2 pointer-sized entries. */ -#define TRAMPOLINE_SIZE (TARGET_ILP32 ? 24 : 32) +/* BTI c + 3 insns + + sls barrier of DSB + ISB. + + 2 pointer-sized entries. */ +#define TRAMPOLINE_SIZE (24 \ + + (TARGET_ILP32 ? 8 : 16)) /* Trampolines contain dwords, so must be dword aligned. */ #define TRAMPOLINE_ALIGNMENT 64 diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 775f49991e5f599a843d3ef490b8cd044acfe78f..9356937fe266c68196392a1589b3cf96607de104 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -10822,8 +10822,8 @@ aarch64_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) static void aarch64_asm_trampoline_template (FILE *f) { - int offset1 = 16; - int offset2 = 20; + int offset1 = 24; + int offset2 = 28; if (aarch64_bti_enabled ()) { @@ -10846,6 +10846,17 @@ aarch64_asm_trampoline_template (FILE *f) } asm_fprintf (f, "\tbr\t%s\n", reg_names [IP1_REGNUM]); + /* We always emit a speculation barrier. + This is because the same trampoline template is used for every nested + function. Since nested functions are not particularly common or + performant we don't worry too much about the extra instructions to copy + around. + This is not yet a problem, since we have not yet implemented function + specific attributes to choose between hardening against straight line + speculation or not, but such function specific attributes are likely to + happen in the future. */ + output_asm_insn ("dsb\tsy\n\tisb", NULL); + /* The trampoline needs an extra padding instruction. In case if BTI is enabled the padding instruction is replaced by the BTI instruction at the beginning. */ @@ -10860,7 +10871,7 @@ static void aarch64_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) { rtx fnaddr, mem, a_tramp; - const int tramp_code_sz = 16; + const int tramp_code_sz = 24; /* Don't need to copy the trailing D-words, we fill those in below. */ emit_block_move (m_tramp, assemble_trampoline_template (), @@ -11054,6 +11065,7 @@ aarch64_output_casesi (rtx *operands) output_asm_insn (buf, operands); output_asm_insn (patterns[index][1], operands); output_asm_insn ("br\t%3", operands); + output_asm_insn (aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()), operands); assemble_label (asm_out_file, label); return ""; } @@ -22895,6 +22907,22 @@ aarch64_file_end_indicate_exec_stack () #undef GNU_PROPERTY_AARCH64_FEATURE_1_BTI #undef GNU_PROPERTY_AARCH64_FEATURE_1_AND +/* Helper function for straight line speculation. + Return what barrier should be emitted for straight line speculation + mitigation. + When not mitigating against straight line speculation this function returns + an empty string. + When mitigating against straight line speculation, use: + * SB when the v8.5-A SB extension is enabled. + * DSB+ISB otherwise. */ +const char * +aarch64_sls_barrier (int mitigation_required) +{ + return mitigation_required + ? (TARGET_SB ? "sb" : "dsb\tsy\n\tisb") + : ""; +} + /* Target-specific selftests. */ #if CHECKING_P diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index ff15505d45546124868d2531b7f4e5b0f1f5bebc..75ef87a3b4674cc73cb42cc82cfb8e782acf77f6 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -447,8 +447,15 @@ (define_insn "indirect_jump" [(set (pc) (match_operand:DI 0 "register_operand" "r"))] "" - "br\\t%0" - [(set_attr "type" "branch")] + { + output_asm_insn ("br\\t%0", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_insn "jump" @@ -765,7 +772,10 @@ "* return aarch64_output_casesi (operands); " - [(set_attr "length" "16") + [(set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 16) + (match_test "TARGET_SB") (const_int 20)] + (const_int 24))) (set_attr "type" "branch")] ) @@ -844,18 +854,26 @@ [(return)] "" { + const char *ret = NULL; if (aarch64_return_address_signing_enabled () && TARGET_ARMV8_3 && !crtl->calls_eh_return) { if (aarch64_ra_sign_key == AARCH64_KEY_B) - return "retab"; + ret = "retab"; else - return "retaa"; + ret = "retaa"; } - return "ret"; + else + ret = "ret"; + output_asm_insn (ret, operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); } - [(set_attr "type" "branch")] + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_expand "return" @@ -867,8 +885,15 @@ (define_insn "simple_return" [(simple_return)] "" - "ret" - [(set_attr "type" "branch")] + { + output_asm_insn ("ret", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + [(set_attr "type" "branch") + (set (attr "length") + (cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)))] ) (define_insn "*cb<optab><mode>1" @@ -1066,10 +1091,20 @@ (unspec:DI [(match_operand:DI 2 "const_int_operand")] UNSPEC_CALLEE_ABI) (return)] "SIBLING_CALL_P (insn)" - "@ - br\\t%0 - b\\t%c0" - [(set_attr "type" "branch, branch")] + { + if (which_alternative == 0) + { + output_asm_insn ("br\\t%0", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + return "b\\t%c0"; + } + [(set_attr "type" "branch, branch") + (set_attr_alternative "length" + [(cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)) + (const_int 4)])] ) (define_insn "*sibcall_value_insn" @@ -1080,10 +1115,20 @@ (unspec:DI [(match_operand:DI 3 "const_int_operand")] UNSPEC_CALLEE_ABI) (return)] "SIBLING_CALL_P (insn)" - "@ - br\\t%1 - b\\t%c1" - [(set_attr "type" "branch, branch")] + { + if (which_alternative == 0) + { + output_asm_insn ("br\\t%1", operands); + return aarch64_sls_barrier (aarch64_harden_sls_retbr_p ()); + } + return "b\\t%c1"; + } + [(set_attr "type" "branch, branch") + (set_attr_alternative "length" + [(cond [(match_test "!aarch64_harden_sls_retbr_p ()") (const_int 4) + (match_test "TARGET_SB") (const_int 8)] + (const_int 12)) + (const_int 4)])] ) ;; Call subroutine returning any type. diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c new file mode 100644 index 0000000000000000000000000000000000000000..11f614b4ef2eb0fa3707cb46a55583d6685b89d0 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr-pacret.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-mharden-sls=retbr -mbranch-protection=pac-ret -march=armv8.3-a" } */ + +/* Testing the do_return pattern for retaa and retab. */ +long retbr_subcall(void); +long retbr_do_return_retaa(void) +{ + return retbr_subcall()+1; +} +__attribute__((target("branch-protection=pac-ret+b-key"))) +long retbr_do_return_retab(void) +{ + return retbr_subcall()+1; +} + +/* Ensure there are no BR or RET instructions which are not directly followed + by a speculation barrier. */ +/* { dg-final { scan-assembler-not "\t(br|ret|retaa|retab)\tx\[0-9\]\[0-9\]?\n\t(?!dsb\tsy\n\tisb|sb)" } } */ +/* { dg-final { scan-assembler-not "ret\t" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c new file mode 100644 index 0000000000000000000000000000000000000000..5cd4da6bbb719a5135faa2c9818dc873e3d5af70 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-miti-retbr.c @@ -0,0 +1,121 @@ +/* We ensure that -Wpedantic is off since it complains about the trampolines + we explicitly want to test. */ +/* { dg-additional-options "-mharden-sls=retbr -Wno-pedantic " } */ +/* + Ensure that the SLS hardening of RET and BR leaves no unprotected RET/BR + instructions. + */ +typedef int (foo) (int, int); +typedef void (bar) (int, int); +struct sls_testclass { + foo *x; + bar *y; + int left; + int right; +}; + +int +retbr_sibcall_value_insn (struct sls_testclass x) +{ + return x.x(x.left, x.right); +} + +void +retbr_sibcall_insn (struct sls_testclass x) +{ + x.y(x.left, x.right); +} + +/* Aim to test two different returns. + One that introduces a tail call in the middle of the function, and one that + has a normal return. */ +int +retbr_multiple_returns (struct sls_testclass x) +{ + int temp; + if (x.left % 10) + return x.x(x.left, 100); + else if (x.right % 20) + { + return x.x(x.left * x.right, 100); + } + temp = x.left % x.right; + temp *= 100; + temp /= 2; + return temp % 3; +} + +void +retbr_multiple_returns_void (struct sls_testclass x) +{ + if (x.left % 10) + { + x.y(x.left, 100); + } + else if (x.right % 20) + { + x.y(x.left * x.right, 100); + } + return; +} + +/* Testing the casesi jump via register. */ +__attribute__ ((optimize ("Os"))) +int +retbr_casesi_dispatch (struct sls_testclass x) +{ + switch (x.left) + { + case -5: + return -2; + case -3: + return -1; + case 0: + return 0; + case 3: + return 1; + case 5: + break; + default: + __builtin_unreachable (); + } + return x.right; +} + +/* Testing the BR in trampolines is mitigated against. */ +void f1 (void *); +void f3 (void *, void (*)(void *)); +void f2 (void *); + +int +retbr_trampolines (void *a, int b) +{ + if (!b) + { + f1 (a); + return 1; + } + if (b) + { + void retbr_tramp_internal (void *c) + { + if (c == a) + f2 (c); + } + f3 (a, retbr_tramp_internal); + } + return 0; +} + +/* Testing the indirect_jump pattern. */ +typedef signed __attribute__((mode(DI))) intptr_t; +intptr_t BUF[5]; +void +retbr_indirect_jump (intptr_t *buf) +{ + __builtin_longjmp(buf, 1); +} + +/* Ensure there are no BR or RET instructions which are not directly followed + by a speculation barrier. */ +/* { dg-final { scan-assembler-not "\t(br|ret|retaa|retab)\tx\[0-9\]\[0-9\]?\n\t(?!dsb\tsy\n\tisb|sb)" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp new file mode 100644 index 0000000000000000000000000000000000000000..fb63c6dfe230e64b11919381c30a3a05eee52e16 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sls-mitigation/sls-mitigation.exp @@ -0,0 +1,73 @@ +# Regression driver for SLS mitigation on AArch64. +# Copyright (C) 2020-2020 Free Software Foundation, Inc. +# Contributed by ARM Ltd. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. */ + +# Exit immediately if this isn't an AArch64 target. +if {![istarget aarch64*-*-*] } then { + return +} + +# Load support procs. +load_lib gcc-dg.exp +load_lib torture-options.exp + +# If a testcase doesn't have special options, use these. +global DEFAULT_CFLAGS +if ![info exists DEFAULT_CFLAGS] then { + set DEFAULT_CFLAGS " " +} + +# Initialize `dg'. +dg-init +torture-init + +# Use different architectures as well as the normal optimisation options. +# (i.e. use both SB and DSB+ISB barriers). + +set save-dg-do-what-default ${dg-do-what-default} +# Main loop. +# Run with torture tests (i.e. a bunch of different optimisation levels) just +# to increase test coverage. +set dg-do-what-default assemble +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "-save-temps" $DEFAULT_CFLAGS + +# Run the same tests but this time with SB extension. +# Since not all supported assemblers will support that extension we decide +# whether to assemble or just compile based on whether the extension is +# supported for the available assembler. + +set templist {} +foreach x $DG_TORTURE_OPTIONS { + lappend templist "$x -march=armv8.3-a+sb " + lappend templist "$x -march=armv8-a+sb " +} +set-torture-options $templist +if { [check_effective_target_aarch64_asm_sb_ok] } { + set dg-do-what-default assemble +} else { + set dg-do-what-default compile +} +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "-save-temps" $DEFAULT_CFLAGS +set dg-do-what-default ${save-dg-do-what-default} + +# All done. +torture-finish +dg-finish diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index a96b0525ba902b4d39e21123186171d951bd4e9d..6018a3ce4069d462087102b6d267d8e25b6f04dd 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -9349,7 +9349,7 @@ proc check_effective_target_aarch64_tiny { } { # various architecture extensions via the .arch_extension pseudo-op. foreach { aarch64_ext } { "fp" "simd" "crypto" "crc" "lse" "dotprod" "sve" - "i8mm" "f32mm" "f64mm" "bf16" } { + "i8mm" "f32mm" "f64mm" "bf16" "sb" } { eval [string map [list FUNC $aarch64_ext] { proc check_effective_target_aarch64_asm_FUNC_ok { } { if { [istarget aarch64*-*-*] } {