diff mbox series

[2/4] rs6000: Emit ROP-mitigation instructions in prologue and epilogue

Message ID 95c7f5db172ccc5bc1a5f910952c0883066c0014.1620958221.git.wschmidt@linux.ibm.com
State New
Headers show
Series ROP support | expand

Commit Message

Bill Schmidt May 14, 2021, 3:34 a.m. UTC
2021-05-13  Bill Schmidt  <wschmidt@linux.ibm.com>

gcc/
	* config/rs6000/rs6000-internal.h (rs6000_stack): Add
	rop_hash_save_offset and rop_hash_size.
	* config/rs6000/rs6000-logue.c (rs6000_stack_info): Compute
	rop_hash_size and rop_hash_save_offset.
	(debug_stack_info): Dump rop_hash_save_offset and rop_hash_size.
	(rs6000_emit_prologue): Emit hashst[p] in prologue.
	(rs6000_emit_epilogue): Emit hashchk[p] in epilogue.
	* config/rs6000/rs6000.md (unspec): Add UNSPEC_HASHST and
	UNSPEC_HASHCHK.
	(hashst): New define_insn.
	(hashchk): Likewise.
---
 gcc/config/rs6000/rs6000-internal.h |  2 +
 gcc/config/rs6000/rs6000-logue.c    | 74 ++++++++++++++++++++++++++---
 gcc/config/rs6000/rs6000.md         | 47 ++++++++++++++++++
 3 files changed, 116 insertions(+), 7 deletions(-)

Comments

Segher Boessenkool May 14, 2021, 6:44 p.m. UTC | #1
Hi!

On Thu, May 13, 2021 at 10:34:55PM -0500, Bill Schmidt via Gcc-patches wrote:
> +;; ROP mitigation instructions.
> +
> +(define_insn "hashst"
> +  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
> +        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
> +	           UNSPEC_HASHST))]
> +  "TARGET_POWER10 && rs6000_rop_protect"
> +{
> +  static char templ[14];

14 is okay, but why not use 200 or so?  You don't call anything anyway
(except that sprintf itself perhaps).  Or 50 or whatever.  Something so
ridiculously big that you do not have to count, that's my point :-)

> +  char p[2];
> +
> +  if (rs6000_privileged)
> +    {
> +      p[0] = 'p';
> +      p[1] = 0;
> +    }
> +  else
> +    p[0] = 0;

const char *p = rs6000_privileged ? "p" : "";

Please make this an unspec_volatile?

> +(define_insn "hashchk"

Same things here of course, but it is volatile already.

Okay for trunk and 11 with such changes.  Thanks!


Segher
Li, Pan2 via Gcc-patches May 14, 2021, 7:10 p.m. UTC | #2
On 5/14/21 1:44 PM, Segher Boessenkool wrote:
> Please make this an unspec_volatile? 

Oops!  Sorry for missing that the first time.

Thanks for the review!!

Bill
diff mbox series

Patch

diff --git a/gcc/config/rs6000/rs6000-internal.h b/gcc/config/rs6000/rs6000-internal.h
index 428a7861a98..88cf9bd5692 100644
--- a/gcc/config/rs6000/rs6000-internal.h
+++ b/gcc/config/rs6000/rs6000-internal.h
@@ -39,6 +39,7 @@  typedef struct rs6000_stack {
   int gp_save_offset;		/* offset to save GP regs from initial SP */
   int fp_save_offset;		/* offset to save FP regs from initial SP */
   int altivec_save_offset;	/* offset to save AltiVec regs from initial SP */
+  int rop_hash_save_offset;	/* offset to save ROP hash from initial SP */
   int lr_save_offset;		/* offset to save LR from initial SP */
   int cr_save_offset;		/* offset to save CR from initial SP */
   int vrsave_save_offset;	/* offset to save VRSAVE from initial SP */
@@ -53,6 +54,7 @@  typedef struct rs6000_stack {
   int gp_size;			/* size of saved GP registers */
   int fp_size;			/* size of saved FP registers */
   int altivec_size;		/* size of saved AltiVec registers */
+  int rop_hash_size;		/* size of ROP hash slot */
   int cr_size;			/* size to hold CR if not in fixed area */
   int vrsave_size;		/* size to hold VRSAVE */
   int altivec_padding_size;	/* size of altivec alignment padding */
diff --git a/gcc/config/rs6000/rs6000-logue.c b/gcc/config/rs6000/rs6000-logue.c
index b0ac183ceff..13c00e740d6 100644
--- a/gcc/config/rs6000/rs6000-logue.c
+++ b/gcc/config/rs6000/rs6000-logue.c
@@ -595,19 +595,21 @@  rs6000_savres_strategy (rs6000_stack_t *info,
 		+---------------------------------------+
 		| Parameter save area (+padding*) (P)	|  32
 		+---------------------------------------+
-		| Alloca space (A)			|  32+P
+		| Optional ROP hash slot (R)		|  32+P
 		+---------------------------------------+
-		| Local variable space (L)		|  32+P+A
+		| Alloca space (A)			|  32+P+R
 		+---------------------------------------+
-		| Save area for AltiVec registers (W)	|  32+P+A+L
+		| Local variable space (L)		|  32+P+R+A
 		+---------------------------------------+
-		| AltiVec alignment padding (Y)		|  32+P+A+L+W
+		| Save area for AltiVec registers (W)	|  32+P+R+A+L
 		+---------------------------------------+
-		| Save area for GP registers (G)	|  32+P+A+L+W+Y
+		| AltiVec alignment padding (Y)		|  32+P+R+A+L+W
 		+---------------------------------------+
-		| Save area for FP registers (F)	|  32+P+A+L+W+Y+G
+		| Save area for GP registers (G)	|  32+P+R+A+L+W+Y
 		+---------------------------------------+
-	old SP->| back chain to caller's caller		|  32+P+A+L+W+Y+G+F
+		| Save area for FP registers (F)	|  32+P+R+A+L+W+Y+G
+		+---------------------------------------+
+	old SP->| back chain to caller's caller		|  32+P+R+A+L+W+Y+G+F
 		+---------------------------------------+
 
      * If the alloca area is present, the parameter save area is
@@ -716,6 +718,19 @@  rs6000_stack_info (void)
 
   /* Does this function call anything (apart from sibling calls)?  */
   info->calls_p = (!crtl->is_leaf || cfun->machine->ra_needs_full_frame);
+  info->rop_hash_size = 0;
+
+  if (TARGET_POWER10
+      && info->calls_p
+      && DEFAULT_ABI == ABI_ELFv2
+      && rs6000_rop_protect)
+    info->rop_hash_size = 8;
+  else if (rs6000_rop_protect && DEFAULT_ABI != ABI_ELFv2)
+    {
+      /* We can't check this in rs6000_option_override_internal since
+	 DEFAULT_ABI isn't established yet.  */
+      error ("%qs requires the ELFv2 ABI", "-mrop-protect");
+    }
 
   /* Determine if we need to save the condition code registers.  */
   if (save_reg_p (CR2_REGNO)
@@ -808,6 +823,11 @@  rs6000_stack_info (void)
 
 	  /* Adjust for AltiVec case.  */
 	  info->ehrd_offset = info->altivec_save_offset - ehrd_size;
+
+	  /* Adjust for ROP protection.  */
+	  info->rop_hash_save_offset
+	    = info->altivec_save_offset - info->rop_hash_size;
+	  info->ehrd_offset -= info->rop_hash_size;
 	}
       else
 	info->ehrd_offset = info->gp_save_offset - ehrd_size;
@@ -849,6 +869,7 @@  rs6000_stack_info (void)
 				  + info->gp_size
 				  + info->altivec_size
 				  + info->altivec_padding_size
+				  + info->rop_hash_size
 				  + ehrd_size
 				  + ehcr_size
 				  + info->cr_size
@@ -987,6 +1008,10 @@  debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\tvrsave_save_offset  = %5d\n",
 	     info->vrsave_save_offset);
 
+  if (info->rop_hash_size)
+    fprintf (stderr, "\trop_hash_save_offset = %5d\n",
+	     info->rop_hash_save_offset);
+
   if (info->lr_save_p)
     fprintf (stderr, "\tlr_save_offset      = %5d\n", info->lr_save_offset);
 
@@ -1026,6 +1051,9 @@  debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\taltivec_padding_size= %5d\n",
 	     info->altivec_padding_size);
 
+  if (info->rop_hash_size)
+    fprintf (stderr, "\trop_hash_size       = %5d\n", info->rop_hash_size);
+
   if (info->cr_size)
     fprintf (stderr, "\tcr_size             = %5d\n", info->cr_size);
 
@@ -3252,6 +3280,22 @@  rs6000_emit_prologue (void)
 	}
     }
 
+  /* The ROP hash store must occur before a stack frame is created,
+     since the hash operates on r1.  */
+  /* NOTE: The hashst isn't needed if we're going to do a sibcall,
+     but there's no way to know that here.  Harmless except for
+     performance, of course.  */
+  if (TARGET_POWER10 && rs6000_rop_protect && info->rop_hash_size != 0)
+    {
+      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
+      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
+			       GEN_INT (info->rop_hash_save_offset));
+      rtx mem = gen_rtx_MEM (Pmode, addr);
+      rtx reg0 = gen_rtx_REG (Pmode, 0);
+      emit_insn (gen_hashst (mem, reg0));
+    }
+
   /* If we need to save CR, put it into r12 or r11.  Choose r12 except when
      r12 will be needed by out-of-line gpr save.  */
   cr_save_regno = ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)
@@ -4980,6 +5024,22 @@  rs6000_emit_epilogue (enum epilogue_type epilogue_type)
       emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx, sa));
     }
 
+  /* The ROP hash check must occur after the stack pointer is restored
+     (since the hash involves r1), and is not performed for a sibcall.  */
+  if (TARGET_POWER10
+      && rs6000_rop_protect
+      && info->rop_hash_size != 0
+      && epilogue_type != EPILOGUE_TYPE_SIBCALL)
+    {
+      gcc_assert (DEFAULT_ABI == ABI_ELFv2);
+      rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+      rtx addr = gen_rtx_PLUS (Pmode, stack_ptr,
+			       GEN_INT (info->rop_hash_save_offset));
+      rtx mem = gen_rtx_MEM (Pmode, addr);
+      rtx reg0 = gen_rtx_REG (Pmode, 0);
+      emit_insn (gen_hashchk (reg0, mem));
+    }
+
   if (epilogue_type != EPILOGUE_TYPE_SIBCALL && restoring_FPRs_inline)
     {
       if (cfa_restores)
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index c8cdc42533c..ce58ecd6b73 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -154,6 +154,8 @@  (define_c_enum "unspec"
    UNSPEC_CNTTZDM
    UNSPEC_PDEPD
    UNSPEC_PEXTD
+   UNSPEC_HASHST
+   UNSPEC_HASHCHK
   ])
 
 ;;
@@ -14948,6 +14950,51 @@  (define_insn "*cmpeqb_internal"
   "TARGET_P9_MISC && TARGET_64BIT"
   "cmpeqb %0,%1,%2"
   [(set_attr "type" "logical")])
+
+
+;; ROP mitigation instructions.
+
+(define_insn "hashst"
+  [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m")
+        (unspec:DI [(match_operand:DI 1 "int_reg_operand" "r")]
+	           UNSPEC_HASHST))]
+  "TARGET_POWER10 && rs6000_rop_protect"
+{
+  static char templ[14];
+  char p[2];
+
+  if (rs6000_privileged)
+    {
+      p[0] = 'p';
+      p[1] = 0;
+    }
+  else
+    p[0] = 0;
+  sprintf (templ, "hashst%s %%1,%%0", p);
+  return templ;
+}
+  [(set_attr "type" "store")])
+
+(define_insn "hashchk"
+  [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r")
+		     (match_operand:DI 1 "simple_offsettable_mem_operand" "m")]
+		    UNSPEC_HASHCHK)]
+  "TARGET_POWER10 && rs6000_rop_protect"
+{
+  static char templ[15];
+  char p[2];
+
+  if (rs6000_privileged)
+    {
+      p[0] = 'p';
+      p[1] = 0;
+    }
+  else
+    p[0] = 0;
+  sprintf (templ, "hashchk%s %%0,%%1", p);
+  return templ;
+}
+  [(set_attr "type" "load")])
 
 
 (include "sync.md")