aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/config/aarch64/aarch64.cc113
-rw-r--r--gcc/config/aarch64/aarch64.h21
-rw-r--r--gcc/config/aarch64/aarch64.md10
-rw-r--r--gcc/doc/invoke.texi30
-rw-r--r--gcc/doc/tm.texi5
-rw-r--r--gcc/doc/tm.texi.in2
-rw-r--r--gcc/flag-types.h2
-rw-r--r--gcc/opts.cc4
-rw-r--r--gcc/target.def8
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_1.c6
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_2.c6
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_3.c45
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_4.c20
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_5.c18
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_6.c18
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_7.c18
-rw-r--r--gcc/testsuite/gcc.target/aarch64/shadow_call_stack_8.c24
-rw-r--r--gcc/toplev.cc10
-rw-r--r--gcc/ubsan.cc4
19 files changed, 330 insertions, 34 deletions
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 37ed22b..8bcee8b 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -80,6 +80,7 @@
#include "fractional-cost.h"
#include "rtlanal.h"
#include "tree-dfa.h"
+#include "asan.h"
/* This file should be included last. */
#include "target-def.h"
@@ -7547,8 +7548,8 @@ aarch64_layout_frame (void)
#define SLOT_NOT_REQUIRED (-2)
#define SLOT_REQUIRED (-1)
- frame.wb_candidate1 = INVALID_REGNUM;
- frame.wb_candidate2 = INVALID_REGNUM;
+ frame.wb_push_candidate1 = INVALID_REGNUM;
+ frame.wb_push_candidate2 = INVALID_REGNUM;
frame.spare_pred_reg = INVALID_REGNUM;
/* First mark all the registers that really need to be saved... */
@@ -7663,9 +7664,9 @@ aarch64_layout_frame (void)
{
/* FP and LR are placed in the linkage record. */
frame.reg_offset[R29_REGNUM] = offset;
- frame.wb_candidate1 = R29_REGNUM;
+ frame.wb_push_candidate1 = R29_REGNUM;
frame.reg_offset[R30_REGNUM] = offset + UNITS_PER_WORD;
- frame.wb_candidate2 = R30_REGNUM;
+ frame.wb_push_candidate2 = R30_REGNUM;
offset += 2 * UNITS_PER_WORD;
}
@@ -7673,10 +7674,10 @@ aarch64_layout_frame (void)
if (known_eq (frame.reg_offset[regno], SLOT_REQUIRED))
{
frame.reg_offset[regno] = offset;
- if (frame.wb_candidate1 == INVALID_REGNUM)
- frame.wb_candidate1 = regno;
- else if (frame.wb_candidate2 == INVALID_REGNUM)
- frame.wb_candidate2 = regno;
+ if (frame.wb_push_candidate1 == INVALID_REGNUM)
+ frame.wb_push_candidate1 = regno;
+ else if (frame.wb_push_candidate2 == INVALID_REGNUM)
+ frame.wb_push_candidate2 = regno;
offset += UNITS_PER_WORD;
}
@@ -7699,11 +7700,11 @@ aarch64_layout_frame (void)
}
frame.reg_offset[regno] = offset;
- if (frame.wb_candidate1 == INVALID_REGNUM)
- frame.wb_candidate1 = regno;
- else if (frame.wb_candidate2 == INVALID_REGNUM
- && frame.wb_candidate1 >= V0_REGNUM)
- frame.wb_candidate2 = regno;
+ if (frame.wb_push_candidate1 == INVALID_REGNUM)
+ frame.wb_push_candidate1 = regno;
+ else if (frame.wb_push_candidate2 == INVALID_REGNUM
+ && frame.wb_push_candidate1 >= V0_REGNUM)
+ frame.wb_push_candidate2 = regno;
offset += vector_save_size;
}
@@ -7734,10 +7735,38 @@ aarch64_layout_frame (void)
frame.sve_callee_adjust = 0;
frame.callee_offset = 0;
+ frame.wb_pop_candidate1 = frame.wb_push_candidate1;
+ frame.wb_pop_candidate2 = frame.wb_push_candidate2;
+
+ /* Shadow call stack only deals with functions where the LR is pushed
+ onto the stack and without specifying the "no_sanitize" attribute
+ with the argument "shadow-call-stack". */
+ frame.is_scs_enabled
+ = (!crtl->calls_eh_return
+ && sanitize_flags_p (SANITIZE_SHADOW_CALL_STACK)
+ && known_ge (cfun->machine->frame.reg_offset[LR_REGNUM], 0));
+
+ /* When shadow call stack is enabled, the scs_pop in the epilogue will
+ restore x30, and we don't need to pop x30 again in the traditional
+ way. Pop candidates record the registers that need to be popped
+ eventually. */
+ if (frame.is_scs_enabled)
+ {
+ if (frame.wb_pop_candidate2 == R30_REGNUM)
+ frame.wb_pop_candidate2 = INVALID_REGNUM;
+ else if (frame.wb_pop_candidate1 == R30_REGNUM)
+ frame.wb_pop_candidate1 = INVALID_REGNUM;
+ }
+
+ /* If candidate2 is INVALID_REGNUM, we need to adjust max_push_offset to
+ 256 to ensure that the offset meets the requirements of emit_move_insn.
+ Similarly, if candidate1 is INVALID_REGNUM, we need to set
+ max_push_offset to 0, because no registers are popped at this time,
+ so callee_adjust cannot be adjusted. */
HOST_WIDE_INT max_push_offset = 0;
- if (frame.wb_candidate2 != INVALID_REGNUM)
+ if (frame.wb_pop_candidate2 != INVALID_REGNUM)
max_push_offset = 512;
- else if (frame.wb_candidate1 != INVALID_REGNUM)
+ else if (frame.wb_pop_candidate1 != INVALID_REGNUM)
max_push_offset = 256;
HOST_WIDE_INT const_size, const_outgoing_args_size, const_fp_offset;
@@ -7827,8 +7856,8 @@ aarch64_layout_frame (void)
{
/* We've decided not to associate any register saves with the initial
stack allocation. */
- frame.wb_candidate1 = INVALID_REGNUM;
- frame.wb_candidate2 = INVALID_REGNUM;
+ frame.wb_pop_candidate1 = frame.wb_push_candidate1 = INVALID_REGNUM;
+ frame.wb_pop_candidate2 = frame.wb_push_candidate2 = INVALID_REGNUM;
}
frame.laid_out = true;
@@ -8141,8 +8170,8 @@ aarch64_save_callee_saves (poly_int64 start_offset,
bool frame_related_p = aarch64_emit_cfi_for_reg_p (regno);
if (skip_wb
- && (regno == cfun->machine->frame.wb_candidate1
- || regno == cfun->machine->frame.wb_candidate2))
+ && (regno == cfun->machine->frame.wb_push_candidate1
+ || regno == cfun->machine->frame.wb_push_candidate2))
continue;
if (cfun->machine->reg_is_wrapped_separately[regno])
@@ -8252,8 +8281,8 @@ aarch64_restore_callee_saves (poly_int64 start_offset, unsigned start,
rtx reg, mem;
if (skip_wb
- && (regno == cfun->machine->frame.wb_candidate1
- || regno == cfun->machine->frame.wb_candidate2))
+ && (regno == cfun->machine->frame.wb_pop_candidate1
+ || regno == cfun->machine->frame.wb_pop_candidate2))
continue;
machine_mode mode = aarch64_reg_save_mode (regno);
@@ -8424,8 +8453,8 @@ aarch64_get_separate_components (void)
if (cfun->machine->frame.spare_pred_reg != INVALID_REGNUM)
bitmap_clear_bit (components, cfun->machine->frame.spare_pred_reg);
- unsigned reg1 = cfun->machine->frame.wb_candidate1;
- unsigned reg2 = cfun->machine->frame.wb_candidate2;
+ unsigned reg1 = cfun->machine->frame.wb_push_candidate1;
+ unsigned reg2 = cfun->machine->frame.wb_push_candidate2;
/* If registers have been chosen to be stored/restored with
writeback don't interfere with them to avoid having to output explicit
stack adjustment instructions. */
@@ -9034,8 +9063,8 @@ aarch64_expand_prologue (void)
poly_int64 sve_callee_adjust = cfun->machine->frame.sve_callee_adjust;
poly_int64 below_hard_fp_saved_regs_size
= cfun->machine->frame.below_hard_fp_saved_regs_size;
- unsigned reg1 = cfun->machine->frame.wb_candidate1;
- unsigned reg2 = cfun->machine->frame.wb_candidate2;
+ unsigned reg1 = cfun->machine->frame.wb_push_candidate1;
+ unsigned reg2 = cfun->machine->frame.wb_push_candidate2;
bool emit_frame_chain = cfun->machine->frame.emit_frame_chain;
rtx_insn *insn;
@@ -9066,6 +9095,10 @@ aarch64_expand_prologue (void)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* Push return address to shadow call stack. */
+ if (cfun->machine->frame.is_scs_enabled)
+ emit_insn (gen_scs_push ());
+
if (flag_stack_usage_info)
current_function_static_stack_size = constant_lower_bound (frame_size);
@@ -9212,8 +9245,10 @@ aarch64_expand_epilogue (bool for_sibcall)
poly_int64 sve_callee_adjust = cfun->machine->frame.sve_callee_adjust;
poly_int64 below_hard_fp_saved_regs_size
= cfun->machine->frame.below_hard_fp_saved_regs_size;
- unsigned reg1 = cfun->machine->frame.wb_candidate1;
- unsigned reg2 = cfun->machine->frame.wb_candidate2;
+ unsigned reg1 = cfun->machine->frame.wb_pop_candidate1;
+ unsigned reg2 = cfun->machine->frame.wb_pop_candidate2;
+ unsigned int last_gpr = (cfun->machine->frame.is_scs_enabled
+ ? R29_REGNUM : R30_REGNUM);
rtx cfi_ops = NULL;
rtx_insn *insn;
/* A stack clash protection prologue may not have left EP0_REGNUM or
@@ -9283,8 +9318,12 @@ aarch64_expand_epilogue (bool for_sibcall)
false, &cfi_ops);
if (maybe_ne (sve_callee_adjust, 0))
aarch64_add_sp (NULL_RTX, NULL_RTX, sve_callee_adjust, true);
+
+ /* When shadow call stack is enabled, the scs_pop in the epilogue will
+ restore x30, we don't need to restore x30 again in the traditional
+ way. */
aarch64_restore_callee_saves (callee_offset - sve_callee_adjust,
- R0_REGNUM, R30_REGNUM,
+ R0_REGNUM, last_gpr,
callee_adjust != 0, &cfi_ops);
if (need_barrier_p)
@@ -9322,6 +9361,17 @@ aarch64_expand_epilogue (bool for_sibcall)
RTX_FRAME_RELATED_P (insn) = 1;
}
+ /* Pop return address from shadow call stack. */
+ if (cfun->machine->frame.is_scs_enabled)
+ {
+ machine_mode mode = aarch64_reg_save_mode (R30_REGNUM);
+ rtx reg = gen_rtx_REG (mode, R30_REGNUM);
+
+ insn = emit_insn (gen_scs_pop ());
+ add_reg_note (insn, REG_CFA_RESTORE, reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
/* 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.
@@ -16878,6 +16928,10 @@ aarch64_override_options_internal (struct gcc_options *opts)
aarch64_stack_protector_guard_offset = offs;
}
+ if ((flag_sanitize & SANITIZE_SHADOW_CALL_STACK)
+ && !fixed_regs[R18_REGNUM])
+ error ("%<-fsanitize=shadow-call-stack%> requires %<-ffixed-x18%>");
+
initialize_aarch64_code_model (opts);
initialize_aarch64_tls_size (opts);
@@ -27084,6 +27138,9 @@ aarch64_libgcc_floating_mode_supported_p
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE aarch64_sls_emit_blr_function_thunks
+#undef TARGET_HAVE_SHADOW_CALL_STACK
+#define TARGET_HAVE_SHADOW_CALL_STACK true
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-aarch64.h"
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index dddf133..27ba4f4 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -922,9 +922,21 @@ struct GTY (()) aarch64_frame
Indicated by CALLEE_ADJUST == 0 && EMIT_FRAME_CHAIN.
These fields indicate which registers we've decided to handle using
- (1) or (2), or INVALID_REGNUM if none. */
- unsigned wb_candidate1;
- unsigned wb_candidate2;
+ (1) or (2), or INVALID_REGNUM if none.
+
+ In some cases we don't always need to pop all registers in the push
+ candidates, pop candidates record which registers need to be popped
+ eventually. The initial value of a pop candidate is copied from its
+ corresponding push candidate.
+
+ Currently, different pop candidates are only used for shadow call
+ stack. When "-fsanitize=shadow-call-stack" is specified, we replace
+ x30 in the pop candidate with INVALID_REGNUM to ensure that x30 is
+ not popped twice. */
+ unsigned wb_push_candidate1;
+ unsigned wb_push_candidate2;
+ unsigned wb_pop_candidate1;
+ unsigned wb_pop_candidate2;
/* Big-endian SVE frames need a spare predicate register in order
to save vector registers in the correct layout for unwinding.
@@ -932,6 +944,9 @@ struct GTY (()) aarch64_frame
unsigned spare_pred_reg;
bool laid_out;
+
+ /* True if shadow call stack should be enabled for the current function. */
+ bool is_scs_enabled;
};
typedef struct GTY (()) machine_function
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 5909184..c985250 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -7093,6 +7093,16 @@
"hint\t7 // xpaclri"
)
+;; Save X30 in the X18-based POST_INC stack (consistent with clang).
+(define_expand "scs_push"
+ [(set (mem:DI (post_inc:DI (reg:DI R18_REGNUM)))
+ (reg:DI R30_REGNUM))])
+
+;; Load X30 form the X18-based PRE_DEC stack (consistent with clang).
+(define_expand "scs_pop"
+ [(set (reg:DI R30_REGNUM)
+ (mem:DI (pre_dec:DI (reg:DI R18_REGNUM))))])
+
;; 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/doc/invoke.texi b/gcc/doc/invoke.texi
index e1a00c8..635c5f7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -15620,6 +15620,36 @@ 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 restoring 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. It is specifically
+designed for linux kernels that enable the CONFIG_SHADOW_CALL_STACK option.
+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 @code{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} might clobber @code{x18} and so corrupt the shadow
+stack pointer.
+
+Also, because there is no userspace runtime support, code compiled with
+ShadowCallStack cannot use exception handling. Use @option{-fno-exceptions}
+to turn off exceptions.
+
+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.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 962bbb8..49864dd 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -12596,3 +12596,8 @@ counters are incremented using atomic operations. Targets not supporting
64-bit atomic operations may override the default value and request a 32-bit
type.
@end deftypefn
+
+@deftypevr {Target Hook} bool TARGET_HAVE_SHADOW_CALL_STACK
+This value is true if the target platform supports
+@option{-fsanitize=shadow-call-stack}. The default value is false.
+@end deftypevr
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 394b59e..95e5e34 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8181,3 +8181,5 @@ maintainer is familiar with.
@hook TARGET_MEMTAG_UNTAGGED_POINTER
@hook TARGET_GCOV_TYPE_SIZE
+
+@hook TARGET_HAVE_SHADOW_CALL_STACK
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index bb0696e..2c84981 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -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
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 6e2ee82..19c68ae 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2017,6 +2017,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 }
@@ -2143,7 +2144,8 @@ parse_sanitizer_options (const char *p, location_t loc, int scode,
}
else
flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
- | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+ | SANITIZE_UNREACHABLE | SANITIZE_RETURN
+ | SANITIZE_SHADOW_CALL_STACK);
}
else if (value)
{
diff --git a/gcc/target.def b/gcc/target.def
index 57e64b2..72c2e1e 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -7096,6 +7096,14 @@ counters are incremented using atomic operations. Targets not supporting\n\
type.",
HOST_WIDE_INT, (void), default_gcov_type_size)
+/* This value represents whether the shadow call stack is implemented on
+ the target platform. */
+DEFHOOKPOD
+(have_shadow_call_stack,
+ "This value is true if the target platform supports\n\
+@option{-fsanitize=shadow-call-stack}. The default value is false.",
+ bool, false)
+
/* Close the 'struct gcc_target' definition. */
HOOK_VECTOR_END (C90_EMPTY_HACK)
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_1.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_1.c
new file mode 100644
index 0000000..ab68d6e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_1.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shadow-call-stack -fno-exceptions" } */
+
+int i;
+
+/* { dg-error "'-fsanitize=shadow-call-stack' requires '-ffixed-x18'" "" {target "aarch64*-*-*" } 0 } */
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_2.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_2.c
new file mode 100644
index 0000000..b5139a2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_2.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shadow-call-stack -ffixed-x18 -fexceptions" } */
+
+int i;
+
+/* { dg-error "'-fsanitize=shadow-call-stack' requires '-fno-exceptions'" "" {target "aarch64*-*-*" } 0 } */
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_3.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_3.c
new file mode 100644
index 0000000..b88e490
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_3.c
@@ -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 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_4.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_4.c
new file mode 100644
index 0000000..f631693
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_4.c
@@ -0,0 +1,20 @@
+/* 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 -fno-omit-frame-pointer -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\]!} } } */
+/* { dg-final { scan-assembler-times {stp\tx29, x30, \[sp, -[0-9]+\]!} 1 } } */
+/* { dg-final { scan-assembler-times {ldp\tx29, x30, \[sp\], [0-9]+} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_5.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_5.c
new file mode 100644
index 0000000..d88357c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_5.c
@@ -0,0 +1,18 @@
+/* Verify:
+ * -fno-omit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18.
+ * without outgoing.
+ * total frame size <= 512 but > 256.
+ * callee-saved reg: x29, x30.
+ * optimized code should use "stp x29, x30, [sp]" to save frame chain.
+ * optimized code should use "ldr x29, [sp]" to restore x29 only. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-omit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18 --save-temps" } */
+
+#include "test_frame_common.h"
+
+t_frame_pattern (func1, 400, )
+
+/* { dg-final { scan-assembler-times {stp\tx29, x30, \[sp\]} 1 } } */
+/* { dg-final { scan-assembler {ldr\tx29, \[sp\]} } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_6.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_6.c
new file mode 100644
index 0000000..83b7483
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_6.c
@@ -0,0 +1,18 @@
+/* Verify:
+ * -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18.
+ * without outgoing.
+ * total frame size <= 256.
+ * callee-saved reg: x30 only.
+ * optimized code should use "str x30, [sp]" to save x30 in prologue.
+ * optimized code should not restore x30 in epilogue. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18 --save-temps" } */
+
+#include "test_frame_common.h"
+
+t_frame_pattern (func1, 200, )
+
+/* { dg-final { scan-assembler-times {str\tx30, \[sp\]} 1 } } */
+/* { dg-final { scan-assembler-not {ld[r|p]\tx30, \[sp} } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_7.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_7.c
new file mode 100644
index 0000000..5537fb3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_7.c
@@ -0,0 +1,18 @@
+/* Verify:
+ * -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18.
+ * without outgoing.
+ * total frame size <= 256.
+ * callee-saved reg: x19, x30.
+ * optimized code should use "stp x19, x30, [sp, -x]!" to save x19, x30 in prologue.
+ * optimized code should use "ldr x19, [sp], x" to restore x19 only. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18 --save-temps" } */
+
+#include "test_frame_common.h"
+
+t_frame_pattern (func1, 200, "x19")
+
+/* { dg-final { scan-assembler-times {stp\tx19, x30, \[sp, -[0-9]+\]!} 1 } } */
+/* { dg-final { scan-assembler {ldr\tx19, \[sp\], [0-9]+} } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_8.c b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_8.c
new file mode 100644
index 0000000..b03f26f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/shadow_call_stack_8.c
@@ -0,0 +1,24 @@
+/* Verify:
+ * -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18.
+ * without outgoing.
+ * total frame <= 512 but > 256.
+ * callee-saved reg: x19, x20, x30.
+ * optimized code should use "stp x19, x20, [sp, -x]!" to save x19, x20 in prologue.
+ * optimized code should use "str x30, [sp " to save x30 in prologue.
+ * optimized code should use "ldp x19, x20, [sp], x" to retore x19, x20 in epilogue.
+ * optimized code should not restore x30 in epilogue. */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -fomit-frame-pointer -fsanitize=shadow-call-stack -fno-exceptions -ffixed-x18 --save-temps" } */
+
+int func1 (void)
+{
+ unsigned char a[200];
+ __asm__ ("":::"x19","x20","x30");
+ return 0;
+}
+
+/* { dg-final { scan-assembler-times {stp\tx19, x20, \[sp, -[0-9]+\]!} 1 } } */
+/* { dg-final { scan-assembler-times {str\tx30, \[sp} 1 } } */
+/* { dg-final { scan-assembler {ldp\tx19, x20, \[sp\], [0-9]+} } } */
+/* { dg-final { scan-assembler-not {ld[r|p]\tx30, \[sp} } } */
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index ea27e44..2d432fb 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -1679,6 +1679,16 @@ process_options (bool no_backend)
flag_sanitize &= ~SANITIZE_HWADDRESS;
}
+ if (flag_sanitize & SANITIZE_SHADOW_CALL_STACK)
+ {
+ if (!targetm.have_shadow_call_stack)
+ sorry ("%<-fsanitize=shadow-call-stack%> not supported "
+ "in current platform");
+ else if (flag_exceptions)
+ error_at (UNKNOWN_LOCATION, "%<-fsanitize=shadow-call-stack%> "
+ "requires %<-fno-exceptions%>");
+ }
+
HOST_WIDE_INT patch_area_size, patch_area_start;
parse_and_check_patch_area (flag_patchable_function_entry, false,
&patch_area_size, &patch_area_start);
diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc
index 5641d3c..a858994 100644
--- a/gcc/ubsan.cc
+++ b/gcc/ubsan.cc
@@ -832,8 +832,8 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
else
{
enum built_in_function bcode
- = (flag_sanitize_recover & ((check_align ? SANITIZE_ALIGNMENT : 0)
- | (check_null ? SANITIZE_NULL : 0)))
+ = (flag_sanitize_recover & ((check_align ? SANITIZE_ALIGNMENT + 0 : 0)
+ | (check_null ? SANITIZE_NULL + 0 : 0)))
? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_V1
: BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_V1_ABORT;
tree fn = builtin_decl_implicit (bcode);