use-after-scope: handle writes to a poisoned variable
authorMartin Liska <marxin@gcc.gnu.org>
Mon, 23 Jan 2017 12:06:13 +0000 (12:06 +0000)
committerMartin Liska <marxin@gcc.gnu.org>
Mon, 23 Jan 2017 12:06:13 +0000 (12:06 +0000)
2017-01-23  Martin Liska  <mliska@suse.cz>

* gcc.dg/asan/use-after-scope-10.c: New test.
* gcc.dg/asan/use-after-scope-11.c: New test.
* g++.dg/asan/use-after-scope-5.C: New test.
2017-01-23  Jakub Jelinek  <jakub@redhat.com>
    Martin Liska  <mliska@suse.cz>

* asan.h: Define ASAN_USE_AFTER_SCOPE_ATTRIBUTE.
* asan.c (asan_expand_poison_ifn): Support stores and use
appropriate ASAN report function.
* internal-fn.c (expand_ASAN_POISON_USE): New function.
* internal-fn.def (ASAN_POISON_USE): Declare.
* tree-into-ssa.c (maybe_add_asan_poison_write): New function.
(maybe_register_def): Create ASAN_POISON_USE when sanitizing.
* tree-ssa-dce.c (eliminate_unnecessary_stmts): Remove
ASAN_POISON calls w/o LHS.
* tree-ssa.c (execute_update_addresses_taken): Create clobber
for ASAN_MARK (UNPOISON, &x, ...) in order to prevent usage of a LHS
from ASAN_MARK (POISON, &x, ...) coming to a PHI node.
* gimplify.c (asan_poison_variables): Add attribute
use_after_scope_memory to variables that really needs to live
in memory.
* tree-ssa.c (is_asan_mark_p): Do not rewrite into SSA when
having the attribute.

From-SVN: r244793

13 files changed:
gcc/ChangeLog
gcc/asan.c
gcc/asan.h
gcc/gimplify.c
gcc/internal-fn.c
gcc/internal-fn.def
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/asan/use-after-scope-5.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/asan/use-after-scope-10.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/asan/use-after-scope-11.c [new file with mode: 0644]
gcc/tree-into-ssa.c
gcc/tree-ssa-dce.c
gcc/tree-ssa.c

index 34728a9e52fd73a8f28d8c3b531343d20abe8a95..5ab065fbcc76ea98eec6c6c72705849f0dfec154 100644 (file)
@@ -1,3 +1,24 @@
+2017-01-23  Jakub Jelinek  <jakub@redhat.com>
+           Martin Liska  <mliska@suse.cz>
+
+       * asan.h: Define ASAN_USE_AFTER_SCOPE_ATTRIBUTE.
+       * asan.c (asan_expand_poison_ifn): Support stores and use
+       appropriate ASAN report function.
+       * internal-fn.c (expand_ASAN_POISON_USE): New function.
+       * internal-fn.def (ASAN_POISON_USE): Declare.
+       * tree-into-ssa.c (maybe_add_asan_poison_write): New function.
+       (maybe_register_def): Create ASAN_POISON_USE when sanitizing.
+       * tree-ssa-dce.c (eliminate_unnecessary_stmts): Remove
+       ASAN_POISON calls w/o LHS.
+       * tree-ssa.c (execute_update_addresses_taken): Create clobber
+       for ASAN_MARK (UNPOISON, &x, ...) in order to prevent usage of a LHS
+       from ASAN_MARK (POISON, &x, ...) coming to a PHI node.
+       * gimplify.c (asan_poison_variables): Add attribute
+       use_after_scope_memory to variables that really needs to live
+       in memory.
+       * tree-ssa.c (is_asan_mark_p): Do not rewrite into SSA when
+       having the attribute.
+
 2017-01-23  Martin Liska  <mliska@suse.cz>
 
        * asan.c (create_asan_shadow_var): New function.
index fe117a6951a7dd8632d0453d3dcae4d6e6720a7a..486ebfdb6af1603aee1f5ec2670d9781eb47b1d1 100644 (file)
@@ -3094,6 +3094,8 @@ create_asan_shadow_var (tree var_decl,
     return *slot;
 }
 
+/* Expand ASAN_POISON ifn.  */
+
 bool
 asan_expand_poison_ifn (gimple_stmt_iterator *iter,
                        bool *need_commit_edge_insert,
@@ -3107,8 +3109,8 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
       return true;
     }
 
-  tree shadow_var  = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var),
-                                            shadow_vars_mapping);
+  tree shadow_var = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var),
+                                           shadow_vars_mapping);
 
   bool recover_p;
   if (flag_sanitize & SANITIZE_USER_ADDRESS)
@@ -3122,16 +3124,16 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
                                                 ASAN_MARK_POISON),
                                  build_fold_addr_expr (shadow_var), size);
 
-  use_operand_p use_p;
+  gimple *use;
   imm_use_iterator imm_iter;
-  FOR_EACH_IMM_USE_FAST (use_p, imm_iter, poisoned_var)
+  FOR_EACH_IMM_USE_STMT (use, imm_iter, poisoned_var)
     {
-      gimple *use = USE_STMT (use_p);
       if (is_gimple_debug (use))
        continue;
 
       int nargs;
-      tree fun = report_error_func (false, recover_p, tree_to_uhwi (size),
+      bool store_p = gimple_call_internal_p (use, IFN_ASAN_POISON_USE);
+      tree fun = report_error_func (store_p, recover_p, tree_to_uhwi (size),
                                    &nargs);
 
       gcall *call = gimple_build_call (fun, 1,
@@ -3160,7 +3162,10 @@ asan_expand_poison_ifn (gimple_stmt_iterator *iter,
       else
        {
          gimple_stmt_iterator gsi = gsi_for_stmt (use);
-         gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+         if (store_p)
+           gsi_replace (&gsi, call, true);
+         else
+           gsi_insert_before (&gsi, call, GSI_NEW_STMT);
        }
     }
 
index 2895bdee645115b45b1d7834fea0966f4b22068e..57663977603b7c9ff8dc4e4ff962e871b5a234e9 100644 (file)
@@ -67,6 +67,8 @@ extern hash_set <tree> *asan_used_labels;
 #define ASAN_STACK_FRAME_MAGIC         0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC       0x45e0360e
 
+#define ASAN_USE_AFTER_SCOPE_ATTRIBUTE "use after scope memory"
+
 /* Various flags for Asan builtins.  */
 enum asan_check_flags
 {
index d382eea69f85b6f2664fca3448bc33eb5e62e378..61a1ba5c7f9eaaa3acf8396796d11089b9488bc1 100644 (file)
@@ -1206,8 +1206,21 @@ asan_poison_variables (hash_set<tree> *variables, bool poison, gimple_seq *seq_p
 
   sorted_variables.qsort (sort_by_decl_uid);
 
-  for (unsigned i = 0; i < sorted_variables.length (); i++)
-    asan_poison_variable (sorted_variables[i], poison, seq_p);
+  unsigned i;
+  tree var;
+  FOR_EACH_VEC_ELT (sorted_variables, i, var)
+    {
+      asan_poison_variable (var, poison, seq_p);
+
+      /* Add use_after_scope_memory attribute for the variable in order
+        to prevent re-written into SSA.  */
+      if (!lookup_attribute (ASAN_USE_AFTER_SCOPE_ATTRIBUTE,
+                            DECL_ATTRIBUTES (var)))
+       DECL_ATTRIBUTES (var)
+         = tree_cons (get_identifier (ASAN_USE_AFTER_SCOPE_ATTRIBUTE),
+                      integer_one_node,
+                      DECL_ATTRIBUTES (var));
+    }
 }
 
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
index 45e4ce05b86e349fc6304d0f81c28a0a4b989c37..0d61375462d030dd33abccd78cdc3d1b73a72b5e 100644 (file)
@@ -388,6 +388,14 @@ expand_ASAN_POISON (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_POISON_USE (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
+
 /* This should get expanded in the tsan pass.  */
 
 static void
index 7b28b6722ff657b1544c576f69f13adf6ac84233..fd25a9522994b16957a9928b8a8569db252d17ca 100644 (file)
@@ -168,6 +168,7 @@ DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R...")
 DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, ".R..")
 DEF_INTERNAL_FN (ASAN_POISON, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
+DEF_INTERNAL_FN (ASAN_POISON_USE, ECF_LEAF | ECF_NOTHROW | ECF_NOVOPS, NULL)
 DEF_INTERNAL_FN (ADD_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
index bb60f696e0338801155bdad32693a31dbacba60a..0ebaae5035c59b6c76c0d16d64fb0a9625c3de4c 100644 (file)
@@ -1,3 +1,9 @@
+2017-01-23  Martin Liska  <mliska@suse.cz>
+
+       * gcc.dg/asan/use-after-scope-10.c: New test.
+       * gcc.dg/asan/use-after-scope-11.c: New test.
+       * g++.dg/asan/use-after-scope-5.C: New test.
+
 2017-01-23  Martin Liska  <mliska@suse.cz>
 
        * gcc.dg/asan/use-after-scope-3.c: Add additional flags.
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-5.C b/gcc/testsuite/g++.dg/asan/use-after-scope-5.C
new file mode 100644 (file)
index 0000000..7e28fc3
--- /dev/null
@@ -0,0 +1,23 @@
+// { dg-do run }
+
+int *
+__attribute__((optimize(("-O0"))))
+fn1 (int *a)
+{
+  return a;
+}
+
+void
+fn2 ()
+{
+  for (int i = 0; i < 10; i++)
+    {
+      int *a;
+      (a) = fn1 (a);
+    }
+}
+
+int main()
+{
+  fn2();
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c
new file mode 100644 (file)
index 0000000..24de8ce
--- /dev/null
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+// { dg-additional-options "-O2 -fdump-tree-asan1" }
+
+int
+main (int argc, char **argv)
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  *ptr = 12345;
+  return *ptr;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size .*" }
+// { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c
new file mode 100644 (file)
index 0000000..b3c4c9e
--- /dev/null
@@ -0,0 +1,30 @@
+// { dg-do run }
+
+#include <string.h>
+
+char cc;
+char ptr[] = "sparta2";
+
+void get(char **x)
+{
+  *x = ptr;
+}
+  
+int main()
+{
+  char *here = &cc;
+
+  for (;;)
+    {
+    next_line:
+       if (here == NULL)
+         __builtin_abort();
+       get (&here);
+       if (strcmp (here, "sparta") == 0)
+           goto next_line;
+       else if (strcmp (here, "sparta2") == 0)
+         break;
+    }
+
+  return 0;
+}
index c7df237d57fee3afb8adc8181e4f63cffe233d67..22261c15dc2af7d30bc977964d9aee34d11365f6 100644 (file)
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa.h"
 #include "domwalk.h"
 #include "statistics.h"
+#include "asan.h"
 
 #define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y))
 
@@ -1807,6 +1808,26 @@ maybe_replace_use_in_debug_stmt (use_operand_p use_p)
 }
 
 
+/* If DEF has x_5 = ASAN_POISON () as its current def, add
+   ASAN_POISON_USE (x_5) stmt before GSI to denote the stmt writes into
+   a poisoned (out of scope) variable.  */
+
+static void
+maybe_add_asan_poison_write (tree def, gimple_stmt_iterator *gsi)
+{
+  tree cdef = get_current_def (def);
+  if (cdef != NULL
+      && TREE_CODE (cdef) == SSA_NAME
+      && gimple_call_internal_p (SSA_NAME_DEF_STMT (cdef), IFN_ASAN_POISON))
+    {
+      gcall *call
+       = gimple_build_call_internal (IFN_ASAN_POISON_USE, 1, cdef);
+      gimple_set_location (call, gimple_location (gsi_stmt (*gsi)));
+      gsi_insert_before (gsi, call, GSI_SAME_STMT);
+    }
+}
+
+
 /* If the operand pointed to by DEF_P is an SSA name in NEW_SSA_NAMES
    or OLD_SSA_NAMES, or if it is a symbol marked for renaming,
    register it as the current definition for the names replaced by
@@ -1837,7 +1858,11 @@ maybe_register_def (def_operand_p def_p, gimple *stmt,
              def = get_or_create_ssa_default_def (cfun, sym);
            }
          else
-           def = make_ssa_name (def, stmt);
+           {
+             if (asan_sanitize_use_after_scope ())
+               maybe_add_asan_poison_write (def, &gsi);
+             def = make_ssa_name (def, stmt);
+           }
          SET_DEF (def_p, def);
 
          tree tracked_var = target_for_debug_bind (sym);
index 4e51e699d49cc639efdbe05cd283aaa5f37f5abd..5ebe57b0983678d22e81a8a45c4104e89d4cbb2f 100644 (file)
@@ -1367,10 +1367,18 @@ eliminate_unnecessary_stmts (void)
                  update_stmt (stmt);
                  release_ssa_name (name);
 
-                 /* GOMP_SIMD_LANE without lhs is not needed.  */
-                 if (gimple_call_internal_p (stmt)
-                     && gimple_call_internal_fn (stmt) == IFN_GOMP_SIMD_LANE)
-                   remove_dead_stmt (&gsi, bb);
+                 /* GOMP_SIMD_LANE or ASAN_POISON without lhs is not
+                    needed.  */
+                 if (gimple_call_internal_p (stmt))
+                   switch (gimple_call_internal_fn (stmt))
+                     {
+                     case IFN_GOMP_SIMD_LANE:
+                     case IFN_ASAN_POISON:
+                       remove_dead_stmt (&gsi, bb);
+                       break;
+                     default:
+                       break;
+                     }
                }
              else if (gimple_call_internal_p (stmt))
                switch (gimple_call_internal_fn (stmt))
index f1826b2c9c45f118d5212db7b0271050c5c21c2d..28020b003f83375ba91f3542c67d9eec9ffc5902 100644 (file)
@@ -1590,6 +1590,10 @@ is_asan_mark_p (gimple *stmt)
       && VAR_P (TREE_OPERAND (addr, 0)))
     {
       tree var = TREE_OPERAND (addr, 0);
+      if (lookup_attribute (ASAN_USE_AFTER_SCOPE_ATTRIBUTE,
+                           DECL_ATTRIBUTES (var)))
+       return false;
+
       unsigned addressable = TREE_ADDRESSABLE (var);
       TREE_ADDRESSABLE (var) = 0;
       bool r = is_gimple_reg (var);
@@ -1911,7 +1915,16 @@ execute_update_addresses_taken (void)
                            gsi_replace (&gsi, call, GSI_SAME_STMT);
                          }
                        else
-                         gsi_remove (&gsi, true);
+                         {
+                           /* In ASAN_MARK (UNPOISON, &b, ...) the variable
+                              is uninitialized.  Avoid dependencies on
+                              previous out of scope value.  */
+                           tree clobber
+                             = build_constructor (TREE_TYPE (var), NULL);
+                           TREE_THIS_VOLATILE (clobber) = 1;
+                           gimple *g = gimple_build_assign (var, clobber);
+                           gsi_replace (&gsi, g, GSI_SAME_STMT);
+                         }
                        continue;
                      }
                  }