common.opt (-fstack-clash-protection): New option.
authorJeff Law <law@redhat.com>
Wed, 20 Sep 2017 04:56:54 +0000 (22:56 -0600)
committerJeff Law <law@gcc.gnu.org>
Wed, 20 Sep 2017 04:56:54 +0000 (22:56 -0600)
* common.opt (-fstack-clash-protection): New option.
* flag-types.h (enum stack_check_type): Note difference between
-fstack-check= and -fstack-clash-protection.
* params.def (PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE): New PARAM.
(PARAM_STACK_CLASH_PROTECTION_PROBE_INTERVAL): Likewise.
* toplev.c (process_options): Issue warnings/errors for cases
not handled with -fstack-clash-protection.
* doc/invoke.texi (-fstack-clash-protection): Document new option.
(-fstack-check): Note additional problem with -fstack-check=generic.
Note that -fstack-check is primarily for Ada and refer users
to -fstack-clash-protection for stack-clash-protection.
Document new params for stack clash protection.

* gcc.dg/stack-check-2.c: New test.
* lib/target-supports.exp
(check_effective_target_supports_stack_clash_protection): New function.
(check_effective_target_frame_pointer_for_non_leaf): Likewise.
(check_effective_target_caller_implicit_probes): Likewise.

From-SVN: r252994

gcc/ChangeLog
gcc/common.opt
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/params.def
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/stack-check-2.c [new file with mode: 0644]
gcc/testsuite/lib/target-supports.exp
gcc/toplev.c

index 9576c8c4f4773a7b2f669350d49220fef8453733..a1aa3f6327d4e97ed91890d4e5100da6cf5b82dd 100644 (file)
@@ -1,3 +1,18 @@
+2017-09-19  Jeff Law  <law@redhat.com>
+
+       * common.opt (-fstack-clash-protection): New option.
+       * flag-types.h (enum stack_check_type): Note difference between
+       -fstack-check= and -fstack-clash-protection.
+       * params.def (PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE): New PARAM.
+       (PARAM_STACK_CLASH_PROTECTION_PROBE_INTERVAL): Likewise.
+       * toplev.c (process_options): Issue warnings/errors for cases
+       not handled with -fstack-clash-protection.
+       * doc/invoke.texi (-fstack-clash-protection): Document new option.
+       (-fstack-check): Note additional problem with -fstack-check=generic.
+       Note that -fstack-check is primarily for Ada and refer users
+       to -fstack-clash-protection for stack-clash-protection.
+       Document new params for stack clash protection.
+
 2017-09-19  Uros Bizjak  <ubizjak@gmail.com>
 
        * config/i386/i386.md (*scc_bt<mode>): New insn_and_split pattern.
index fa6dd845d54f5fa2563202c62db6d06b0ad05dff..f22661cfbba7f9e7a4265a545838993674985007 100644 (file)
@@ -2320,13 +2320,18 @@ Common Report Var(flag_variable_expansion_in_unroller) Optimization
 Apply variable expansion when loops are unrolled.
 
 fstack-check=
-Common Report RejectNegative Joined
+Common Report RejectNegative Joined Optimization
 -fstack-check=[no|generic|specific]    Insert stack checking code into the program.
 
 fstack-check
 Common Alias(fstack-check=, specific, no)
 Insert stack checking code into the program.  Same as -fstack-check=specific.
 
+fstack-clash-protection
+Common Report Var(flag_stack_clash_protection) Optimization
+Insert code to probe each page of stack space as it is allocated to protect
+from stack-clash style attacks.
+
 fstack-limit
 Common Var(common_deferred_options) Defer
 
index 204c9b77b612b4a9638c99112cfba566d1dd0418..f0f9559b0243975f3fb2f72e9df23ac1209fc3c0 100644 (file)
@@ -10187,6 +10187,21 @@ compilation without.  The value for compilation with profile feedback
 needs to be more conservative (higher) in order to make tracer
 effective.
 
+@item stack-clash-protection-guard-size
+Specify the size of the operating system provided stack guard as
+2 raised to @var{num} bytes.  The default value is 12 (4096 bytes).
+Acceptable values are between 12 and 30.  Higher values may reduce the
+number of explicit probes, but a value larger than the operating system
+provided guard will leave code vulnerable to stack clash style attacks.
+
+@item stack-clash-protection-probe-interval
+Stack clash protection involves probing stack space as it is allocated.  This
+param controls the maximum distance between probes into the stack as 2 raised
+to @var{num} bytes.  Acceptable values are between 10 and 16 and defaults to
+12.  Higher values may reduce the number of explicit probes, but a value
+larger than the operating system provided guard will leave code vulnerable to
+stack clash style attacks.
+
 @item max-cse-path-length
 
 The maximum number of basic blocks on path that CSE considers.
@@ -11412,7 +11427,8 @@ target support in the compiler but comes with the following drawbacks:
 @enumerate
 @item
 Modified allocation strategy for large objects: they are always
-allocated dynamically if their size exceeds a fixed threshold.
+allocated dynamically if their size exceeds a fixed threshold.  Note this
+may change the semantics of some code.
 
 @item
 Fixed limit on the size of the static frame of functions: when it is
@@ -11427,6 +11443,25 @@ generic implementation, code performance is hampered.
 Note that old-style stack checking is also the fallback method for
 @samp{specific} if no target support has been added in the compiler.
 
+@samp{-fstack-check=} is designed for Ada's needs to detect infinite recursion
+and stack overflows.  @samp{specific} is an excellent choice when compiling
+Ada code.  It is not generally sufficient to protect against stack-clash
+attacks.  To protect against those you want @samp{-fstack-clash-protection}.
+
+@item -fstack-clash-protection
+@opindex fstack-clash-protection
+Generate code to prevent stack clash style attacks.  When this option is
+enabled, the compiler will only allocate one page of stack space at a time
+and each page is accessed immediately after allocation.  Thus, it prevents
+allocations from jumping over any stack guard page provided by the
+operating system.
+
+Most targets do not fully support stack clash protection.  However, on
+those targets @option{-fstack-clash-protection} will protect dynamic stack
+allocations.  @option{-fstack-clash-protection} may also provide limited
+protection for static stack allocations if the target supports
+@option{-fstack-check=specific}.
+
 @item -fstack-limit-register=@var{reg}
 @itemx -fstack-limit-symbol=@var{sym}
 @itemx -fno-stack-limit
index 4938f69b1b6f5adad96243c8f2f1d454949bd201..1f439d35b07b6c1dbb84686ad8fe2d6083f0086a 100644 (file)
@@ -166,7 +166,14 @@ enum permitted_flt_eval_methods
   PERMITTED_FLT_EVAL_METHODS_C11
 };
 
-/* Type of stack check.  */
+/* Type of stack check.
+
+   Stack checking is designed to detect infinite recursion and stack
+   overflows for Ada programs.  Furthermore stack checking tries to ensure
+   in that scenario that enough stack space is left to run a signal handler.
+
+   -fstack-check= does not prevent stack-clash style attacks.  For that
+   you want -fstack-clash-protection.  */
 enum stack_check_type
 {
   /* Do not check the stack.  */
index 805302bb93e94cf7a57db590c5335c6116b49946..860e79e32093ced9b60148295c2fd6b99eebf940 100644 (file)
@@ -213,6 +213,16 @@ DEFPARAM(PARAM_STACK_FRAME_GROWTH,
         "Maximal stack frame growth due to inlining (in percent).",
         1000, 0, 0)
 
+DEFPARAM(PARAM_STACK_CLASH_PROTECTION_GUARD_SIZE,
+        "stack-clash-protection-guard-size",
+        "Size of the stack guard expressed as a power of two.",
+        12, 12, 30)
+
+DEFPARAM(PARAM_STACK_CLASH_PROTECTION_PROBE_INTERVAL,
+        "stack-clash-protection-probe-interval",
+        "Interval in which to probe the stack expressed as a power of two.",
+        12, 10, 16)
+
 /* The GCSE optimization will be disabled if it would require
    significantly more memory than this value.  */
 DEFPARAM(PARAM_MAX_GCSE_MEMORY,
index 011420575b27935a86b8cc060418120fad2d9f15..a77b54604e623558b4be346e07ecb8d508ae936d 100644 (file)
@@ -1,3 +1,11 @@
+2017-09-19  Jeff Law  <law@redhat.com>
+
+       * gcc.dg/stack-check-2.c: New test.
+       * lib/target-supports.exp
+       (check_effective_target_supports_stack_clash_protection): New function.
+       (check_effective_target_frame_pointer_for_non_leaf): Likewise.
+       (check_effective_target_caller_implicit_probes): Likewise.
+
 2017-09-19  Uros Bizjak  <ubizjak@gmail.com>
 
        * gcc.target/i386/bt-5.c: New test.
diff --git a/gcc/testsuite/gcc.dg/stack-check-2.c b/gcc/testsuite/gcc.dg/stack-check-2.c
new file mode 100644 (file)
index 0000000..196c4bb
--- /dev/null
@@ -0,0 +1,66 @@
+/* The goal here is to ensure that we never consider a call to a noreturn
+   function as a potential tail call.
+
+   Right now GCC discovers potential tail calls by looking at the
+   predecessors of the exit block.  A call to a non-return function
+   has no successors and thus can never match that first filter.
+
+   But that could change one day and we want to catch it.  The problem
+   is the compiler could potentially optimize a tail call to a nonreturn
+   function, even if the caller has a frame.  That breaks the assumption
+   that calls probe *sp when saving the return address that some targets
+   depend on to elide stack probes.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-clash-protection -fdump-tree-tailc -fdump-tree-optimized" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+extern void foo (void) __attribute__ ((__noreturn__));
+
+
+void
+test_direct_1 (void)
+{
+  foo ();
+}
+
+void
+test_direct_2 (void)
+{
+  return foo ();
+}
+
+void (*indirect)(void)__attribute__ ((noreturn));
+
+
+void
+test_indirect_1 ()
+{
+  (*indirect)();
+}
+
+void
+test_indirect_2 (void)
+{
+  return (*indirect)();;
+}
+
+
+typedef void (*pvfn)() __attribute__ ((noreturn));
+
+void (*indirect_casted)(void);
+
+void
+test_indirect_casted_1 ()
+{
+  (*(pvfn)indirect_casted)();
+}
+
+void
+test_indirect_casted_2 (void)
+{
+  return (*(pvfn)indirect_casted)();
+}
+/* { dg-final { scan-tree-dump-not "tail call" "tailc" } } */
+/* { dg-final { scan-tree-dump-not "tail call" "optimized" } } */
+
index 6ea71222c6e59cadc1248530c10e9154eca4e445..feed524680d8a73a270e93a7466a83c0fcc95b1d 100644 (file)
@@ -8621,3 +8621,80 @@ proc check_effective_target_autoincdec { } {
     }
     return 0
 }
+
+# Return 1 if the target has support for stack probing designed
+# to avoid stack-clash style attacks.
+#
+# This is used to restrict the stack-clash mitigation tests to
+# just those targets that have been explicitly supported.
+# 
+# In addition to the prologue work on those targets, each target's
+# properties should be described in the functions below so that
+# tests do not become a mess of unreadable target conditions.
+# 
+proc check_effective_target_supports_stack_clash_protection { } {
+
+   # Temporary until the target bits are fully ACK'd.
+#  if { [istarget aarch*-*-*] || [istarget x86_64-*-*]
+#       || [istarget i?86-*-*] || [istarget s390*-*-*]
+#       || [istarget powerpc*-*-*] || [istarget rs6000*-*-*] } {
+#      return 1
+#  }
+  return 0
+}
+
+# Return 1 if the target creates a frame pointer for non-leaf functions
+# Note we ignore cases where we apply tail call optimization here.
+proc check_effective_target_frame_pointer_for_non_leaf { } {
+  if { [istarget aarch*-*-*] } {
+       return 1
+  }
+  return 0
+}
+
+# Return 1 if the target's calling sequence or its ABI
+# create implicit stack probes at or prior to function entry.
+proc check_effective_target_caller_implicit_probes { } {
+
+  # On x86/x86_64 the call instruction itself pushes the return
+  # address onto the stack.  That is an implicit probe of *sp.
+  if { [istarget x86_64-*-*] || [istarget i?86-*-*] } {
+       return 1
+  }
+
+  # On PPC, the ABI mandates that the address of the outer
+  # frame be stored at *sp.  Thus each allocation of stack
+  # space is itself an implicit probe of *sp.
+  if { [istarget powerpc*-*-*] || [istarget rs6000*-*-*] } {
+       return 1
+  }
+
+  # s390's ABI has a register save area allocated by the
+  # caller for use by the callee.  The mere existence does
+  # not constitute a probe by the caller, but when the slots
+  # used by the callee those stores are implicit probes.
+  if { [istarget s390*-*-*] } {
+       return 1
+  }
+
+  # Not strictly true on aarch64, but we have agreed that we will
+  # consider any function that pushes SP more than 3kbytes into
+  # the guard page as broken.  This essentially means that we can
+  # consider the aarch64 as having a caller implicit probe at
+  # *(sp + 1k).
+  if { [istarget aarch64*-*-*] } {
+       return 1;
+  }
+
+  return 0
+}
+
+# Targets that potentially realign the stack pointer often cause residual
+# stack allocations and make it difficult to elimination loops or residual
+# allocations for dynamic stack allocations
+proc check_effective_target_callee_realigns_stack { } {
+  if { [istarget x86_64-*-*] || [istarget i?86-*-*] } {
+       return 1
+  }
+  return 0
+}
index 7d2b8fffa0bc545159302e7537beb098c616bedf..6f48e10850d22636c58405c137bbf89db6032d28 100644 (file)
@@ -1605,6 +1605,26 @@ process_options (void)
       flag_associative_math = 0;
     }
 
+  /* -fstack-clash-protection is not currently supported on targets
+     where the stack grows up.  */
+  if (flag_stack_clash_protection && !STACK_GROWS_DOWNWARD)
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+                 "%<-fstack-clash-protection%> is not supported on targets "
+                 "where the stack grows from lower to higher addresses");
+      flag_stack_clash_protection = 0;
+    }
+
+  /* We can not support -fstack-check= and -fstack-clash-protection at
+     the same time.  */
+  if (flag_stack_check != NO_STACK_CHECK && flag_stack_clash_protection)
+    {
+      warning_at (UNKNOWN_LOCATION, 0,
+                 "%<-fstack-check=%> and %<-fstack-clash_protection%> are "
+                 "mutually exclusive.  Disabling %<-fstack-check=%>");
+      flag_stack_check = NO_STACK_CHECK;
+    }
+
   /* With -fcx-limited-range, we do cheap and quick complex arithmetic.  */
   if (flag_cx_limited_range)
     flag_complex_method = 0;