@@ -56,6 +56,8 @@ static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
int, bool *);
+static tree handle_no_sanitize_shadow_call_stack_attribute (tree *, tree,
+ tree, int, bool *);
static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
int, bool *);
static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
@@ -454,6 +456,10 @@ const struct attribute_spec c_common_attribute_table[] =
handle_no_sanitize_attribute, NULL },
{ "no_sanitize_address", 0, 0, true, false, false, false,
handle_no_sanitize_address_attribute, NULL },
+ { "no_sanitize_shadow_call_stack",
+ 0, 0, true, false, false, false,
+ handle_no_sanitize_shadow_call_stack_attribute,
+ NULL },
{ "no_sanitize_thread", 0, 0, true, false, false, false,
handle_no_sanitize_thread_attribute, NULL },
{ "no_sanitize_undefined", 0, 0, true, false, false, false,
@@ -1175,6 +1181,21 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
return NULL_TREE;
}
+/* Handle a "no_sanitize_shadow_call_stack" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+handle_no_sanitize_shadow_call_stack_attribute (tree *node, tree name,
+ tree, int, bool *no_add_attrs)
+{
+ *no_add_attrs = true;
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ else
+ add_no_sanitize_value (*node, SANITIZE_SHADOW_CALL_STACK);
+
+ return NULL_TREE;
+}
+
/* Handle a "no_sanitize_thread" attribute; arguments as in
struct attribute_spec.handler. */
@@ -893,6 +893,7 @@ void aarch64_register_pragmas (void);
void aarch64_relayout_simd_types (void);
void aarch64_reset_previous_fndecl (void);
bool aarch64_return_address_signing_enabled (void);
+bool aarch64_shadow_call_stack_enabled (void);
bool aarch64_bti_enabled (void);
void aarch64_save_restore_target_globals (tree);
void aarch64_addti_scratch_regs (rtx, rtx, rtx *,
@@ -79,6 +79,7 @@
#include "tree-ssa-loop-niter.h"
#include "fractional-cost.h"
#include "rtlanal.h"
+#include "asan.h"
/* This file should be included last. */
#include "target-def.h"
@@ -7799,6 +7800,24 @@ aarch64_return_address_signing_enabled (void)
&& known_ge (cfun->machine->frame.reg_offset[LR_REGNUM], 0)));
}
+/* Return TRUE if shadow call stack should be enabled for the current
+ function, otherwise return FALSE. */
+
+bool
+aarch64_shadow_call_stack_enabled (void)
+{
+ /* This function should only be called after frame laid out. */
+ gcc_assert (cfun->machine->frame.laid_out);
+
+ if (crtl->calls_eh_return)
+ return false;
+
+ /* We only deal with a function if its LR is pushed onto stack
+ and attribute no_sanitize_shadow_call_stack is not specified. */
+ return (sanitize_flags_p (SANITIZE_SHADOW_CALL_STACK)
+ && known_ge (cfun->machine->frame.reg_offset[LR_REGNUM], 0));
+}
+
/* Return TRUE if Branch Target Identification Mechanism is enabled. */
bool
aarch64_bti_enabled (void)
@@ -8810,6 +8829,10 @@ aarch64_expand_prologue (void)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* Push return address to shadow call stack. */
+ if (aarch64_shadow_call_stack_enabled ())
+ emit_insn (gen_scs_push ());
+
if (flag_stack_usage_info)
current_function_static_stack_size = constant_lower_bound (frame_size);
@@ -9066,6 +9089,10 @@ aarch64_expand_epilogue (bool for_sibcall)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* Pop return address from shadow call stack. */
+ if (aarch64_shadow_call_stack_enabled ())
+ emit_insn (gen_scs_pop ());
+
/* We prefer to emit the combined return/authenticate instruction RETAA,
however there are three cases in which we must instead emit an explicit
authentication instruction.
@@ -100,6 +100,17 @@
generating stack clash probes. */
#define STACK_CLASH_MAX_UNROLL_PAGES 4
+/* This value represents whether the shadow call stack is implemented on
+ the target platform. */
+#define TARGET_SUPPORT_SHADOW_CALL_STACK true
+
+#define TARGET_CHECK_SCS_RESERVED_REGISTER() \
+ do { \
+ if (!fixed_regs[R18_REGNUM]) \
+ error ("%<-fsanitize=shadow-call-stack%> only " \
+ "allowed with %<-ffixed-x18%>"); \
+ } while (0)
+
/* The architecture reserves all bits of the address for hardware use,
so the vbit must go into the delta field of pointers to member
functions. This is the same config as that in the AArch32
@@ -6994,6 +6994,24 @@ (define_insn "xpaclri"
"hint\t7 // xpaclri"
)
+;; Save X30 in the X18-based POST_INC stack (consistent with clang).
+(define_insn "scs_push"
+ [(set (mem:DI (reg:DI R18_REGNUM)) (reg:DI R30_REGNUM))
+ (set (reg:DI R18_REGNUM) (plus:DI (reg:DI R18_REGNUM) (const_int 8)))]
+ ""
+ "str\\tx30, [x18], #8"
+ [(set_attr "type" "store_8")]
+)
+
+;; Load X30 form the X18-based PRE_DEC stack (consistent with clang).
+(define_insn "scs_pop"
+ [(set (reg:DI R18_REGNUM) (minus:DI (reg:DI R18_REGNUM) (const_int 8)))
+ (set (reg:DI R30_REGNUM) (mem:DI (reg:DI R18_REGNUM)))]
+ ""
+ "ldr\\tx30, [x18, #-8]!"
+ [(set_attr "type" "load_8")]
+)
+
;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory. This blocks insns from being moved across this point.
@@ -1172,6 +1172,10 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#define PCC_BITFIELD_TYPE_MATTERS false
#endif
+#ifndef TARGET_SUPPORT_SHADOW_CALL_STACK
+#define TARGET_SUPPORT_SHADOW_CALL_STACK false
+#endif
+
#ifndef INSN_SETS_ARE_DELAYED
#define INSN_SETS_ARE_DELAYED(INSN) false
#endif
@@ -3453,6 +3453,13 @@ The @code{no_address_safety_analysis} is a deprecated alias of the
@code{no_sanitize_address} attribute, new code should use
@code{no_sanitize_address}.
+@item no_sanitize_shadow_call_stack
+@cindex @code{no_sanitize_shadow_call_stack} function attribute
+The @code{no_sanitize_shadow_call_stack} attribute on functions is used
+to inform the compiler that it should not instrument shadow call stack
+push/pop instructions in the function when compiling with the
+@option{-fsanitize=shadow-call-stack} option.
+
@item no_sanitize_thread
@cindex @code{no_sanitize_thread} function attribute
The @code{no_sanitize_thread} attribute on functions is used
@@ -15224,6 +15224,35 @@ add @code{detect_invalid_pointer_pairs=2} to the environment variable
@env{ASAN_OPTIONS}. Using @code{detect_invalid_pointer_pairs=1} detects
invalid operation only when both pointers are non-null.
+@item -fsanitize=shadow-call-stack
+@opindex fsanitize=shadow-call-stack
+Enable ShadowCallStack, a security enhancement mechanism used to protect
+programs against return address overwrites (e.g. stack buffer overflows.)
+It works by saving a function's return address to a separately allocated
+shadow call stack in the function prologue and restore the return address
+from the shadow call stack in the function epilogue. Instrumentation only
+occurs in functions that need to save the return address to the stack.
+
+Currently it only supports the aarch64 platform and only works fine in
+the linux kernel that implements CONFIG_SHADOW_CALL_STACK.
+For the user space programs, runtime support is not currently provided
+in libc and libgcc. Users who want to use this feature in user space need
+to provide their own support for the runtime. It should be noted that
+this may cause the ABI rules to be broken.
+
+On aarch64, the instrumentation makes use of the platform register 'x18'.
+This generally means that any code that may run on the same thread as code
+compiled with ShadowCallStack must be compiled with the flag
+@option{-ffixed-x18}, otherwise functions compiled without
+@option{-ffixed-x18} may clobber x18 and break scs.
+
+And also because there is no runtime support, the code compiled with
+ShadowCallStack cannot use exception handling, @option{-fno-exceptions}
+also needs to be specified.
+
+See @uref{https://clang.llvm.org/docs/ShadowCallStack.html} for more
+details.
+
@item -fsanitize=thread
@opindex fsanitize=thread
Enable ThreadSanitizer, a fast data race detector.
@@ -321,6 +321,8 @@ enum sanitize_code {
SANITIZE_HWADDRESS = 1UL << 28,
SANITIZE_USER_HWADDRESS = 1UL << 29,
SANITIZE_KERNEL_HWADDRESS = 1UL << 30,
+ /* Shadow Call Stack. */
+ SANITIZE_SHADOW_CALL_STACK = 1UL << 31,
SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
@@ -477,4 +477,10 @@ handle_common_deferred_options (void)
gcc_unreachable ();
}
}
+
+#ifdef TARGET_CHECK_SCS_RESERVED_REGISTER
+ if (flag_sanitize & SANITIZE_SHADOW_CALL_STACK)
+ TARGET_CHECK_SCS_RESERVED_REGISTER ();
+#endif
+
}
@@ -1308,6 +1308,17 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
sorry ("transactional memory is not supported with "
"%<-fsanitize=kernel-address%>");
+ if (opts->x_flag_sanitize & SANITIZE_SHADOW_CALL_STACK)
+ {
+ if (!TARGET_SUPPORT_SHADOW_CALL_STACK)
+ error_at (loc, "%<-fsanitize=shadow-call-stack%> not supported "
+ "in current platform");
+
+ if (opts->x_flag_exceptions)
+ error_at (loc, "%<-fsanitize=shadow-call-stack%> only allowed "
+ "with %<-fno-exceptions%>");
+ }
+
/* Currently live patching is not support for LTO. */
if (opts->x_flag_live_patching && opts->x_flag_lto)
sorry ("live patching is not supported with LTO");
@@ -1994,6 +2005,7 @@ const struct sanitizer_opts_s sanitizer_opts[] =
SANITIZER_OPT (vptr, SANITIZE_VPTR, true),
SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true),
SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true),
+ SANITIZER_OPT (shadow-call-stack, SANITIZE_SHADOW_CALL_STACK, false),
SANITIZER_OPT (all, ~0U, true),
#undef SANITIZER_OPT
{ NULL, 0U, 0UL, false }
new file mode 100644
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shadow-call-stack" } */
+
+int i;
+
+/* { dg-error "'-fsanitize=shadow-call-stack' only allowed with '-ffixed-x18'" "" {target "aarch64*-*-*" } 0 } */
new file mode 100644
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shadow-call-stack -ffixed-x18 -fexceptions" } */
+
+int i;
+
+/* { dg-error "'-fsanitize=shadow-call-stack' only allowed with '-fno-exceptions'" "" {target "aarch64*-*-*" } 0 } */
new file mode 100644
@@ -0,0 +1,45 @@
+/* Testing shadow call stack. */
+/* scs_push: str x30, [x18], #8 */
+/* scs_pop: ldr x30, [x18, #-8]! */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsanitize=shadow-call-stack -ffixed-x18 -fno-exceptions" } */
+
+int foo (int);
+
+/* function not use x30. */
+int func1 (void)
+{
+ return 0;
+}
+
+/* function use x30. */
+int func2 (void)
+{
+ /* scs push */
+ asm volatile ("":::"x30");
+
+ return 0;
+ /* scs pop */
+}
+
+/* sibcall. */
+int func3 (int a, int b)
+{
+ /* scs push */
+ asm volatile ("":::"x30");
+
+ return foo (a+b);
+ /* scs pop */
+}
+
+/* eh_return. */
+int func4 (long offset, void *handler)
+{
+ /* Do not emit scs push/pop */
+ asm volatile ("":::"x30");
+
+ __builtin_eh_return (offset, handler);
+}
+
+/* { dg-final { scan-assembler-times "str\tx30, \\\[x18\\\], #8" 2 } } */
+/* { dg-final { scan-assembler-times "ldr\tx30, \\\[x18, #-8\\\]!" 2 } } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* Testing the disable of shadow call stack. */
+/* scs_push: str x30, [x18], #8 */
+/* scs_pop: ldr x30, [x18, #-8]! */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsanitize=shadow-call-stack -ffixed-x18 -fno-exceptions" } */
+
+int foo (int);
+
+/* function disable shadow call stack. */
+int __attribute__((no_sanitize_shadow_call_stack)) func1 (void)
+{
+ asm volatile ("":::"x30");
+
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "str\tx30, \\\[x18\\\], #8" } } */
+/* { dg-final { scan-assembler-not "ldr\tx30, \\\[x18, #-8\\\]!" } } */
Shadow Call Stack can be used to protect the return address of a function at runtime, and clang already supports this feature[1]. To enable SCS in user mode, in addition to compiler, other support is also required (as discussed in [2]). This patch only adds basic support for SCS from the compiler side, and provides convenience for users to enable SCS. For linux kernel, only the support of the compiler is required. [1] https://clang.llvm.org/docs/ShadowCallStack.html [2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102768 Signed-off-by: Dan Li <ashimida@linux.alibaba.com> gcc/c-family/ChangeLog: * c-attribs.c (handle_no_sanitize_shadow_call_stack_attribute): New. gcc/ChangeLog: * config/aarch64/aarch64-protos.h (aarch64_shadow_call_stack_enabled): New decl. * config/aarch64/aarch64.c (aarch64_shadow_call_stack_enabled): New. (aarch64_expand_prologue): Push x30 onto SCS before it's pushed onto stack. (aarch64_expand_epilogue): Pop x30 frome SCS. * config/aarch64/aarch64.h (TARGET_SUPPORT_SHADOW_CALL_STACK): New macro. (TARGET_CHECK_SCS_RESERVED_REGISTER): Likewise. * config/aarch64/aarch64.md (scs_push): New template. (scs_pop): Likewise. * defaults.h (TARGET_SUPPORT_SHADOW_CALL_STACK): New macro. * doc/extend.texi: Document -fsanitize=shadow-call-stack. * doc/invoke.texi: Document attribute. * flag-types.h (enum sanitize_code): Add SANITIZE_SHADOW_CALL_STACK. * opts-global.c (handle_common_deferred_options): Add SCS compile option check. * opts.c (finish_options): Likewise. gcc/testsuite/ChangeLog: * gcc.target/aarch64/shadow_call_stack_1.c: New test. * gcc.target/aarch64/shadow_call_stack_2.c: New test. * gcc.target/aarch64/shadow_call_stack_3.c: New test. * gcc.target/aarch64/shadow_call_stack_4.c: New test. --- gcc/c-family/c-attribs.c | 21 +++++++++ gcc/config/aarch64/aarch64-protos.h | 1 + gcc/config/aarch64/aarch64.c | 27 +++++++++++ gcc/config/aarch64/aarch64.h | 11 +++++ gcc/config/aarch64/aarch64.md | 18 ++++++++ gcc/defaults.h | 4 ++ gcc/doc/extend.texi | 7 +++ gcc/doc/invoke.texi | 29 ++++++++++++ gcc/flag-types.h | 2 + gcc/opts-global.c | 6 +++ gcc/opts.c | 12 +++++ .../gcc.target/aarch64/shadow_call_stack_1.c | 6 +++ .../gcc.target/aarch64/shadow_call_stack_2.c | 6 +++ .../gcc.target/aarch64/shadow_call_stack_3.c | 45 +++++++++++++++++++ .../gcc.target/aarch64/shadow_call_stack_4.c | 18 ++++++++ 15 files changed, 213 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/shadow_call_stack_1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/shadow_call_stack_2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/shadow_call_stack_3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/shadow_call_stack_4.c