diff mbox series

[5/5] RISC-V: Enable stack clash in alloca

Message ID 1206ea55e95fef9c6a10cc8b9a5e85e2ec8d78bc.1721681845.git.rzinsly@ventanamicro.com
State New
Headers show
Series RISC-V: Enable stack-clash protection | expand

Commit Message

Raphael Moreira Zinsly July 24, 2024, 6 p.m. UTC
Add the TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE to riscv in
order to enable stack clash protection when using alloca.
The code and tests are the same used by aarch64.

gcc/ChangeLog:
	* config/riscv/riscv.cc (riscv_compute_frame_info): Update
	  outgoing args size.
	  (riscv_stack_clash_protection_alloca_probe_range): New.
	  (TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE): New.
	* config/riscv/riscv.h
	  (STACK_CLASH_MIN_BYTES_OUTGOING_ARGS): New.
	  (STACK_DYNAMIC_OFFSET): New.

gcc/testsuite/ChangeLog:
	* gcc.target/riscv/stack-check-14.c: New test.
	* gcc.target/riscv/stack-check-15.c: New test.
	* gcc.target/riscv/stack-check-alloca-1.c: New test.
	* gcc.target/riscv/stack-check-alloca-2.c: New test.
	* gcc.target/riscv/stack-check-alloca-3.c: New test.
	* gcc.target/riscv/stack-check-alloca-4.c: New test.
	* gcc.target/riscv/stack-check-alloca-5.c: New test.
	* gcc.target/riscv/stack-check-alloca-6.c: New test.
	* gcc.target/riscv/stack-check-alloca-7.c: New test.
	* gcc.target/riscv/stack-check-alloca-8.c: New test.
	* gcc.target/riscv/stack-check-alloca-9.c: New test.
	* gcc.target/riscv/stack-check-alloca-10.c: New test.
	* gcc.target/riscv/stack-check-alloca.h: New.
---
 gcc/config/riscv/riscv.cc                     | 17 +++++++++++++
 gcc/config/riscv/riscv.h                      | 17 +++++++++++++
 .../gcc.target/riscv/stack-check-14.c         | 24 +++++++++++++++++++
 .../gcc.target/riscv/stack-check-15.c         | 21 ++++++++++++++++
 .../gcc.target/riscv/stack-check-alloca-1.c   | 15 ++++++++++++
 .../gcc.target/riscv/stack-check-alloca-10.c  | 13 ++++++++++
 .../gcc.target/riscv/stack-check-alloca-2.c   | 11 +++++++++
 .../gcc.target/riscv/stack-check-alloca-3.c   | 11 +++++++++
 .../gcc.target/riscv/stack-check-alloca-4.c   | 12 ++++++++++
 .../gcc.target/riscv/stack-check-alloca-5.c   | 12 ++++++++++
 .../gcc.target/riscv/stack-check-alloca-6.c   | 12 ++++++++++
 .../gcc.target/riscv/stack-check-alloca-7.c   | 12 ++++++++++
 .../gcc.target/riscv/stack-check-alloca-8.c   | 14 +++++++++++
 .../gcc.target/riscv/stack-check-alloca-9.c   | 13 ++++++++++
 .../gcc.target/riscv/stack-check-alloca.h     | 15 ++++++++++++
 15 files changed, 219 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-14.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-15.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-1.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-10.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-2.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-3.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-4.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-5.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-6.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-7.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-8.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca-9.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/stack-check-alloca.h

Comments

Jeff Law July 26, 2024, 9:53 p.m. UTC | #1
On 7/24/24 12:00 PM, Raphael Moreira Zinsly wrote:
> Add the TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE to riscv in
> order to enable stack clash protection when using alloca.
> The code and tests are the same used by aarch64.
> 
> gcc/ChangeLog:
> 	* config/riscv/riscv.cc (riscv_compute_frame_info): Update
> 	  outgoing args size.
> 	  (riscv_stack_clash_protection_alloca_probe_range): New.
> 	  (TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE): New.
> 	* config/riscv/riscv.h
> 	  (STACK_CLASH_MIN_BYTES_OUTGOING_ARGS): New.
> 	  (STACK_DYNAMIC_OFFSET): New.
Thanks for adding this.   I don't offhand remember if I originally 
noticed the lack of ALLOCA_PROBE_RANGE during review or as a result of 
the initial package testing that started before the milkv blew up where 
the unhandled cases very much looked alloca related.  Either way, needed 
fixing and thanks for taking care of it.

This looks good.  So the only outstanding question is that one for 
prologue temporaries in patch 4/5.

Jeff
diff mbox series

Patch

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 69c0e07f4c5..a110e011766 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -7245,6 +7245,10 @@  riscv_compute_frame_info (void)
 
   frame = &cfun->machine->frame;
 
+  /* Adjust the outgoing arguments size if required.  Keep it in sync with what
+     the mid-end is doing.  */
+  crtl->outgoing_args_size = STACK_DYNAMIC_OFFSET (cfun);
+
   /* In an interrupt function, there are two cases in which t0 needs to be used:
      1, If we have a large frame, then we need to save/restore t0.  We check for
      this before clearing the frame struct.
@@ -11879,6 +11883,15 @@  riscv_c_mode_for_floating_type (enum tree_index ti)
   return default_mode_for_floating_type (ti);
 }
 
+/* On riscv we have an ABI defined safe buffer.  This constant is used to
+   determining the probe offset for alloca.  */
+
+static HOST_WIDE_INT
+riscv_stack_clash_protection_alloca_probe_range (void)
+{
+  return STACK_CLASH_CALLER_GUARD;
+}
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
@@ -12187,6 +12200,10 @@  riscv_c_mode_for_floating_type (enum tree_index ti)
 #define TARGET_VECTORIZE_PREFERRED_VECTOR_ALIGNMENT \
   riscv_vectorize_preferred_vector_alignment
 
+#undef TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE
+#define TARGET_STACK_CLASH_PROTECTION_ALLOCA_PROBE_RANGE \
+  riscv_stack_clash_protection_alloca_probe_range
+
 /* Mode switching hooks.  */
 
 #undef TARGET_MODE_EMIT
diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
index 0432beb81e0..7f20190e960 100644
--- a/gcc/config/riscv/riscv.h
+++ b/gcc/config/riscv/riscv.h
@@ -1270,4 +1270,21 @@  extern void riscv_remove_unneeded_save_restore_calls (void);
    generating stack clash probes.  */
 #define STACK_CLASH_MAX_UNROLL_PAGES 4
 
+/* This value represents the minimum amount of bytes we expect the function's
+   outgoing arguments to be when stack-clash is enabled.  */
+#define STACK_CLASH_MIN_BYTES_OUTGOING_ARGS 8
+
+/* Allocate a minimum of STACK_CLASH_MIN_BYTES_OUTGOING_ARGS bytes for the
+   outgoing arguments if stack clash protection is enabled.  This is essential
+   as the extra arg space allows us to skip a check in alloca.  */
+#undef STACK_DYNAMIC_OFFSET
+#define STACK_DYNAMIC_OFFSET(FUNDECL)			   \
+   ((flag_stack_clash_protection			   \
+     && cfun->calls_alloca				   \
+     && known_lt (crtl->outgoing_args_size,		   \
+		  STACK_CLASH_MIN_BYTES_OUTGOING_ARGS))    \
+    ? ROUND_UP (STACK_CLASH_MIN_BYTES_OUTGOING_ARGS,       \
+		STACK_BOUNDARY / BITS_PER_UNIT)		   \
+    : (crtl->outgoing_args_size + STACK_POINTER_OFFSET))
+
 #endif /* ! GCC_RISCV_H */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-14.c b/gcc/testsuite/gcc.target/riscv/stack-check-14.c
new file mode 100644
index 00000000000..8ca0488c468
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-14.c
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+int t1(int);
+
+int t2(int x)
+{
+  char *p = __builtin_alloca (2048);
+  x = t1 (x);
+  return p[x];
+}
+
+
+/* This test has a constant sized alloca that is smaller than the
+   probe interval.  Only one probe is required since the value is larger
+   than 1024 bytes but smaller than page size.
+
+   The form can change quite a bit so we just check for one
+   probe without looking at the actual address.  */
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+
+
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-15.c b/gcc/testsuite/gcc.target/riscv/stack-check-15.c
new file mode 100644
index 00000000000..a44b257ba75
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-15.c
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+
+int t1(int);
+
+int t2(int x)
+{
+  char *p = __builtin_alloca (x);
+  x = t1 (x);
+  return p[x];
+}
+
+
+/* This test has a variable sized alloca.  It requires 3 probes.
+   One in the loop, one for the residual, one for when it's < 1024 and one for
+   when it's not.
+
+   The form can change quite a bit so we just check for three
+   probes without looking at the actual address.  */
+/* { dg-final { scan-assembler-times {sd\tzero,} 3 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-1.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-1.c
new file mode 100644
index 00000000000..642840fb50c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-1.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE y
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 2 } } */
+/* { dg-final { scan-assembler-times {sd\tzero,0\(sp\)} 1 } } */
+
+/* Dynamic alloca, expect loop, and 2 probes with 1kB offset and 1 at sp.
+   1st probe is inside the loop for the full guard-size allocations, second
+   probe is for the case where residual is zero and the final probe for when
+   residiual is > 1024 bytes.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-10.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-10.c
new file mode 100644
index 00000000000..11844aad748
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-10.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 127.5 * 3 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 2 } } */
+
+/* Large alloca of an amount which isn't a multiple of a guard-size, and
+   residiual is more than 1kB.  Loop expected with one 1Kb probe offset and
+   one residual probe at offset 1kB.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-2.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-2.c
new file mode 100644
index 00000000000..5c7a158adec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 0
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-not {sd\tzero,} } } */
+
+/* Alloca of 0 should emit no probes, boundary condition.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-3.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-3.c
new file mode 100644
index 00000000000..a5db2679aef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-3.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 100
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,8\(sp\)} 1 } } */
+
+/* Alloca is less than 1kB, 1 probe expected at word offset.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-4.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-4.c
new file mode 100644
index 00000000000..1841412ff36
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-4.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 1.5 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* Alloca is more than 1kB, but less than guard-size, 1 probe expected at
+   1kB offset.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-5.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-5.c
new file mode 100644
index 00000000000..f8f9d944564
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-5.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 2 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* Alloca is more than 1kB, but less than guard-size, 1 probe expected at
+   1kB offset.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-6.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-6.c
new file mode 100644
index 00000000000..d937e929d75
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-6.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 2.5 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* Alloca is more than 1kB, but less than guard-size, 1 probe expected at 1kB
+   offset.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-7.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-7.c
new file mode 100644
index 00000000000..cbb32f3157e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-7.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 3 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* Alloca is exactly one guard-size, 1 probe expected at 1kB offset.
+   Boundary condition.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-8.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-8.c
new file mode 100644
index 00000000000..3cc3450355b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-8.c
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection --param stack-clash-protection-guard-size=16" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 65 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+/* { dg-final { scan-assembler-times {sd\tzero,8\(sp\)} 1 } } */
+
+/* Alloca is more than one guard-page, and residual is exactly 1Kb. 2 probes
+   expected.  One at 1kB offset for the guard-size allocation and one at word
+   offset for the residual.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca-9.c b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-9.c
new file mode 100644
index 00000000000..36466930e4e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca-9.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=rv64gc -mabi=lp64d -fstack-clash-protection" } */
+/* { dg-require-effective-target supports_stack_clash_protection } */
+/* { dg-require-effective-target alloca } */
+
+#define SIZE 127 * 3 * 1024
+#include "stack-check-alloca.h"
+
+/* { dg-final { scan-assembler-times {sd\tzero,1024\(sp\)} 1 } } */
+
+/* Large alloca of a constant amount which is a multiple of a guard-size,
+   no residiual.  Loop expected with one 1Kb probe offset and no residual probe
+   because residual is at compile time known to be zero.  */
diff --git a/gcc/testsuite/gcc.target/riscv/stack-check-alloca.h b/gcc/testsuite/gcc.target/riscv/stack-check-alloca.h
new file mode 100644
index 00000000000..8c75f6c0f70
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/stack-check-alloca.h
@@ -0,0 +1,15 @@ 
+
+/* Avoid inclusion of alloca.h, unavailable on some systems.  */
+#define alloca __builtin_alloca
+
+__attribute__((noinline, noipa))
+void g (char* ptr, int y)
+{
+  ptr[y] = '\0';
+}
+
+void f_caller (int y)
+{
+  char* pStr = alloca(SIZE);
+  g (pStr, y);
+}