This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH][RFA/RFC] Stack clash mitigation patch 02/08


The key goal in this patch is to introduce the new probing style for
dynamically allocated stack space and indirect uses of
STACK_CHECK_PROTECT via get_stack_check_protect().

Those two changes accomplish two things.  First it gives most targets
protection of dynamically allocated space (exceptions are targets which
expanders to allocate dynamic stack space such as ppc).

Second, targets which are not covered by -fstack-check=clash prologues
later, but which are covered by -fstack-check=specific get a fair amount
of protection.

We essentially vector into a totally different routine to allocate/probe
the dynamic stack space when -fstack-check=clash is active.  It differs
from the existing routine is that it allocates PROBE_INTERVAL chunks and
probes them as they are allocated.  The existing code would allocate the
entire space as a single hunk, then probe PROBE_INTERVAL chunks within
the hunk.

That routine is never presented with constant allocations on x86, but is
presented with constant allocations on other architectures.  It will
optimize cases when it knows it does not need the loop or the residual
allocation after the loop.   It does not have an unrolled loop mode, but
one could be added -- it didn't seem worth the effort.

The test will check that the loop is avoided for one case where it makes
sense.  It does not check for avoiding the residual allocation, but it
could probably be made to do so.


The indirection for STACK_CHECK_PROTECT via get_stack_protect is worth
some further discussion as well.

Early in the development of the stack-clash mitigation patches we
thought we could get away with re-using much of the existing target code
for -stack-check=specific.

Essentially that code starts a probing loop at STACK_CHECK_PROTECT and
probes 2-3 pages beyond the current function's needs.  The problem was
that starting at STACK_CHECK_PROTECT would skip probes in the first
couple pages leaving the code vulnerable.

So the idea was to avoid using STACK_CHECK_PROTECT directly.  Instead we
would indirect through a new function (get_stack_check_protect) which
would return either 0 or STACK_CHECK_PROTECT depending on whether or not
we wanted -fstack-check=clash or -fstack-check=specific respectively.

That scheme works reasonably well.  Except that it will tend to allocate
a large (larger than PROBE_INTERVAL) chunk of memory at once, then go
back and probe regions of PROBE_INTERVAL size.  That introduces an
unfortunate race condition with asynch signals and also crashes valgrind
on ppc and aarch64.

Rather than throw that code away, it may still be valuable to those
targets with -fstack-check=specific support, but without
-fstack-check=clash support.  So I'm including it here.


Thoughts/comments?  Ok for the trunk?


	* defaults.h (STACK_CHECK_MOVING_SP): Enable with -fstack-check=clash
	* explow.c (anti_adjust_stack_and_probe_stack_clash): New function.
	(get_stack_check_protect): Likewise.
	(allocate_dynamic_stack_space): Use new functions.
	* rtl.h (get_stack_check_protect): Prototype.
	* config/aarch64/aarch64.c (aarch64_expand_prologue): Use
	get_stack_check_protect.
	* config/alpha/alpha.c (alpha_expand_prologue): Likewise.
	* config/arm/arm.c (arm_expand_prologue): Likewise.
	* config/i386/i386.c (ix86_expand_prologue): Likewise.
	* config/ia64/ia64.c (ia64_expand_prologue): Likewise.
	* conifg/mips/mips.c (mips_expand_prologue): Likewise.
	* config/powerpcspe/powerpcspe.c (rs6000_emit_prologue): Likewise.
	* config/rs6000/rs6000.c (rs6000_emit_prologue): Likewise.
	* config/sparc/sparc.c (sparc_expand_prologue): Likewise.


testsuite

	* gcc.dg/stack-check-3.c: New test.
commit cddc77979e4183769e1817676c3b449d8c8cb202
Author: Jeff Law <law@torsion.usersys.redhat.com>
Date:   Wed Jun 28 14:02:16 2017 -0400

    Use stack-clash probing if requested for alloca space
    Use MOVING_SP by default for stack-clash probing
    Simple test for dynamic allocations + probing
    
    stack-check-3 fixes for improvements in dynamic area probe loop elision
    use dg-requires...  do not optimizing sibling calls
    
    Indirect for STACK_CHECK_PROTECT

diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index ef1b5a8..0a8b40a 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -3676,12 +3676,14 @@ aarch64_expand_prologue (void)
     {
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (frame_size > PROBE_INTERVAL && frame_size > STACK_CHECK_PROTECT)
-	    aarch64_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					    frame_size - STACK_CHECK_PROTECT);
+	  if (frame_size > PROBE_INTERVAL
+	      && frame_size > get_stack_check_protect ())
+	    aarch64_emit_probe_stack_range (get_stack_check_protect (),
+					    (frame_size
+					     - get_stack_check_protect ()));
 	}
       else if (frame_size > 0)
-	aarch64_emit_probe_stack_range (STACK_CHECK_PROTECT, frame_size);
+	aarch64_emit_probe_stack_range (get_stack_check_protect (), frame_size);
     }
 
   aarch64_sub_sp (IP0_REGNUM, initial_adjust, true);
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index 00a69c1..91f3d7c 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -7741,7 +7741,7 @@ alpha_expand_prologue (void)
 
   probed_size = frame_size;
   if (flag_stack_check)
-    probed_size += STACK_CHECK_PROTECT;
+    probed_size += get_stack_check_protect ();
 
   if (probed_size <= 32768)
     {
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index c6101ef..9822ca7 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -21680,13 +21680,13 @@ arm_expand_prologue (void)
 
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    arm_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					size - STACK_CHECK_PROTECT,
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    arm_emit_probe_stack_range (get_stack_check_protect (),
+					size - get_stack_check_protect (),
 					regno, live_regs_mask);
 	}
       else if (size > 0)
-	arm_emit_probe_stack_range (STACK_CHECK_PROTECT, size,
+	arm_emit_probe_stack_range (get_stack_check_protect (), size,
 				    regno, live_regs_mask);
     }
 
@@ -27854,7 +27854,7 @@ arm_frame_pointer_required (void)
 	{
 	  /* We don't have the final size of the frame so adjust.  */
 	  size += 32 * UNITS_PER_WORD;
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
 	    return true;
 	}
       else
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 1a8a3a3..0947b3c 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -14638,7 +14638,7 @@ ix86_expand_prologue (void)
 	  HOST_WIDE_INT size = allocate;
 
 	  if (TARGET_64BIT && size >= HOST_WIDE_INT_C (0x80000000))
-	    size = 0x80000000 - STACK_CHECK_PROTECT - 1;
+	    size = 0x80000000 - get_stack_check_protect () - 1;
 
 	  if (TARGET_STACK_PROBE)
 	    {
@@ -14648,18 +14648,20 @@ ix86_expand_prologue (void)
 		    ix86_emit_probe_stack_range (0, size);
 		}
 	      else
-		ix86_emit_probe_stack_range (0, size + STACK_CHECK_PROTECT);
+		ix86_emit_probe_stack_range (0,
+					     size + get_stack_check_protect ());
 	    }
 	  else
 	    {
 	      if (crtl->is_leaf && !cfun->calls_alloca)
 		{
-		  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-		    ix86_emit_probe_stack_range (STACK_CHECK_PROTECT,
-						 size - STACK_CHECK_PROTECT);
+		  if (size > PROBE_INTERVAL
+		      && size > get_stack_check_protect ())
+		    ix86_emit_probe_stack_range (get_stack_check_protect (),
+						 size - get_stack_check_protect ());
 		}
 	      else
-		ix86_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+		ix86_emit_probe_stack_range (get_stack_check_protect (), size);
 	    }
 	}
     }
diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c
index 617d188..70aef34 100644
--- a/gcc/config/ia64/ia64.c
+++ b/gcc/config/ia64/ia64.c
@@ -3481,15 +3481,16 @@ ia64_expand_prologue (void)
 
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    ia64_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					 size - STACK_CHECK_PROTECT,
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    ia64_emit_probe_stack_range (get_stack_check_protect (),
+					 size - get_stack_check_protect (),
 					 bs_size);
-	  else if (size + bs_size > STACK_CHECK_PROTECT)
-	    ia64_emit_probe_stack_range (STACK_CHECK_PROTECT, 0, bs_size);
+	  else if (size + bs_size > get_stack_check_protect ())
+	    ia64_emit_probe_stack_range (get_stack_check_protect (),
+					 0, bs_size);
 	}
       else if (size + bs_size > 0)
-	ia64_emit_probe_stack_range (STACK_CHECK_PROTECT, size, bs_size);
+	ia64_emit_probe_stack_range (get_stack_check_protect (), size, bs_size);
     }
 
   if (dump_file) 
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 6bfd86a..7d85ce7 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -12081,12 +12081,12 @@ mips_expand_prologue (void)
     {
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    mips_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					 size - STACK_CHECK_PROTECT);
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    mips_emit_probe_stack_range (get_stack_check_protect (),
+					 size - get_stack_check_protect ());
 	}
       else if (size > 0)
-	mips_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+	mips_emit_probe_stack_range (get_stack_check_protect (), size);
     }
 
   /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
diff --git a/gcc/config/powerpcspe/powerpcspe.c b/gcc/config/powerpcspe/powerpcspe.c
index 06d66d7..df5d3cd 100644
--- a/gcc/config/powerpcspe/powerpcspe.c
+++ b/gcc/config/powerpcspe/powerpcspe.c
@@ -29597,12 +29597,12 @@ rs6000_emit_prologue (void)
 
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					   size - STACK_CHECK_PROTECT);
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    rs6000_emit_probe_stack_range (get_stack_check_protect (),
+					   size - get_stack_check_protect ());
 	}
       else if (size > 0)
-	rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+	rs6000_emit_probe_stack_range (get_stack_check_protect (), size);
     }
 
   if (TARGET_FIX_AND_CONTINUE)
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 63a6c80..aa70e30 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -26895,12 +26895,12 @@ rs6000_emit_prologue (void)
 
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					   size - STACK_CHECK_PROTECT);
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    rs6000_emit_probe_stack_range (get_stack_check_protect (),
+					   size - get_stack_check_protect ());
 	}
       else if (size > 0)
-	rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+	rs6000_emit_probe_stack_range (get_stack_check_protect (), size);
     }
 
   if (TARGET_FIX_AND_CONTINUE)
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 790a036..1da032a 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -5552,12 +5552,12 @@ sparc_expand_prologue (void)
     {
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    sparc_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					  size - STACK_CHECK_PROTECT);
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    sparc_emit_probe_stack_range (get_stack_check_protect (),
+					  size - get_stack_check_protect ());
 	}
       else if (size > 0)
-	sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+	sparc_emit_probe_stack_range (get_stack_check_protect (), size);
     }
 
   if (size == 0)
@@ -5663,12 +5663,12 @@ sparc_flat_expand_prologue (void)
     {
       if (crtl->is_leaf && !cfun->calls_alloca)
 	{
-	  if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
-	    sparc_emit_probe_stack_range (STACK_CHECK_PROTECT,
-					  size - STACK_CHECK_PROTECT);
+	  if (size > PROBE_INTERVAL && size > get_stack_check_protect ())
+	    sparc_emit_probe_stack_range (get_stack_check_protect (),
+					  size - get_stack_check_protect ());
 	}
       else if (size > 0)
-	sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size);
+	sparc_emit_probe_stack_range (get_stack_check_protect (), size);
     }
 
   if (sparc_save_local_in_regs_p)
diff --git a/gcc/defaults.h b/gcc/defaults.h
index 7ad92d9..7c612a4 100644
--- a/gcc/defaults.h
+++ b/gcc/defaults.h
@@ -1408,8 +1408,11 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #endif
 
 /* The default is not to move the stack pointer.  */
+/* The default is not to move the stack pointer, unless we are using
+   stack clash prevention stack checking.  */
 #ifndef STACK_CHECK_MOVING_SP
-#define STACK_CHECK_MOVING_SP 0
+#define STACK_CHECK_MOVING_SP\
+  (flag_stack_check == STACK_CLASH_BUILTIN_STACK_CHECK)
 #endif
 
 /* This is a kludge to try to capture the discrepancy between the old
diff --git a/gcc/explow.c b/gcc/explow.c
index 50074e2..bfa21bd 100644
--- a/gcc/explow.c
+++ b/gcc/explow.c
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 
 static rtx break_out_memory_refs (rtx);
+static void anti_adjust_stack_and_probe_stack_clash (rtx);
 
 
 /* Truncate and perhaps sign-extend C as appropriate for MODE.  */
@@ -1272,6 +1273,25 @@ get_dynamic_stack_size (rtx *psize, unsigned size_align,
   *psize = size;
 }
 
+/* Return the number of bytes to protect on the stack for -fstack-check.
+
+   The default is to protect STACK_CHECK_PROTECT bytes which should be
+   enough to handle a signal.
+
+   When mitigating stack clash style attacks we do not save enough
+   space to handle a signal, so we protect zero bytes.
+
+   The distinction is important because it determines both how far beyond
+   current need we probe the stack and it determines how many bytes are
+   assumed to have already been checked by prior callers in the call chain.  */
+HOST_WIDE_INT
+get_stack_check_protect (void)
+{
+  if (flag_stack_check == STACK_CLASH_BUILTIN_STACK_CHECK)
+    return 0;
+ return STACK_CHECK_PROTECT;
+}
+
 /* Return an rtx representing the address of an area of memory dynamically
    pushed on the stack.
 
@@ -1430,7 +1450,7 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
     probe_stack_range (STACK_OLD_CHECK_PROTECT + STACK_CHECK_MAX_FRAME_SIZE,
 		       size);
   else if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK)
-    probe_stack_range (STACK_CHECK_PROTECT, size);
+    probe_stack_range (get_stack_check_protect (), size);
 
   /* Don't let anti_adjust_stack emit notes.  */
   suppress_reg_args_size = true;
@@ -1482,7 +1502,12 @@ allocate_dynamic_stack_space (rtx size, unsigned size_align,
       saved_stack_pointer_delta = stack_pointer_delta;
 
       if (flag_stack_check && STACK_CHECK_MOVING_SP)
-	anti_adjust_stack_and_probe (size, false);
+	{
+	  if (flag_stack_check == STACK_CLASH_BUILTIN_STACK_CHECK)
+	    anti_adjust_stack_and_probe_stack_clash (size);
+	  else
+	    anti_adjust_stack_and_probe (size, false);
+	}
       else
 	anti_adjust_stack (size);
 
@@ -1760,6 +1785,126 @@ probe_stack_range (HOST_WIDE_INT first, rtx size)
 
 /* Adjust the stack pointer by minus SIZE (an rtx for a number of bytes)
    while probing it.  This pushes when SIZE is positive.  SIZE need not
+   be constant.
+
+   This is subtly different than anti_adjust_stack_and_probe to try and
+   prevent attacks that jump the stack guard.
+
+     1. It assumes the prologue did not probe any residual stack allocation,
+	Thus the stack pointer could currently be in the guard page and if
+	this call results in any allocation, it must be probed.
+
+     2. It never skips probes, whereas anti_adjust_stack_and_probe will
+	skip probes on the first couple PROBE_INTERVALs on the assumption
+	they're done elsewhere.
+
+     3. It only allocates and probes SIZE bytes, it does not need to
+	allocate/probe beyond that because this probing style does not
+	guarantee signal handling capability if the guard is hit.
+
+     4. It does not bother handling constant allocations.  They do not
+	happen in practice here.  */
+
+static void
+anti_adjust_stack_and_probe_stack_clash (rtx size)
+{
+  /* First ensure SIZE is Pmode.  */
+  if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
+    size = convert_to_mode (Pmode, size, 1);
+
+  /* We can get here with a constant size on some targets.  But it is not
+     worth having paths for small, medium and large allocations.
+
+     We detect the cases where we can avoid the loop entirely or avoid the
+     residuals entirely.  */
+
+  /* Step 1: round SIZE to the previous multiple of the interval.  */
+
+  /* ROUNDED_SIZE = SIZE & -PROBE_INTERVAL  */
+  rtx rounded_size
+    = simplify_gen_binary (AND, Pmode, size, GEN_INT (-PROBE_INTERVAL));
+  rtx rounded_size_op = force_operand (rounded_size, NULL_RTX);
+
+  if (rounded_size != CONST0_RTX (Pmode))
+    {
+
+      /* Step 2: compute final value of the loop counter.  */
+
+      /* LAST_ADDR = SP + ROUNDED_SIZE.  */
+      rtx last_addr = force_operand (gen_rtx_fmt_ee (STACK_GROW_OP, Pmode,
+						     stack_pointer_rtx,
+						     rounded_size_op),
+				     NULL_RTX);
+
+
+      /* Step 3: the loop
+
+         while (SP != LAST_ADDR)
+	   {
+	     SP = SP + PROBE_INTERVAL
+	     probe at SP - PROBE_INTERVAL + small constant
+           }
+
+	 adjusts SP and probes into the newly allocated space as closely
+	 to the original SP as possible, iterating until LAST_ADDR is hit.  */
+
+      rtx loop_lab = gen_label_rtx ();
+      rtx end_lab = gen_label_rtx ();
+
+      emit_label (loop_lab);
+
+      /* Jump to END_LAB if SP == LAST_ADDR.  */
+      emit_cmp_and_jump_insns (stack_pointer_rtx, last_addr, EQ, NULL_RTX,
+			       Pmode, 1, end_lab);
+
+      /* SP = SP + PROBE_INTERVAL and probe at SP.  */
+      anti_adjust_stack (GEN_INT (PROBE_INTERVAL));
+
+      /* The prologue does not probe residuals.  Thus the offset here
+	 to probe just beyond what the prologue had already allocated.  */
+      emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+				       (PROBE_INTERVAL
+					- GET_MODE_SIZE (word_mode))));
+
+      emit_jump (loop_lab);
+
+      emit_label (end_lab);
+
+      /* This is primarily to make writing tests easy.  */
+      if (dump_file)
+	fprintf (dump_file,
+		 "Stack clash dynamic allocation and probing in loop.\n");
+    }
+  else if (dump_file)
+    fprintf (dump_file,
+	     "Stack clash skipped dynamic allocation "
+	     "and probing loop.\n");
+
+  /* Step 4: adjust SP if we cannot assert at compile-time that
+     SIZE is equal to ROUNDED_SIZE.  */
+
+  /* TEMP = SIZE - ROUNDED_SIZE.  */
+  rtx temp = simplify_gen_binary (MINUS, Pmode, size, rounded_size);
+  if (temp != const0_rtx)
+    {
+      /* Manual CSE if the difference is not known at compile-time.  */
+      if (GET_CODE (temp) != CONST_INT)
+	temp = gen_rtx_MINUS (Pmode, size, rounded_size_op);
+      anti_adjust_stack (temp);
+      rtx x = force_reg (Pmode, plus_constant (Pmode, temp,
+					       -GET_MODE_SIZE (word_mode)));
+      emit_stack_probe (gen_rtx_PLUS (Pmode, stack_pointer_rtx, x));
+
+      /* This is primarily to make writing tests easy.  */
+      if (dump_file)
+	fprintf (dump_file,
+		 "Stack clash dynamic allocation and probing residuals.\n");
+    }
+}
+
+
+/* Adjust the stack pointer by minus SIZE (an rtx for a number of bytes)
+   while probing it.  This pushes when SIZE is positive.  SIZE need not
    be constant.  If ADJUST_BACK is true, adjust back the stack pointer
    by plus SIZE at the end.  */
 
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 59da995..24240fc 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -2703,6 +2703,7 @@ get_full_set_src_cost (rtx x, machine_mode mode, struct full_rtx_costs *c)
 /* In explow.c */
 extern HOST_WIDE_INT trunc_int_for_mode	(HOST_WIDE_INT, machine_mode);
 extern rtx plus_constant (machine_mode, rtx, HOST_WIDE_INT, bool = false);
+extern HOST_WIDE_INT get_stack_check_protect (void);
 
 /* In rtl.c */
 extern rtx rtx_alloc_stat (RTX_CODE MEM_STAT_DECL);
diff --git a/gcc/testsuite/gcc.dg/stack-check-3.c b/gcc/testsuite/gcc.dg/stack-check-3.c
new file mode 100644
index 0000000..9d45db2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/stack-check-3.c
@@ -0,0 +1,75 @@
+/* The goal here is to ensure that dynamic allocations via vlas or
+   alloca calls receive probing.
+
+   Scanning the RTL or assembly code seems like insanity here as does
+   checking for particular allocation sizes and probe offsets.  For
+   now we just verify that there's an allocation + probe loop and
+   residual allocation + probe for f?.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fstack-check=clash -fdump-rtl-expand -fno-optimize-sibling-calls" } */
+/* { dg-require-effective-target stack_clash_protected } */
+
+__attribute__((noinline, noclone)) void
+foo (char *p)
+{
+  asm volatile ("" : : "r" (p) : "memory");
+}
+
+/* Simple VLA, no other locals. */
+__attribute__((noinline, noclone)) void
+f0 (int x)
+{
+  char vla[x];
+  foo (vla);
+}
+
+/* Simple VLA, small local frame.  */
+__attribute__((noinline, noclone)) void
+f1 (int x)
+{
+  char locals[128];
+  char vla[x];
+  foo (vla);
+}
+
+/* Small constant alloca, no other locals. */
+__attribute__((noinline, noclone)) void
+f2 (int x)
+{
+  char *vla = __builtin_alloca (128);
+  foo (vla);
+}
+
+/* Big constant alloca, small local frame.  */
+__attribute__((noinline, noclone)) void
+f3 (int x)
+{
+  char locals[128];
+  char *vla = __builtin_alloca (16384);
+  foo (vla);
+}
+
+/* Nonconstant alloca, no other locals. */
+__attribute__((noinline, noclone)) void
+f4 (int x)
+{
+  char *vla = __builtin_alloca (x);
+  foo (vla);
+}
+
+/* Nonconstant alloca, small local frame.  */
+__attribute__((noinline, noclone)) void
+f5 (int x)
+{
+  char locals[128];
+  char *vla = __builtin_alloca (x);
+  foo (vla);
+}
+
+/* { dg-final { scan-rtl-dump-times "allocation and probing residuals" 6 "expand" } } */
+
+
+/* { dg-final { scan-rtl-dump-times "allocation and probing in loop" 6 "expand" { target callee_realigns_stack } } } */
+/* { dg-final { scan-rtl-dump-times "allocation and probing in loop" 5 "expand" { target { ! callee_realigns_stack } } } } */
+/* { dg-final { scan-rtl-dump-times "skipped dynamic allocation and probing loop" 1 "expand" { target { ! callee_realigns_stack } } } } */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]