Hi, this patch provides a new stack protection option - 
"fstack-protector-strong".

Background - some times stack-protector is too-simple while stack-protector-all 
over-kills, for example, to build one of our core systems, we forcibly add 
"-fstack-protector-all" to all compile commands, which brings big performance 
penalty (due to extra stack guard/check insns on function prologue and 
epilogue) on both atom and arm. To use "-fstack-protector" is just regarded as 
not secure enough (only "protects" <2% functions) by the system secure team. So 
I'd like to add the option "-fstack-protector-strong", that hits the balance 
between "-fstack-protector" and "-fstack-protector-all".

Benefit - gain big performance while sacrificing little security (for scenarios 
using -fstack-protector-all)

Status - implemented internally, to be up-streamed or merged to google branch 
only.

Detail - 
https://docs.google.com/a/google.com/document/d/1xXBH6rRZue4f296vGt9YQcuLVQHeE516stHwt8M9xyU/edit?hl=en_US

Tested - manually, built chrome browser using the modified compiler with 
"-fstack-protector-strong".

=========================
gcc/ChangeLog:
        * cfgexpand.c (expand_used_vars): Add logic handling 
stack-protector-strong.
        (is_local_address_taken): Internal function that returns true when 
gimple contains
        an address taken on function local variables.
        (record_or_union_type_has_array): New, tests if a record or union type 
contains an array.
        * common.opt (fstack-protector-all): New option.

gcc/testsuite/ChangeLog
        * gcc.dg/fstack-protector-strong.c: New.

==========================

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 8684721..1d9df87 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -1507,6 +1507,50 @@ estimated_stack_frame_size (struct cgraph_node *node)
   return size;
 }
 
+static int is_local_address_taken(tree t) {
+  if (t && TREE_CODE(t) == ADDR_EXPR) {
+    int i;
+    tree local_var;
+    tree v = TREE_OPERAND(t, 0);
+    switch (TREE_CODE(v)) {
+    case MEM_REF:
+      for (i = 0; i < TREE_OPERAND_LENGTH(v); ++i)
+        if (is_local_address_taken(TREE_OPERAND(v, i)))
+          return 1;
+      return 0;
+    case COMPONENT_REF:
+      while (v && TREE_CODE(v) == COMPONENT_REF)
+        v = TREE_OPERAND(v, 0);
+      break;
+    case VAR_DECL:
+    default:
+      ;
+    }
+    if (v && TREE_CODE(v) == VAR_DECL && !is_global_var(v)) {
+      FOR_EACH_VEC_ELT(tree, cfun->local_decls, i, local_var) {
+        if (local_var == v)
+          return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+static int record_or_union_type_has_array(tree tree_type) {
+  tree fields = TYPE_FIELDS(tree_type);
+  tree f;
+  for (f = fields; f; f = DECL_CHAIN (f)) {
+    if (TREE_CODE(f) == FIELD_DECL) {
+      tree field_type = TREE_TYPE(f);
+      if (RECORD_OR_UNION_TYPE_P(field_type))
+        return record_or_union_type_has_array(field_type);
+      if (TREE_CODE(field_type) == ARRAY_TYPE)
+        return 1;
+    }
+  }
+  return 0;
+}
+
 /* Expand all variables used in the function.  */
 
 static void
@@ -1516,6 +1560,8 @@ expand_used_vars (void)
   VEC(tree,heap) *maybe_local_decls = NULL;
   unsigned i;
   unsigned len;
+  int gen_stack_protect_signal = 0;
+  basic_block bb;
 
   /* Compute the phase of the stack frame for this function.  */
   {
@@ -1548,6 +1594,63 @@ expand_used_vars (void)
        }
     }
 
+  /* Examine each basic block for address taking of local variables. */
+  FOR_EACH_BB(bb) {
+    gimple_stmt_iterator si;
+    /* Scanning phis. */
+    for (si = gsi_start_phis(bb); !gsi_end_p(si); gsi_next(&si)) {
+      gimple phi_stmt = gsi_stmt(si);
+      unsigned int i;
+      for (i = 0; i < gimple_phi_num_args(phi_stmt); ++i)
+        if (is_local_address_taken(gimple_phi_arg(phi_stmt, i)->def))
+          ++gen_stack_protect_signal;
+    }
+    /* Scanning assignments and calls. */
+    for (si = gsi_start_bb(bb); !gen_stack_protect_signal && !gsi_end_p(si);
+         gsi_next(&si)) {
+      gimple stmt = gsi_stmt (si);
+      if (is_gimple_assign(stmt)) {
+        switch(gimple_assign_rhs_class(stmt)) {
+        case GIMPLE_TERNARY_RHS:
+          if (is_local_address_taken(gimple_assign_rhs3(stmt))) {
+            ++gen_stack_protect_signal;
+            break;
+          }
+          /* Otherwise, fall through. */
+        case GIMPLE_BINARY_RHS:
+          if (is_local_address_taken(gimple_assign_rhs2(stmt))) {
+            ++gen_stack_protect_signal;
+            break;
+          }
+        case GIMPLE_SINGLE_RHS:
+        case GIMPLE_UNARY_RHS:
+          if (is_local_address_taken(gimple_assign_rhs1(stmt)))
+            ++gen_stack_protect_signal;
+          break;
+        case GIMPLE_INVALID_RHS:
+          break;
+        }
+      }
+
+      if (!gen_stack_protect_signal && is_gimple_call(stmt)) {
+        int ii, num_arg = gimple_call_num_args(stmt);
+        for (ii = 0; !gen_stack_protect_signal && ii < num_arg; ++ii)
+          if (is_local_address_taken(gimple_call_arg(stmt, ii)))
+            ++gen_stack_protect_signal;
+      }
+    }
+  }
+
+  /* Examine local variable declaration. */
+  if (!gen_stack_protect_signal)
+    FOR_EACH_LOCAL_DECL (cfun, i, var)
+      if (TREE_CODE(var) == VAR_DECL && !is_global_var(var)) {
+        tree var_type = TREE_TYPE(var);
+        gen_stack_protect_signal += (TREE_CODE(var_type) == ARRAY_TYPE) ||
+          (RECORD_OR_UNION_TYPE_P(var_type) &&
+           record_or_union_type_has_array(var_type));
+      }
+
   /* At this point all variables on the local_decls with TREE_USED
      set are not associated with any block scope.  Lay them out.  */
 
@@ -1640,9 +1743,13 @@ expand_used_vars (void)
 
   /* There are several conditions under which we should create a
      stack guard: protect-all, alloca used, protected decls present.  */
-  if (flag_stack_protect == 2
-      || (flag_stack_protect
-         && (cfun->calls_alloca || has_protected_decls)))
+  if (flag_stack_protect == 2 /* -fstack-protector-all */
+      || (flag_stack_protect == 1 /* -fstack-protector */
+         && (cfun->calls_alloca || has_protected_decls))) {
+    create_stack_guard ();
+  } else if (flag_stack_protect == 3 && /* -fstack-protector-strong */
+             (gen_stack_protect_signal ||
+              cfun->calls_alloca || has_protected_decls))
     create_stack_guard ();
 
   /* Assign rtl to each variable based on these partitions.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index 55d3f2d..1ad9717 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1848,6 +1848,10 @@ fstack-protector-all
 Common Report RejectNegative Var(flag_stack_protect, 2)
 Use a stack protection method for every function
 
+fstack-protector-strong
+Common Report RejectNegative Var(flag_stack_protect, 3)
+Use a smart stack protection method for certain functions
+
 fstack-usage
 Common RejectNegative Var(flag_stack_usage)
 Output stack usage information on a per-function basis
diff --git a/gcc/testsuite/gcc.dg/fstack-protector-strong.c 
b/gcc/testsuite/gcc.dg/fstack-protector-strong.c
new file mode 100644
index 0000000..44225f5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fstack-protector-strong.c
@@ -0,0 +1,110 @@
+/* Test that stack protection is done on chosen functions. */
+
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-O2 -fstack-protector-strong" } */
+
+#include<string.h>
+#include<stdlib.h>
+
+extern int g0;
+extern int *pg0;
+int goo(int *);
+int hoo(int);
+
+/* Function frame address escaped function call. */
+int foo1()
+{
+  int i;
+  return goo(&i);
+}
+
+struct ArrayStruct {
+  int a;
+  int array[10];
+};
+
+struct AA {
+  int b;
+  struct ArrayStruct as;
+};
+
+/* Function frame contains array. */
+int foo2() {
+  struct AA aa;
+  int i;
+  for (i = 0; i < 10; ++i) {
+    aa.as.array[i] = i * (i-1) + i / 2;
+  }
+  return aa.as.array[5];
+}
+
+/* Address computation based on a function frame address. */
+int foo3() {
+  int a;
+  int *p;
+  p = &a + 5;
+  return goo(p);
+}
+
+/* Address cast based on a function frame address. */
+int foo4() {
+  int a;
+  return goo(g0 << 2 ? (int *)(3 * (long)(void *)(&a)) : 0);
+
+}
+
+/* Address cast based on a local array. */
+int foo5() {
+  short array[10];
+  return goo((int *)(array + 5));
+}
+
+struct BB {
+  int one;
+  int two;
+  int three;
+};
+
+/* Address computaton based on a function frame address.*/
+int foo6() {
+  struct BB bb;
+  return goo(&bb.one + sizeof(int));
+}
+
+/* Function frame address escaped via global variable. */
+int foo7() {
+  int a;
+  pg0 = &a;
+  goo(pg0);
+  return *pg0;
+}
+
+/* Check that this covers -fstack-protector. */
+int foo8() {
+  char base[100];
+  memcpy((void *)base, (const void *)pg0, 105);
+  return (int)(base[32]);
+}
+
+/* Check that this covers -fstack-protector. */
+int foo9() {
+  char* p = alloca(100);
+  return goo((int *)(p + 50));
+}
+
+/* Address taken on struct. */
+int foo10() {
+  struct BB bb;
+  int i;
+  memset(&bb, 5, sizeof bb);
+  for (i = 0; i < 10; ++i) {
+    bb.one = i;
+    bb.two = bb.one + bb.two;
+    bb.three = bb.one + bb.two + bb.three;
+  }
+  return bb.three;
+}
+
+/* { dg-final { scan-assembler-times "stack_chk_fail" 10 } } */
+
+

--
This patch is available for review at http://codereview.appspot.com/5461043

Reply via email to