ASAN: handle addressable params (PR sanitize/81040).
authorMartin Liska <mliska@suse.cz>
Mon, 3 Jul 2017 11:48:47 +0000 (13:48 +0200)
committerMartin Liska <marxin@gcc.gnu.org>
Mon, 3 Jul 2017 11:48:47 +0000 (11:48 +0000)
2017-07-03  Martin Liska  <mliska@suse.cz>

PR sanitize/81040
* g++.dg/asan/function-argument-1.C: New test.
* g++.dg/asan/function-argument-2.C: New test.
* g++.dg/asan/function-argument-3.C: New test.
2017-07-03  Martin Liska  <mliska@suse.cz>

PR sanitize/81040
* sanopt.c (rewrite_usage_of_param): New function.
(sanitize_rewrite_addressable_params): Likewise.
(pass_sanopt::execute): Call rewrite_usage_of_param.

From-SVN: r249903

gcc/ChangeLog
gcc/doc/extend.texi
gcc/sanopt.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/asan/function-argument-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/asan/function-argument-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/asan/function-argument-3.C [new file with mode: 0644]

index 721901cea1c25fc9d8835c7c7274e82f3816ca99..905c6916262d03191acbcb5dccde50806f017b05 100644 (file)
@@ -1,8 +1,21 @@
+2017-07-03  Martin Liska  <mliska@suse.cz>
+
+       PR sanitize/81040
+       * sanopt.c (rewrite_usage_of_param): New function.
+       (sanitize_rewrite_addressable_params): Likewise.
+       (pass_sanopt::execute): Call rewrite_usage_of_param.
+
 2017-07-03  Richard Biener  <rguenther@suse.de>
 
        * tree-vect-loop.c (vect_create_epilog_for_reduction): Revert
        back to using VIEW_CONVERT_EXPR.
 
+2017-07-03  Martin Liska  <mliska@suse.cz>
+
+       PR other/78366
+       * doc/extend.texi: Document when a resolver function is
+       generated for target_clones.
+
 2017-07-03  Martin Liska  <mliska@suse.cz>
 
        * asan.c (asan_emit_stack_protection): Unpoison just red zones
index 84150ccf144debfef689ed48e3cc21a83dbb2c3a..03ba8fc436c187f52cbd482fbbbef8262c22a781 100644 (file)
@@ -3278,16 +3278,16 @@ are the same as for @code{target} attribute.
 For instance, on an x86, you could compile a function with
 @code{target_clones("sse4.1,avx")}.  GCC creates two function clones,
 one compiled with @option{-msse4.1} and another with @option{-mavx}.
-It also creates a resolver function (see the @code{ifunc} attribute
-above) that dynamically selects a clone suitable for current
-architecture.
 
 On a PowerPC, you can compile a function with
 @code{target_clones("cpu=power9,default")}.  GCC will create two
 function clones, one compiled with @option{-mcpu=power9} and another
-with the default options.  It also creates a resolver function (see
+with the default options.
+
+It also creates a resolver function (see
 the @code{ifunc} attribute above) that dynamically selects a clone
-suitable for current architecture.
+suitable for current architecture.  The resolver is created only if there
+is a usage of a function with @code{target_clones} attribute.
 
 @item unused
 @cindex @code{unused} function attribute
index 16bdba760423fc9fa05cc8c639b8fe83da16e115..7692f6a9db716c793ed8423b5359a08cbced235c 100644 (file)
@@ -37,6 +37,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-ssa.h"
 #include "tree-phinodes.h"
 #include "ssa-iterators.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+#include "cfghooks.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
 
 /* This is used to carry information about basic blocks.  It is
    attached to the AUX field of the standard CFG block.  */
@@ -858,6 +864,135 @@ sanitize_asan_mark_poison (void)
     }
 }
 
+/* Rewrite all usages of tree OP which is a PARM_DECL with a VAR_DECL
+   that is it's DECL_VALUE_EXPR.  */
+
+static tree
+rewrite_usage_of_param (tree *op, int *walk_subtrees, void *)
+{
+  if (TREE_CODE (*op) == PARM_DECL && DECL_HAS_VALUE_EXPR_P (*op))
+    {
+      *op = DECL_VALUE_EXPR (*op);
+      *walk_subtrees = 0;
+    }
+
+  return NULL;
+}
+
+/* For a given function FUN, rewrite all addressable parameters so that
+   a new automatic variable is introduced.  Right after function entry
+   a parameter is assigned to the variable.  */
+
+static void
+sanitize_rewrite_addressable_params (function *fun)
+{
+  gimple *g;
+  gimple_seq stmts = NULL;
+  bool has_any_addressable_param = false;
+  auto_vec<tree> clear_value_expr_list;
+
+  for (tree arg = DECL_ARGUMENTS (current_function_decl);
+       arg; arg = DECL_CHAIN (arg))
+    {
+      if (TREE_ADDRESSABLE (arg) && !TREE_ADDRESSABLE (TREE_TYPE (arg)))
+       {
+         TREE_ADDRESSABLE (arg) = 0;
+         /* The parameter is no longer addressable.  */
+         tree type = TREE_TYPE (arg);
+         has_any_addressable_param = true;
+
+         /* Create a new automatic variable.  */
+         tree var = build_decl (DECL_SOURCE_LOCATION (arg),
+                                VAR_DECL, DECL_NAME (arg), type);
+         TREE_ADDRESSABLE (var) = 1;
+         DECL_ARTIFICIAL (var) = 1;
+
+         gimple_add_tmp_var (var);
+
+         if (dump_file)
+           fprintf (dump_file,
+                    "Rewriting parameter whose address is taken: %s\n",
+                    IDENTIFIER_POINTER (DECL_NAME (arg)));
+
+         gcc_assert (!DECL_HAS_VALUE_EXPR_P (arg));
+         DECL_HAS_VALUE_EXPR_P (arg) = 1;
+         SET_DECL_VALUE_EXPR (arg, var);
+
+         SET_DECL_PT_UID (var, DECL_PT_UID (arg));
+
+         /* Assign value of parameter to newly created variable.  */
+         if ((TREE_CODE (type) == COMPLEX_TYPE
+              || TREE_CODE (type) == VECTOR_TYPE))
+           {
+             /* We need to create a SSA name that will be used for the
+                assignment.  */
+             DECL_GIMPLE_REG_P (arg) = 1;
+             tree tmp = get_or_create_ssa_default_def (cfun, arg);
+             g = gimple_build_assign (var, tmp);
+             gimple_set_location (g, DECL_SOURCE_LOCATION (arg));
+             gimple_seq_add_stmt (&stmts, g);
+           }
+         else
+           {
+             g = gimple_build_assign (var, arg);
+             gimple_set_location (g, DECL_SOURCE_LOCATION (arg));
+             gimple_seq_add_stmt (&stmts, g);
+           }
+
+         if (target_for_debug_bind (arg))
+           {
+             g = gimple_build_debug_bind (arg, var, NULL);
+             gimple_seq_add_stmt (&stmts, g);
+             clear_value_expr_list.safe_push (arg);
+           }
+       }
+    }
+
+  if (!has_any_addressable_param)
+    return;
+
+  /* Replace all usages of PARM_DECLs with the newly
+     created variable VAR.  */
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple *stmt = gsi_stmt (gsi);
+         gimple_stmt_iterator it = gsi_for_stmt (stmt);
+         walk_gimple_stmt (&it, NULL, rewrite_usage_of_param, NULL);
+       }
+      for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gphi *phi = dyn_cast<gphi *> (gsi_stmt (gsi));
+          for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+           {
+             hash_set<tree> visited_nodes;
+             walk_tree (gimple_phi_arg_def_ptr (phi, i),
+                        rewrite_usage_of_param, NULL, &visited_nodes);
+           }
+       }
+    }
+
+  /* Unset value expr for parameters for which we created debug bind
+     expressions.  */
+  unsigned i;
+  tree arg;
+  FOR_EACH_VEC_ELT (clear_value_expr_list, i, arg)
+    {
+      DECL_HAS_VALUE_EXPR_P (arg) = 0;
+      SET_DECL_VALUE_EXPR (arg, NULL_TREE);
+    }
+
+  /* Insert default assignments at the beginning of a function.  */
+  basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (fun);
+  entry_bb = split_edge (single_succ_edge (entry_bb));
+
+  gimple_stmt_iterator gsi = gsi_start_bb (entry_bb);
+  gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT);
+}
+
 unsigned int
 pass_sanopt::execute (function *fun)
 {
@@ -891,6 +1026,9 @@ pass_sanopt::execute (function *fun)
       sanitize_asan_mark_poison ();
     }
 
+  if (asan_sanitize_stack_p ())
+    sanitize_rewrite_addressable_params (fun);
+
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
index f5d2f8be78973bb60770cc8b69aa3ace42429bcc..09d9d7b339783238c6918f284063eca1b5027a41 100644 (file)
@@ -1,3 +1,10 @@
+2017-07-03  Martin Liska  <mliska@suse.cz>
+
+       PR sanitize/81040
+       * g++.dg/asan/function-argument-1.C: New test.
+       * g++.dg/asan/function-argument-2.C: New test.
+       * g++.dg/asan/function-argument-3.C: New test.
+
 2017-07-03  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * gcc.dg/vect/bb-slp-pr65935.c: Expect SLP to be used in main
diff --git a/gcc/testsuite/g++.dg/asan/function-argument-1.C b/gcc/testsuite/g++.dg/asan/function-argument-1.C
new file mode 100644 (file)
index 0000000..148c462
--- /dev/null
@@ -0,0 +1,30 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+struct A
+{
+  int a[5];
+};
+
+static __attribute__ ((noinline)) int
+goo (A *a)
+{
+  int *ptr = &a->a[0];
+  return *(volatile int *) (ptr - 1);
+}
+
+__attribute__ ((noinline)) int
+foo (A arg)
+{
+  return goo (&arg);
+}
+
+int
+main ()
+{
+  return foo (A ());
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-buffer-underflow on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size . at.*" }
+// { dg-output ".*'arg' <== Memory access at offset \[0-9\]* underflows this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/function-argument-2.C b/gcc/testsuite/g++.dg/asan/function-argument-2.C
new file mode 100644 (file)
index 0000000..3a7c33b
--- /dev/null
@@ -0,0 +1,24 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+static __attribute__ ((noinline)) int
+goo (int *a)
+{
+  return *(volatile int *)a;
+}
+
+__attribute__ ((noinline)) int
+foo (char arg)
+{
+  return goo ((int *)&arg);
+}
+
+int
+main ()
+{
+  return foo (12);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-buffer-overflow on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size . at.*" }
+// { dg-output ".*'arg' <== Memory access at offset \[0-9\]* partially overflows this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/function-argument-3.C b/gcc/testsuite/g++.dg/asan/function-argument-3.C
new file mode 100644 (file)
index 0000000..14617ba
--- /dev/null
@@ -0,0 +1,27 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+typedef int v4si __attribute__ ((vector_size (16)));
+
+static __attribute__ ((noinline)) int
+goo (v4si *a)
+{
+  return (*(volatile v4si *) (a + 1))[2];
+}
+
+__attribute__ ((noinline)) int
+foo (v4si arg)
+{
+  return goo (&arg);
+}
+
+int
+main ()
+{
+  v4si v = {1,2,3,4};
+  return foo (v);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-buffer-overflow on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size . at.*" }
+// { dg-output ".*'arg' <== Memory access at offset \[0-9\]* overflows this variable.*" }