Hi,
   the attached patch adds a new attribute 'security_sensitive' for functions.
The idea was discussed in PR middle-end/69976.
This attribute makes gcc to emit clean up code at the function's epilogue.
This clean-up code cleans the stack used by this function and that isn't
needed anymore. It also cleans used registers. It only works in x86_64.
Please, review the discussion here:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69976
since we had some doubts with the implementation.

We also added some test-cases and ran all tests in x86_64.
We think this isn't a bug-fix but a new feature.

Changelog
2016-03-21  Marcos Diaz  <marcos.d...@tallertechnologies.com>
   Andres Tiraboschi  <andres.tirabos...@tallertechnologies.com>

PR tree-optimization/69820
* config/i386/i386-protos.h: Add ix86_clear_regs_emit and
ix86_sec_sensitive_attr_p
* config/i386/i386.c: (ix86_sec_sensitive_attr_p): New function
(ix86_using_red_zone): now take into account if the function has the new
attribute.
(is_preserved_reg): New function.
(is_integer_reg): New function.
(is_used_as_ret): New function.
(reg_to_string): New function.
(clear_reg_emit): New function.
(ix86_clear_regs_emit): New function.
(ix86_expand_epilogue): Added code to emit clean up code only when
security_sensitive attribute is set.
(ix86_handle_security_sensitive_attribute): New function.
(ix86_attribute_table): Added new attribute.
* config/i386/i386.md: (UNSPECV_CLRSTACK): New unspecv.
(UNSPECV_CLRREGS): New unspecv.
(return): Conditionally emit cleaning regs code.
(simple_return): Likewise
(clear_regs): New insn.
(clear_stack): New insn.
* doc/extend.texi: Added description for new security_sensitive attribute.
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index e4652f3..b69aa59 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -32,6 +32,8 @@ extern HOST_WIDE_INT ix86_initial_elimination_offset (int, 
int);
 extern void ix86_expand_prologue (void);
 extern void ix86_maybe_emit_epilogue_vzeroupper (void);
 extern void ix86_expand_epilogue (int);
+extern void ix86_clear_regs_emit (rtx*);
+extern bool ix86_sec_sensitive_attr_p(void);
 extern void ix86_expand_split_stack_prologue (void);
 
 extern void ix86_output_addr_vec_elt (FILE *, int);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 3d8dbc4..7c58d6d 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -3690,12 +3690,19 @@ make_pass_stv (gcc::context *ctxt)
   return new pass_stv (ctxt);
 }
 
+bool ix86_sec_sensitive_attr_p(void)
+{
+  return lookup_attribute ("security_sensitive", DECL_ATTRIBUTES (cfun->decl));
+}
+
+
 /* Return true if a red-zone is in use.  */
 
 static inline bool
 ix86_using_red_zone (void)
 {
-  return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI;
+  return TARGET_RED_ZONE && !TARGET_64BIT_MS_ABI
+       && !ix86_sec_sensitive_attr_p();
 }
 
 /* Return a string that documents the current -m options.  The caller is
@@ -13325,6 +13332,144 @@ ix86_emit_restore_sse_regs_using_mov (HOST_WIDE_INT 
cfa_offset,
       }
 }
 
+/* Returns true iff regno is a register that must be preserved in a function.*/
+static inline bool
+is_preserved_reg(const unsigned int regno)
+{
+  return (regno == BX_REG)
+        || (regno == BP_REG)
+        || (regno == SP_REG)
+        || (regno == R12_REG)
+        || (regno == R13_REG)
+        || (regno == R14_REG)
+        || (regno == R15_REG);
+}
+
+/* Returns true iff regno is an integer register.*/
+static inline bool is_integer_reg(unsigned int regno)
+{
+  return (regno <= 7u) ||
+     ((FIRST_REX_INT_REG <= regno) && (regno <= LAST_REX_INT_REG));
+}
+
+/* Returns true iff regno is used to return in the current function.*/
+static inline bool
+is_used_as_ret(const unsigned int reg_number)
+{
+  bool is_ret = ix86_function_value_regno_p(reg_number);
+  if (is_ret)
+    {
+      rtx ret = ix86_function_value
+               (TREE_TYPE (DECL_RESULT (cfun->decl)), cfun->decl, true);
+      if ((REG_P(ret))
+         && (is_integer_reg(REGNO(ret)))
+         && (is_integer_reg(reg_number))
+        )
+       {
+         if ((GET_MODE(ret) == TImode) || (GET_MODE(ret) == CDImode))
+           is_ret = (reg_number == AX_REG) || (reg_number == DX_REG);
+         else
+           is_ret = (reg_number == AX_REG);
+       }
+      else if ((REG_P(ret)))
+       is_ret = REGNO(ret) == reg_number;
+      else
+       {
+         // Is parallel
+         const unsigned int len = XVECLEN(ret, 0);
+         unsigned int j = 0u;
+         bool found = false;
+         while (!found && j < len)
+           {
+             const rtx explist = XVECEXP(ret, 0, j);
+             const rtx ret_reg = XEXP(explist, 0);
+             found = REGNO(ret_reg) == reg_number;
+             ++j;
+           }
+         is_ret = found;
+       }
+
+    }
+  return is_ret;
+}
+
+// Make this big enough to store any instruction
+#define MAX_CLEAR_STRING_SIZE 50
+
+/* Adds to str in pos position the neame of the register regno.*/
+static size_t
+reg_to_string(const unsigned int regno, size_t pos, char * const str)
+{
+  str[pos++] = '%';
+  str[pos++] = '%';
+  if (regno <= STACK_POINTER_REGNUM)
+    str[pos++] = 'e';
+
+  strncpy(str + pos, reg_names[regno], MAX_CLEAR_STRING_SIZE - pos);
+  pos += strnlen(reg_names[regno], MAX_CLEAR_STRING_SIZE - pos);
+  return pos;
+}
+
+/*Emits an instruction to clear regno register.*/
+static void
+clear_reg_emit(const unsigned int regno, rtx *operands)
+{
+  static char output[MAX_CLEAR_STRING_SIZE];
+  size_t size = 0u;
+  bool emit = true;
+  if (regno <= STACK_POINTER_REGNUM)
+    {
+      static const char clear_ins[] = "xorl\t";
+      size = strnlen(clear_ins, sizeof(clear_ins));
+      strncpy(output, clear_ins, size);
+    }
+  else if ((FIRST_REX_INT_REG <= regno) && (regno <= LAST_REX_INT_REG))
+    {
+      static const char clear_ins[] = "xorq\t";
+      size = strnlen(clear_ins, sizeof(clear_ins));
+      strncpy(output, clear_ins, size);
+    }
+  else if ( ((FIRST_SSE_REG <= regno) && (regno <= LAST_SSE_REG)) ||
+           ((FIRST_REX_SSE_REG <= regno) && (regno <= LAST_REX_SSE_REG)) )
+    {
+      static const char clear_ins[] = "xorps\t";
+      size = strnlen(clear_ins, sizeof(clear_ins));
+      strncpy(output, clear_ins, size);
+    }
+  else if ((FIRST_MMX_REG <= regno) && (regno <= LAST_MMX_REG))
+    {
+      static const char clear_ins[] = "pxor\t";
+      size = strnlen(clear_ins, sizeof(clear_ins));
+      strncpy(output, clear_ins, size);
+    }
+  else
+    emit = false;
+
+  if (emit)
+    {
+      size = reg_to_string (regno, size, output);
+      gcc_assert (size < MAX_CLEAR_STRING_SIZE - 2);
+      output[size++] = ',';
+      output[size++] = ' ';
+      size = reg_to_string (regno, size, output);
+      gcc_assert (size < MAX_CLEAR_STRING_SIZE);
+      output[size] = '\0';
+      output_asm_insn (output, operands);
+    }
+}
+
+/* Finds which registers can be cleared and emits code to do that.*/
+void ix86_clear_regs_emit (rtx *operands)
+{
+  for (unsigned int j = 0u; j <= LAST_REX_SSE_REG; ++j)
+    {
+      const bool is_ret = is_used_as_ret(j);
+      const bool is_pres = is_preserved_reg(j);
+      if (df_hard_reg_used_p(j) && !is_pres && !is_ret)
+       clear_reg_emit(j, operands);
+    }
+}
+
 /* Restore function stack, frame, and registers.  */
 
 void
@@ -13335,6 +13480,8 @@ ix86_expand_epilogue (int style)
   struct ix86_frame frame;
   bool restore_regs_via_mov;
   bool using_drap;
+  const bool security_sensitive_attribute =
+       ix86_sec_sensitive_attr_p();
 
   ix86_finalize_stack_realign_flags ();
   ix86_compute_frame_layout (&frame);
@@ -13620,6 +13767,17 @@ ix86_expand_epilogue (int style)
   else
     ix86_add_queued_cfa_restore_notes (get_last_insn ());
 
+  /* If "security_sensitive" attribute enabled, emit code to clear the stack.*/
+  if (security_sensitive_attribute)
+    {
+      const int stack_size_to_clear = frame.stack_pointer_offset
+                                     - m->fs.sp_offset;
+      const int size_in_words = stack_size_to_clear / UNITS_PER_WORD;
+      if (stack_size_to_clear)
+       emit_insn(gen_clear_stack(GEN_INT(stack_size_to_clear),
+                 GEN_INT(size_in_words)));
+    }
+
   /* Sibcall epilogues don't want a return instruction.  */
   if (style == 0)
     {
@@ -13627,6 +13785,10 @@ ix86_expand_epilogue (int style)
       return;
     }
 
+  /* We emit cleaning registers code only in non sibcalls*/
+  if (security_sensitive_attribute)
+    emit_insn(gen_clear_regs());
+
   if (crtl->args.pops_args && crtl->args.size)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
@@ -44532,6 +44694,32 @@ ix86_handle_fndecl_attribute (tree *node, tree name, 
tree, int,
   return NULL_TREE;
 }
 
+static tree
+ix86_handle_security_sensitive_attribute (tree *node, tree name, tree, int,
+                              bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+               name);
+      *no_add_attrs = true;
+    }
+  else if (!TARGET_64BIT || ix86_cfun_abi () == MS_ABI)
+    {
+      warning (OPT_Wattributes, "%qE attribute only valid for 64bits not MS 
ABI",
+               name);
+      *no_add_attrs = true;
+    }
+  else
+    {
+      DECL_ATTRIBUTES (*node)
+        = tree_cons (get_identifier ("noinline"),
+                     NULL_TREE, DECL_ATTRIBUTES (*node));
+      DECL_UNINLINABLE (*node) = 1;
+    }
+  return NULL_TREE;
+}
+
 static bool
 ix86_ms_bitfield_layout_p (const_tree record_type)
 {
@@ -48742,6 +48930,8 @@ static const struct attribute_spec 
ix86_attribute_table[] =
     false },
   { "callee_pop_aggregate_return", 1, 1, false, true, true,
     ix86_handle_callee_pop_aggregate_return, true },
+  { "security_sensitive", 0, 0, true, false, false,
+    ix86_handle_security_sensitive_attribute, false},
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index eed43b1..7c2ccc0 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -270,6 +270,10 @@
 
   ;; For RDPKRU and WRPKRU support
   UNSPECV_PKU
+
+  ;; For security_sensitive attribute
+  UNSPECV_CLRSTACK
+  UNSPECV_CLRREGS
 ])
 
 ;; Constants to represent rounding modes in the ROUND instruction
@@ -12341,6 +12345,8 @@
   [(simple_return)]
   "ix86_can_use_return_insn_p ()"
 {
+  if (ix86_sec_sensitive_attr_p())
+    emit_insn(gen_clear_regs());
   if (crtl->args.pops_args)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
@@ -12361,6 +12367,8 @@
   [(simple_return)]
   "!TARGET_SEH && !ix86_static_chain_on_stack"
 {
+  if (ix86_sec_sensitive_attr_p())
+    emit_insn(gen_clear_regs());
   if (crtl->args.pops_args)
     {
       rtx popc = GEN_INT (crtl->args.pops_args);
@@ -12572,6 +12580,23 @@
   DONE;
 })
 
+(define_insn "clear_regs"
+  [(unspec_volatile [(const_int 0)] UNSPECV_CLRREGS)]
+  "reload_completed"
+  "*
+  {
+    ix86_clear_regs_emit (operands);
+    return \"\";
+  }"
+)
+
+(define_insn "clear_stack"
+  [(unspec_volatile [(match_operand 0 "const_int_operand" "")
+  (match_operand 1 "const_int_operand" "")] UNSPECV_CLRSTACK)]
+  "TARGET_64BIT"
+  "movq\t%%rax, %%rsi\n\txorl\t%%eax, %%eax\n\tmovq\t%%rsp, %%rdi\n\tsubq\t%0, 
%%rdi\n\tmovl\t%1, %%ecx\n\trep stosq\n\tmovq\t%%rsi, %%rax"
+)
+
 (define_insn_and_split "eh_return_internal"
   [(eh_return)]
   ""
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8fddb34..73d9f2d 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5216,6 +5216,14 @@ this function attribute to make GCC generate the 
``hot-patching'' function
 prologue used in Win32 API functions in Microsoft Windows XP Service Pack 2
 and newer.
 
+@item security_sensitive
+@cindex @code{security_sensitive} function attribute, x86
+
+On x86_64 targets, you can use this function attribute to make GCC generate
+stack and registers cleaning code, after the execution of this function.
+Note that this will enable the attribute @code{noinline}, and will make the
+function not to use the red zone.
+
 @item regparm (@var{number})
 @cindex @code{regparm} function attribute, x86
 @cindex functions that are passed arguments in registers on x86-32
diff --git a/gcc/testsuite/gcc.target/i386/security_sensitive_attr-asm.c 
b/gcc/testsuite/gcc.target/i386/security_sensitive_attr-asm.c
new file mode 100644
index 0000000..b54374a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/security_sensitive_attr-asm.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-times "rep stosq" 1 } }*/
+/* { dg-final { scan-assembler-times "xorl\\t%esi, %esi"  1 } } */
+
+void read_secret(char* buf, unsigned int size);
+int
+  __attribute__((security_sensitive))
+check_password (void)
+{
+  char buf[16];
+  read_secret (buf, 16);
+  return 1;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/i386/security_sensitive_attr.c 
b/gcc/testsuite/gcc.target/i386/security_sensitive_attr.c
new file mode 100644
index 0000000..3fadd14
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/security_sensitive_attr.c
@@ -0,0 +1,39 @@
+/* { dg-do run { target { ! ia32 } } } */
+/* { dg-options "-O2" } */
+
+#include <string.h>
+
+void __attribute__((noinline)) read_secret (char *out, size_t size)
+{
+  strncpy (out, "secret password", size);
+}
+
+int
+  __attribute__((security_sensitive))
+check_password (void)
+{
+  char buf[16];
+  read_secret (buf, 16);
+  return 1;
+}
+
+/* Attempt to access the secret still on the stack.  */
+
+int
+  __attribute__((noinline))
+steal_password (void)
+{
+  char buf[16];
+  if (strstr(buf, "password"))
+    return 1;
+  return 0;
+}
+
+int main (int argc, const char **argv)
+{
+  if (!check_password ())
+    return 1;
+
+  if (steal_password ())
+    return 2;
+}

Reply via email to