diff mbox

Speed-up use-after-scope (re-writing to SSA) (version 2)

Message ID 4ec48432-9df6-154a-1b13-065b9772cbbf@suse.cz
State New
Headers show

Commit Message

Martin Liška Dec. 22, 2016, 5:03 p.m. UTC
On 12/21/2016 09:52 AM, Jakub Jelinek wrote:
> On Tue, Dec 20, 2016 at 12:26:41PM +0100, Martin Liška wrote:

>> Ok, llvm folks are unwilling to accept the new API function, thus I've decided to come up

>> with approach suggested by Jakub. Briefly, when expanding ASAN_POISON internal function,

>> we create a new variable (with the same name as the original one). The variable is poisoned

>> at the location of the ASAN_POISON and all usages just call ASAN_CHECK that would trigger

>> use-after-scope run-time error. Situation where ASAN_POISON has a LHS is very rare and

>> is very likely to be a bug. Thus suggested not super-optimized approach should not be

>> problematic.

> 

> Do you have a testcase for the case where there is a write to the var after

> poison that is then made non-addressable?  use-after-scope-9.c only covers

> the read.

> 

>> I'm not sure about the introduction of 'create_var' function, maybe we would need some

>> refactoring. Thoughts?

> 

> It doesn't belong to gimple-expr.c and the name is way too generic, we have

> many create var functions already.  And this one is very specialized.

> 

>> 2016-12-19  Martin Liska  <mliska@suse.cz>

>>

>> 	* asan.c (asan_expand_poison_ifn): New function.

>> 	* asan.h (asan_expand_poison_ifn):  Likewise.

> 

> Too many spaces.

> 

>> +  tree shadow_var = create_var (TREE_TYPE (poisoned_var),

>> +				IDENTIFIER_POINTER (DECL_NAME (var_decl)));

> 

> For the shadow var creation, IMHO you should

> 1) use a hash table, once you add a shadow variable for a certain variable

>    for the first time, reuse it for all the other cases; you can have many

>    ASAN_POISON () calls for the same underlying variable


Thanks for review.

Done by hash_map.

> 2) as I said, use just a function in sanopt.c for this,

>    create_asan_shadow_var or whatever


Also done.

> 3) I think you just want to do copy_node, plus roughly what

>    copy_decl_for_dup_finish does (and set DECL_ARTIFICIAL and

>    DECL_IGNORED_P) - except that you don't have copy_body_data

>    so you can't use it directly (well, you could create copy_body_data

>    just for that purpose and set src_fn and dst_fn to current_function_decl

>    and the rest to NULL)


I decided to use the function with prepared copy_body_data ;)

> 

> I'd really like to see the storing to poisoned var becoming non-addressable

> in action (if it can ever happen, so it isn't just theoretical) to look at

> what it does.


Well, having following sample:

int
main (int argc, char **argv)
{
  int *ptr = 0;

  {
    int a;
    ptr = &a;
    *ptr = 12345;
  }

  *ptr = 12345;
  return *ptr;
}

Right after rewriting into SSA it looks as follows:

main (int argc, char * * argv)
{
  int a;
  int * ptr;
  int _8;

  <bb 2> [0.00%]:
  a_9 = 12345;
  a_10 = ASAN_POISON ();
  a_11 = 12345;
  _8 = a_11;
  return _8;

}

Thus, I guess it not possible to do a write.

Following v2 can bootstrap on ppc64le-redhat-linux and survives regression tests.

Martin

> 

> 	Jakub

>

Comments

Jakub Jelinek Dec. 22, 2016, 5:21 p.m. UTC | #1
On Thu, Dec 22, 2016 at 06:03:50PM +0100, Martin Liška wrote:
> Done by hash_map.


Ok.

> > 3) I think you just want to do copy_node, plus roughly what

> >    copy_decl_for_dup_finish does (and set DECL_ARTIFICIAL and

> >    DECL_IGNORED_P) - except that you don't have copy_body_data

> >    so you can't use it directly (well, you could create copy_body_data

> >    just for that purpose and set src_fn and dst_fn to current_function_decl

> >    and the rest to NULL)

> 

> I decided to use the function with prepared copy_body_data ;)


Ok.

> > I'd really like to see the storing to poisoned var becoming non-addressable

> > in action (if it can ever happen, so it isn't just theoretical) to look at

> > what it does.

> 

> Well, having following sample:

> 

> int

> main (int argc, char **argv)

> {

>   int *ptr = 0;

> 

>   {

>     int a;

>     ptr = &a;

>     *ptr = 12345;

>   }

> 

>   *ptr = 12345;

>   return *ptr;

> }

> 

> Right after rewriting into SSA it looks as follows:

> 

> main (int argc, char * * argv)

> {

>   int a;

>   int * ptr;

>   int _8;

> 

>   <bb 2> [0.00%]:

>   a_9 = 12345;

>   a_10 = ASAN_POISON ();

>   a_11 = 12345;

>   _8 = a_11;

>   return _8;

> 

> }


But we do not want to rewrite into SSA that way, but instead as

main (int argc, char * * argv)
{
  int a;
  int * ptr;
  int _8;

  <bb 2> [0.00%]:
  a_9 = 12345;
  a_10 = ASAN_POISON ();
  ASAN_POISON (a_10);
  a_11 = 12345;
  _8 = a_11;
  return _8;

}

or something similar, so that you can 1) emit a diagnostics at the spot
where the out of scope store happens 2) differentiate between reads from
out of scope var and stores to out of scope var

What we need is to hook into tree-into-ssa.c for this, where a_11 is
created, find out that there is a store to a var that has ASAN_POISON result
as currently active definition.  Something like if we emit ASAN_POISON
for some var, during tree-into-ssa.c if we see a store to that var that we
need to rewrite into SSA pretend there is a read from that var first at
that location and if it is result of ASAN_POISON, emit the additional
stmt.

	Jakub
Martin Liška Jan. 9, 2017, 2:58 p.m. UTC | #2
On 12/22/2016 06:21 PM, Jakub Jelinek wrote:
> On Thu, Dec 22, 2016 at 06:03:50PM +0100, Martin Liška wrote:

>> Done by hash_map.

> 

> Ok.

> 

>>> 3) I think you just want to do copy_node, plus roughly what

>>>    copy_decl_for_dup_finish does (and set DECL_ARTIFICIAL and

>>>    DECL_IGNORED_P) - except that you don't have copy_body_data

>>>    so you can't use it directly (well, you could create copy_body_data

>>>    just for that purpose and set src_fn and dst_fn to current_function_decl

>>>    and the rest to NULL)

>>

>> I decided to use the function with prepared copy_body_data ;)

> 

> Ok.

> 

>>> I'd really like to see the storing to poisoned var becoming non-addressable

>>> in action (if it can ever happen, so it isn't just theoretical) to look at

>>> what it does.

>>

>> Well, having following sample:

>>

>> int

>> main (int argc, char **argv)

>> {

>>   int *ptr = 0;

>>

>>   {

>>     int a;

>>     ptr = &a;

>>     *ptr = 12345;

>>   }

>>

>>   *ptr = 12345;

>>   return *ptr;

>> }

>>

>> Right after rewriting into SSA it looks as follows:

>>

>> main (int argc, char * * argv)

>> {

>>   int a;

>>   int * ptr;

>>   int _8;

>>

>>   <bb 2> [0.00%]:

>>   a_9 = 12345;

>>   a_10 = ASAN_POISON ();

>>   a_11 = 12345;

>>   _8 = a_11;

>>   return _8;

>>

>> }

> 

> But we do not want to rewrite into SSA that way, but instead as

> 

> main (int argc, char * * argv)

> {

>   int a;

>   int * ptr;

>   int _8;

> 

>   <bb 2> [0.00%]:

>   a_9 = 12345;

>   a_10 = ASAN_POISON ();

>   ASAN_POISON (a_10);

>   a_11 = 12345;

>   _8 = a_11;

>   return _8;

> 

> }


I'm still not sure how to do that. Problem is that transformation from:

  ASAN_MARK (UNPOISON, &a, 4);
  a = 5;
  ASAN_MARK (POISON, &a, 4);

to 

  a_8 = 5;
  a_9 = ASAN_POISON ();

happens in tree-ssa.c, after SSA is created, in situation where we prove the 'a'
does not need to live in memory. Thus said, question is how to identify that we
need to transform into SSA in a different way:

   a_10 = ASAN_POISON ();
   ASAN_POISON (a_10);

Thanks for help,
Martin

> 

> or something similar, so that you can 1) emit a diagnostics at the spot

> where the out of scope store happens 2) differentiate between reads from

> out of scope var and stores to out of scope var

> 

> What we need is to hook into tree-into-ssa.c for this, where a_11 is

> created, find out that there is a store to a var that has ASAN_POISON result

> as currently active definition.  Something like if we emit ASAN_POISON

> for some var, during tree-into-ssa.c if we see a store to that var that we

> need to rewrite into SSA pretend there is a read from that var first at

> that location and if it is result of ASAN_POISON, emit the additional

> stmt.

> 

> 	Jakub

>
Jakub Jelinek Jan. 16, 2017, 2:20 p.m. UTC | #3
On Mon, Jan 09, 2017 at 03:58:04PM +0100, Martin Liška wrote:
> >> Well, having following sample:

> >>

> >> int

> >> main (int argc, char **argv)

> >> {

> >>   int *ptr = 0;

> >>

> >>   {

> >>     int a;

> >>     ptr = &a;

> >>     *ptr = 12345;

> >>   }

> >>

> >>   *ptr = 12345;

> >>   return *ptr;

> >> }

> >>


> I'm still not sure how to do that. Problem is that transformation from:

> 

>   ASAN_MARK (UNPOISON, &a, 4);

>   a = 5;

>   ASAN_MARK (POISON, &a, 4);

> 

> to 

> 

>   a_8 = 5;

>   a_9 = ASAN_POISON ();

> 

> happens in tree-ssa.c, after SSA is created, in situation where we prove the 'a'

> does not need to live in memory. Thus said, question is how to identify that we

> need to transform into SSA in a different way:

> 

>    a_10 = ASAN_POISON ();

>    ASAN_POISON (a_10);


I meant something like this (completely untested, and without the testcase
added to the testsuite).
The incremental patch as is relies on the ASAN_POISON_USE call having the
argument the result of ASAN_POISON, it would ICE if that is not the case
(especially if -fsanitize-recover=address).  Dunno if some optimization
might decide to create a PHI in between, say merge two unrelated vars for
if (something)
  {
    x_1 = ASAN_POISON ();
    ...
    ASAN_POISON_USE (x_1);
  }
else
  {
    y_2 = ASAN_POISON ();
    ...
    ASAN_POISON_USE (y_2);
  }
to turn that into:
if (something)
  x_1 = ASAN_POISON ();
else
  y_2 = ASAN_POISON ();
_3 = PHI <x_1, y_2>;
...
ASAN_POISON_USE (_3);

If it did, we would ICE because ASAN_POISON_USE would survive this way until
expansion.  A quick fix for the ICE (if it can ever happen) would be easy,
in sanopt remove ASAN_POISON_USE calls which have argument that is not lhs
of ASAN_POISON (all other ASAN_POISON_USE calls will be handled by my
incremental patch).  Of course that would also mean in that case we'd report
a read rather than write.  But if it can't happen or is very unlikely to
happen, then it is a non-issue.

Something missing from the patch is some change in DCE to remove ASAN_POISON
calls without lhs earlier.  I think we can't make ASAN_POISON ECF_CONST, we
don't want it to be merged for different variables.



	Jakub--- gcc/internal-fn.def.jj	2017-01-16 13:19:49.000000000 +0100
+++ gcc/internal-fn.def	2017-01-16 14:25:37.427962196 +0100
@@ -167,6 +167,7 @@ DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, EC
 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)
--- gcc/asan.c.jj	2017-01-16 13:19:49.000000000 +0100
+++ gcc/asan.c	2017-01-16 14:52:34.022044223 +0100
@@ -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_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_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_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);
 	}
     }
 
--- gcc/tree-into-ssa.c.jj	2017-01-01 12:45:35.000000000 +0100
+++ gcc/tree-into-ssa.c	2017-01-16 14:32:14.853808726 +0100
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.
 #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_ope
 }
 
 
+/* 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,
 	      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);
--- gcc/internal-fn.c.jj	2017-01-16 13:19:49.000000000 +0100
+++ gcc/internal-fn.c	2017-01-16 14:26:10.828529039 +0100
@@ -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

Martin Liška Jan. 17, 2017, 4:16 p.m. UTC | #4
On 01/16/2017 03:20 PM, Jakub Jelinek wrote:
> On Mon, Jan 09, 2017 at 03:58:04PM +0100, Martin Liška wrote:

>>>> Well, having following sample:

>>>>

>>>> int

>>>> main (int argc, char **argv)

>>>> {

>>>>   int *ptr = 0;

>>>>

>>>>   {

>>>>     int a;

>>>>     ptr = &a;

>>>>     *ptr = 12345;

>>>>   }

>>>>

>>>>   *ptr = 12345;

>>>>   return *ptr;

>>>> }

>>>>

> 

>> I'm still not sure how to do that. Problem is that transformation from:

>>

>>   ASAN_MARK (UNPOISON, &a, 4);

>>   a = 5;

>>   ASAN_MARK (POISON, &a, 4);

>>

>> to 

>>

>>   a_8 = 5;

>>   a_9 = ASAN_POISON ();

>>

>> happens in tree-ssa.c, after SSA is created, in situation where we prove the 'a'

>> does not need to live in memory. Thus said, question is how to identify that we

>> need to transform into SSA in a different way:

>>

>>    a_10 = ASAN_POISON ();

>>    ASAN_POISON (a_10);

> 

> I meant something like this (completely untested, and without the testcase

> added to the testsuite).

> The incremental patch as is relies on the ASAN_POISON_USE call having the

> argument the result of ASAN_POISON, it would ICE if that is not the case

> (especially if -fsanitize-recover=address).  Dunno if some optimization

> might decide to create a PHI in between, say merge two unrelated vars for

> if (something)

>   {

>     x_1 = ASAN_POISON ();

>     ...

>     ASAN_POISON_USE (x_1);

>   }

> else

>   {

>     y_2 = ASAN_POISON ();

>     ...

>     ASAN_POISON_USE (y_2);

>   }

> to turn that into:

> if (something)

>   x_1 = ASAN_POISON ();

> else

>   y_2 = ASAN_POISON ();

> _3 = PHI <x_1, y_2>;

> ...

> ASAN_POISON_USE (_3);

> 

> If it did, we would ICE because ASAN_POISON_USE would survive this way until

> expansion.  A quick fix for the ICE (if it can ever happen) would be easy,

> in sanopt remove ASAN_POISON_USE calls which have argument that is not lhs

> of ASAN_POISON (all other ASAN_POISON_USE calls will be handled by my

> incremental patch).  Of course that would also mean in that case we'd report

> a read rather than write.  But if it can't happen or is very unlikely to

> happen, then it is a non-issue.


Thank you Jakub for working on that.

The patch is fine, I added DCE support and a test-case. Please see attached patch.
asan.exp regression tests look fine and I've been building linux kernel with KASAN
enabled. I'll also do asan-boostrap.

I would like to commit the patch soon, should I squash both patches together, or would it
be preferred to separate basic optimization and support for stores?

Thanks,
Martin

> Something missing from the patch is some change in DCE to remove ASAN_POISON

> calls without lhs earlier.  I think we can't make ASAN_POISON ECF_CONST, we

> don't want it to be merged for different variables.

> 

> --- gcc/internal-fn.def.jj	2017-01-16 13:19:49.000000000 +0100

> +++ gcc/internal-fn.def	2017-01-16 14:25:37.427962196 +0100

> @@ -167,6 +167,7 @@ DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, EC

>  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)

> --- gcc/asan.c.jj	2017-01-16 13:19:49.000000000 +0100

> +++ gcc/asan.c	2017-01-16 14:52:34.022044223 +0100

> @@ -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_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_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_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);

>  	}

>      }

>  

> --- gcc/tree-into-ssa.c.jj	2017-01-01 12:45:35.000000000 +0100

> +++ gcc/tree-into-ssa.c	2017-01-16 14:32:14.853808726 +0100

> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.

>  #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_ope

>  }

>  

>  

> +/* 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,

>  	      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);

> --- gcc/internal-fn.c.jj	2017-01-16 13:19:49.000000000 +0100

> +++ gcc/internal-fn.c	2017-01-16 14:26:10.828529039 +0100

> @@ -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

> 

> 

> 	Jakub

>From c30802f6a29390a83208bfdb1090a6378ed42691 Mon Sep 17 00:00:00 2001

From: marxin <mliska@suse.cz>

Date: Tue, 17 Jan 2017 16:49:29 +0100
Subject: [PATCH] use-after-scope: handle writes to a poisoned variable

gcc/testsuite/ChangeLog:

2017-01-17  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/use-after-scope-10.c: New test.

gcc/ChangeLog:

2017-01-16  Jakub Jelinek  <jakub@redhat.com>

	* 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.
---
 gcc/asan.c                                     | 19 +++++++++++-------
 gcc/internal-fn.c                              |  8 ++++++++
 gcc/internal-fn.def                            |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-10.c | 22 +++++++++++++++++++++
 gcc/tree-into-ssa.c                            | 27 +++++++++++++++++++++++++-
 gcc/tree-ssa-dce.c                             |  4 ++++
 6 files changed, 73 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-10.c

diff --git a/gcc/asan.c b/gcc/asan.c
index fe117a6951a..486ebfdb6af 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -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);
 	}
     }
 
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 71be382ab8b..a4a2995f58b 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -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
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 7b28b6722ff..fd25a952299 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -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)
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
index 00000000000..24de8cec1ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c
@@ -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/tree-into-ssa.c b/gcc/tree-into-ssa.c
index c7df237d57f..22261c15dc2 100644
--- a/gcc/tree-into-ssa.c
+++ b/gcc/tree-into-ssa.c
@@ -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);
diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c
index 4e51e699d49..7fd2478adec 100644
--- a/gcc/tree-ssa-dce.c
+++ b/gcc/tree-ssa-dce.c
@@ -1384,6 +1384,10 @@ eliminate_unnecessary_stmts (void)
 		  case IFN_MUL_OVERFLOW:
 		    maybe_optimize_arith_overflow (&gsi, MULT_EXPR);
 		    break;
+		  case IFN_ASAN_POISON:
+		    if (!gimple_has_lhs (stmt))
+		      remove_dead_stmt (&gsi, bb);
+		    break;
 		  default:
 		    break;
 		  }
-- 
2.11.0


Jakub Jelinek Jan. 17, 2017, 4:47 p.m. UTC | #5
On Tue, Jan 17, 2017 at 05:16:44PM +0100, Martin Liška wrote:
> > If it did, we would ICE because ASAN_POISON_USE would survive this way until

> > expansion.  A quick fix for the ICE (if it can ever happen) would be easy,

> > in sanopt remove ASAN_POISON_USE calls which have argument that is not lhs

> > of ASAN_POISON (all other ASAN_POISON_USE calls will be handled by my

> > incremental patch).  Of course that would also mean in that case we'd report

> > a read rather than write.  But if it can't happen or is very unlikely to

> > happen, then it is a non-issue.

> 

> Thank you Jakub for working on that.

> 

> The patch is fine, I added DCE support and a test-case. Please see attached patch.

> asan.exp regression tests look fine and I've been building linux kernel with KASAN

> enabled. I'll also do asan-boostrap.

> 

> I would like to commit the patch soon, should I squash both patches together, or would it

> be preferred to separate basic optimization and support for stores?


Your choice, either is fine.  If the two patches pass bootstrap/regtest
(ideally also asan-bootstrap), they are ok for trunk.  Just one nit:

> --- a/gcc/tree-ssa-dce.c

> +++ b/gcc/tree-ssa-dce.c

> @@ -1384,6 +1384,10 @@ eliminate_unnecessary_stmts (void)

>  		  case IFN_MUL_OVERFLOW:

>  		    maybe_optimize_arith_overflow (&gsi, MULT_EXPR);

>  		    break;

> +		  case IFN_ASAN_POISON:

> +		    if (!gimple_has_lhs (stmt))

> +		      remove_dead_stmt (&gsi, bb);

> +		    break;

>  		  default:

>  		    break;

>  		  }


This doesn't seem to be the best spot for it.  At least when looking at
say:
int
foo (int x)
{
  int *ptr = 0;

  if (x < 127)
    return 5;

  {
    int a;
    ptr = &a;
    *ptr = 12345;
  }

  if (x == 34)
    return *ptr;
  return 7;
}
where the ASAN_POISON is initially used and only after evrp becomes dead,
then cddce1 calls eliminate_unnecessary_stmts and removes the lhs of the
ASAN_POISON only (and not the whole stmt, unlike how e.g. GOMP_SIMD_LANE is
handled), and only next dce pass tons of passes later removes the
ASAN_POISON call.
So IMHO you need one of these (untested) patches.  The former assumes that
the DCE pass is the only one that can drop the lhs of ASAN_POISON.  If that
is not the case, then perhaps the second patch is better, by removing the
stmt regardless if we've removed the lhs in the current dce pass or in
whatever earlier pass.  I think it shouldn't break IFN_*_OVERFLOW, because
maybe_optimize_arith_overflow starts with
tree lhs = gimple_call_lhs (stmt);
if (lhs == NULL_TREE || ...)
  return;

	Jakub

--- gcc/tree-ssa-dce.c.jj	2017-01-01 12:45:38.380670110 +0100
+++ gcc/tree-ssa-dce.c	2017-01-17 17:37:38.639427099 +0100
@@ -1366,13 +1366,8 @@ eliminate_unnecessary_stmts (void)
 		  maybe_clean_or_replace_eh_stmt (stmt, stmt);
 		  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);
 		}
-	      else if (gimple_call_internal_p (stmt))
+	      if (gimple_call_internal_p (stmt))
 		switch (gimple_call_internal_fn (stmt))
 		  {
 		  case IFN_ADD_OVERFLOW:
@@ -1384,6 +1379,13 @@ eliminate_unnecessary_stmts (void)
 		  case IFN_MUL_OVERFLOW:
 		    maybe_optimize_arith_overflow (&gsi, MULT_EXPR);
 		    break;
+		  /* GOMP_SIMD_LANE or ASAN_POISON without lhs is not
+		     needed.  */
+		  case IFN_GOMP_SIMD_LANE:
+		  case IFN_ASAN_POISON:
+		    if (gimple_call_lhs (stmt) == NULL_TREE)
+		      remove_dead_stmt (&gsi, bb);
+		    break;
 		  default:
 		    break;
 		  }--- gcc/tree-ssa-dce.c.jj	2017-01-01 12:45:38.380670110 +0100
+++ gcc/tree-ssa-dce.c	2017-01-17 17:35:43.650902141 +0100
@@ -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))

Martin Liška Jan. 18, 2017, 3:34 p.m. UTC | #6
Hello.

During bootstrap, I came to following test-case:

struct A
{
  int regno;
};
struct
{
  A base;
} typedef *df_ref;
int *a;
void
fn1 (int N)
{
  for (int i = 0; i < N; i++)
    {
      df_ref b;
      a[(b)->base.regno]++;
    }
}

As we expand all usages of an LHS of a ASAN_POISON to all uses, we propagate that to
a PHI node that originally contained ASAN_MARK (UNPOISON):

  <bb 4> [0.00%]:
  ASAN_MARK (UNPOISON, &b, 8);
  a.0_1 = a;
  b.1_2 = b;
  _3 = b.1_2->base.regno;
  _4 = (long unsigned int) _3;
  _5 = _4 * 4;
  _6 = a.0_1 + _5;
  _7 = *_6;
  _8 = _7 + 1;
  *_6 = _8;
  ASAN_MARK (POISON, &b, 8);
  i_17 = i_9 + 1;
  goto <bb 3>; [0.00%]

Is transformed to:

  <bb 3> [0.00%]:
  # i_9 = PHI <0(2), i_17(4)>
  # b_18 = PHI <b_19(D)(2), b_20(4)>
  if (i_9 >= N_13(D))
    goto <bb 5>; [0.00%]
  else
    goto <bb 4>; [0.00%]

  <bb 4> [0.00%]:
  a.0_1 = a;
  b.1_2 = b_18;
  _3 = b.1_2->base.regno;
  _4 = (long unsigned int) _3;
  _5 = _4 * 4;
  _6 = a.0_1 + _5;
  _7 = *_6;
  _8 = _7 + 1;
  *_6 = _8;
  b_20 = ASAN_POISON ();
  i_17 = i_9 + 1;
  goto <bb 3>; [0.00%]

Motivation for propagation over PHI nodes was:

cat use.c
int
main (int argc, char **argv)
{
  int *ptr = 0;

  if (argc == 1)
    {
      int my_char;
      ptr = &my_char;
    }

  if (ptr)
    return *ptr;

  return 0;
}

I'm thinking whether the selected approach is fundamentally wrong, our we'll have to stop the PHI propagation
and we still be able to catch some cases with -O2?

Thanks,
Martin
Jakub Jelinek Jan. 19, 2017, 4:33 p.m. UTC | #7
On Wed, Jan 18, 2017 at 04:34:48PM +0100, Martin Liška wrote:
> Hello.

> 

> During bootstrap, I came to following test-case:

> 

> struct A

> {

>   int regno;

> };

> struct

> {

>   A base;

> } typedef *df_ref;

> int *a;

> void

> fn1 (int N)

> {

>   for (int i = 0; i < N; i++)

>     {

>       df_ref b;

>       a[(b)->base.regno]++;

>     }

> }


Well, in this case it is UB too, just not actually out of bounds access,
but use of uninitialized variable.
Perhaps what we should do, in addition to turning ASAN_MARK (POISON, &b, ...)
into b = ASAN_POISON (); turn ASAN_MARK (UNPOISON, &b, ...) into
b = b_YYY(D);
The following seems to do the job:

	Jakub--- gcc/tree-ssa.c.jj	2017-01-19 17:20:15.000000000 +0100
+++ gcc/tree-ssa.c	2017-01-19 17:29:58.015356370 +0100
@@ -1911,7 +1911,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;
 		      }
 		  }

Martin Liška Jan. 20, 2017, 11:49 a.m. UTC | #8
On 01/19/2017 05:33 PM, Jakub Jelinek wrote:
> On Wed, Jan 18, 2017 at 04:34:48PM +0100, Martin Liška wrote:

>> Hello.

>>

>> During bootstrap, I came to following test-case:

>>

>> struct A

>> {

>>   int regno;

>> };

>> struct

>> {

>>   A base;

>> } typedef *df_ref;

>> int *a;

>> void

>> fn1 (int N)

>> {

>>   for (int i = 0; i < N; i++)

>>     {

>>       df_ref b;

>>       a[(b)->base.regno]++;

>>     }

>> }

> 

> Well, in this case it is UB too, just not actually out of bounds access,

> but use of uninitialized variable.

> Perhaps what we should do, in addition to turning ASAN_MARK (POISON, &b, ...)

> into b = ASAN_POISON (); turn ASAN_MARK (UNPOISON, &b, ...) into

> b = b_YYY(D);


Great, thanks a lot. I'm going to re-trigger asan-bootstrap with your patch.
I'm also adding gcc/testsuite/gcc.dg/asan/use-after-scope-10.c that is a valid
test-case for this issue.

Hopefully it will survive both regression tests and asan-bootstrap.

Thanks,
Martin


> The following seems to do the job:

> --- gcc/tree-ssa.c.jj	2017-01-19 17:20:15.000000000 +0100

> +++ gcc/tree-ssa.c	2017-01-19 17:29:58.015356370 +0100

> @@ -1911,7 +1911,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;

>  		      }

>  		  }

> 

> 	Jakub

>From fa8a7fa81df7cf775dcf9018911044e5a331570d Mon Sep 17 00:00:00 2001

From: marxin <mliska@suse.cz>

Date: Tue, 17 Jan 2017 16:49:29 +0100
Subject: [PATCH] use-after-scope: handle writes to a poisoned variable

gcc/testsuite/ChangeLog:

2017-01-17  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/use-after-scope-10.c: New test.
	* g++.dg/asan/use-after-scope-5.C: New test.

gcc/ChangeLog:

2017-01-16  Jakub Jelinek  <jakub@redhat.com>

	* 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.
---
 gcc/asan.c                                     | 19 +++++++++++-------
 gcc/internal-fn.c                              |  8 ++++++++
 gcc/internal-fn.def                            |  1 +
 gcc/testsuite/g++.dg/asan/use-after-scope-5.C  | 23 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-10.c | 22 +++++++++++++++++++++
 gcc/tree-into-ssa.c                            | 27 +++++++++++++++++++++++++-
 gcc/tree-ssa-dce.c                             | 16 +++++++++++----
 gcc/tree-ssa.c                                 | 11 ++++++++++-
 8 files changed, 114 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-5.C
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-10.c

diff --git a/gcc/asan.c b/gcc/asan.c
index fe117a6951a..486ebfdb6af 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -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);
 	}
     }
 
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 71be382ab8b..a4a2995f58b 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -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
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 7b28b6722ff..fd25a952299 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -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)
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
index 00000000000..7e28fc35e6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-5.C
@@ -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
index 00000000000..24de8cec1ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-10.c
@@ -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/tree-into-ssa.c b/gcc/tree-into-ssa.c
index c7df237d57f..22261c15dc2 100644
--- a/gcc/tree-into-ssa.c
+++ b/gcc/tree-into-ssa.c
@@ -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);
diff --git a/gcc/tree-ssa-dce.c b/gcc/tree-ssa-dce.c
index 4e51e699d49..5ebe57b0983 100644
--- a/gcc/tree-ssa-dce.c
+++ b/gcc/tree-ssa-dce.c
@@ -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))
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index cc920950bab..5bd9004e715 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1886,7 +1886,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;
 		      }
 		  }
-- 
2.11.0


Martin Liška Jan. 20, 2017, 2:08 p.m. UTC | #9
On 01/20/2017 12:49 PM, Martin Liška wrote:
> Great, thanks a lot. I'm going to re-trigger asan-bootstrap with your patch.

> I'm also adding gcc/testsuite/gcc.dg/asan/use-after-scope-10.c that is a valid

> test-case for this issue.


Hi.

Unfortunately this way would not work as clobber marks content of the memory as uninitialize
is different behavior that just marking a memory can be used (and maybe already contains a value).

This shows the problem:

#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;
    }
}

With the patch, DSE would optimize out '*here = &cc;' and thus aborts. The problem is definitely
related to goto magic, where we are more defensive in placement of ASAN_MARK(UNPOISON,...).
Hope your optimization is still valid for situations w/o artificial ASAN_MARK(UNPOISON,...) placed due
to goto magic.

Do we still want to do it now, or postponing to GCC 8 would be better option?

Thanks,
Martin
Jakub Jelinek Jan. 20, 2017, 2:27 p.m. UTC | #10
On Fri, Jan 20, 2017 at 03:08:21PM +0100, Martin Liška wrote:
> Unfortunately this way would not work as clobber marks content of the memory as uninitialize

> is different behavior that just marking a memory can be used (and maybe already contains a value).

> 

> This shows the problem:

> 

> #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;

>     }

> }

> 

> With the patch, DSE would optimize out '*here = &cc;' and thus aborts. The problem is definitely

> related to goto magic, where we are more defensive in placement of ASAN_MARK(UNPOISON,...).

> Hope your optimization is still valid for situations w/o artificial ASAN_MARK(UNPOISON,...) placed due

> to goto magic.

> 

> Do we still want to do it now, or postponing to GCC 8 would be better option?


I'd still like to resolve it for GCC 7 if at all possible, I think otherwise
-fsanitize=address is by default unnecessarily slower (so it is a regression
anyway).
So, do we always amit ASAN_MARK(UNPOISON, ...) at the start of scope and
then yet another ASAN_MARK(UNPOISON, ...) at the goto destination?
At least on the above testcase that is the case, so if we say split
ASAN_MARK_UNPOISON into something that is used at the start of scope
(we'd emit the clobber for those) and others (we would not), then perhaps we
could get around that.  The above is BTW a clear case where shouldn't emit
UNPOISON on the label, as the goto doesn't cross its initialization.
But I can see with gotos from outside of some var's scope into it we
wouldn't handle it properly.  Perhaps for now set some
flag/attribute/whatever on vars for which we emit the conservative
UNPOISON and never allow those to be made non-addressable (i.e. for those
say that POISON/UNPOISON actually makes them always addressable)?

	Jakub
Markus Trippelsdorf Jan. 20, 2017, 2:34 p.m. UTC | #11
On 2017.01.20 at 15:27 +0100, Jakub Jelinek wrote:
> On Fri, Jan 20, 2017 at 03:08:21PM +0100, Martin Liška wrote:

> > Unfortunately this way would not work as clobber marks content of the memory as uninitialize

> > is different behavior that just marking a memory can be used (and maybe already contains a value).

> > 

> > This shows the problem:

> > 

> > #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;

> >     }

> > }

> > 

> > With the patch, DSE would optimize out '*here = &cc;' and thus aborts. The problem is definitely

> > related to goto magic, where we are more defensive in placement of ASAN_MARK(UNPOISON,...).

> > Hope your optimization is still valid for situations w/o artificial ASAN_MARK(UNPOISON,...) placed due

> > to goto magic.

> > 

> > Do we still want to do it now, or postponing to GCC 8 would be better option?

> 

> I'd still like to resolve it for GCC 7 if at all possible, I think otherwise

> -fsanitize=address is by default unnecessarily slower (so it is a regression

> anyway).


Another possibility would be to disable use-after-scope for gcc-7 (like
LLVM) and re-enable it for gcc-8.


-- 
Markusdiff --git a/gcc/opts.c b/gcc/opts.c
index 5f573a16ff15..2664b54133e4 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -993,7 +993,7 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
      enabled.  */
   if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
       && !opts_set->x_flag_sanitize_address_use_after_scope)
-    opts->x_flag_sanitize_address_use_after_scope = true;
+    opts->x_flag_sanitize_address_use_after_scope = false;

   /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
      is enabled.  */

Martin Liška Jan. 23, 2017, 9:19 a.m. UTC | #12
On 01/20/2017 03:27 PM, Jakub Jelinek wrote:
> On Fri, Jan 20, 2017 at 03:08:21PM +0100, Martin Liška wrote:

>> Unfortunately this way would not work as clobber marks content of the memory as uninitialize

>> is different behavior that just marking a memory can be used (and maybe already contains a value).

>>

>> This shows the problem:

>>

>> #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;

>>     }

>> }

>>

>> With the patch, DSE would optimize out '*here = &cc;' and thus aborts. The problem is definitely

>> related to goto magic, where we are more defensive in placement of ASAN_MARK(UNPOISON,...).

>> Hope your optimization is still valid for situations w/o artificial ASAN_MARK(UNPOISON,...) placed due

>> to goto magic.

>>

>> Do we still want to do it now, or postponing to GCC 8 would be better option?

> 

> I'd still like to resolve it for GCC 7 if at all possible, I think otherwise

> -fsanitize=address is by default unnecessarily slower (so it is a regression

> anyway).


Good, I hope I have patch that finally works as we want. It add attribute to variables that
are unpoisoned as live switch variables, or are defined in a label which address is taken.

With [1] I can bootstrap-asan and the patch can bootstrap on ppc64le-redhat-linux
and survives regression tests.

I'm sending latest diff, as well as the final pair of patches I would like to install.

Ready to be installed?
Martin

> So, do we always amit ASAN_MARK(UNPOISON, ...) at the start of scope and

> then yet another ASAN_MARK(UNPOISON, ...) at the goto destination?

> At least on the above testcase that is the case, so if we say split

> ASAN_MARK_UNPOISON into something that is used at the start of scope

> (we'd emit the clobber for those) and others (we would not), then perhaps we

> could get around that.  The above is BTW a clear case where shouldn't emit

> UNPOISON on the label, as the goto doesn't cross its initialization.

> But I can see with gotos from outside of some var's scope into it we

> wouldn't handle it properly.  Perhaps for now set some

> flag/attribute/whatever on vars for which we emit the conservative

> UNPOISON and never allow those to be made non-addressable (i.e. for those

> say that POISON/UNPOISON actually makes them always addressable)?

> 

> 	Jakub

> 


[1] https://gcc.gnu.org/ml/gcc-patches/2017-01/msg01446.htmldiff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 2777a23eb93..1b076fdf45c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1206,8 +1206,19 @@ 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.  */
+      DECL_ATTRIBUTES (var)
+	= tree_cons (get_identifier ("use_after_scope_memory"),
+		     build_int_cst (integer_type_node, 1),
+		     DECL_ATTRIBUTES (var));
+    }
 }
 
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 5bd9004e715..2b33da93a23 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1565,6 +1565,10 @@ is_asan_mark_p (gimple *stmt)
       && VAR_P (TREE_OPERAND (addr, 0)))
     {
       tree var = TREE_OPERAND (addr, 0);
+      if (lookup_attribute ("use_after_scope_memory",
+			    DECL_ATTRIBUTES (var)))
+	return false;
+
       unsigned addressable = TREE_ADDRESSABLE (var);
       TREE_ADDRESSABLE (var) = 0;
       bool r = is_gimple_reg (var);

Jakub Jelinek Jan. 23, 2017, 9:38 a.m. UTC | #13
On Mon, Jan 23, 2017 at 10:19:33AM +0100, Martin Liška wrote:
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c

> index 2777a23eb93..1b076fdf45c 100644

> --- a/gcc/gimplify.c

> +++ b/gcc/gimplify.c

> @@ -1206,8 +1206,19 @@ 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.  */

> +      DECL_ATTRIBUTES (var)

> +	= tree_cons (get_identifier ("use_after_scope_memory"),


Please use "use after scope memory" to make it clear it is internal
only attribute users can't specify.
Also, can't asan_poison_variables be performed on the same var
multiple times (multiple labels, or switches, ...)?
If yes, then the addition of the attribute should be guarded
with if (!lookup_attribute ("use after scope memory", DECL_ATTRIBUTES (vars)))
so that you don't add the attributes many times.

> +		     build_int_cst (integer_type_node, 1),

> +		     DECL_ATTRIBUTES (var));


Please use:
		     integer_one_node, DECL_ATTRIBUTES (var));
instead.

> --- a/gcc/tree-ssa.c

> +++ b/gcc/tree-ssa.c

> @@ -1565,6 +1565,10 @@ is_asan_mark_p (gimple *stmt)

>        && VAR_P (TREE_OPERAND (addr, 0)))

>      {

>        tree var = TREE_OPERAND (addr, 0);

> +      if (lookup_attribute ("use_after_scope_memory",

> +			    DECL_ATTRIBUTES (var)))

> +	return false;


See above.

Patchset is ok for trunk with these nits fixed, thanks.

	Jakub
Martin Liška Jan. 23, 2017, 12:07 p.m. UTC | #14
On 01/23/2017 10:38 AM, Jakub Jelinek wrote:
> On Mon, Jan 23, 2017 at 10:19:33AM +0100, Martin Liška wrote:

>> diff --git a/gcc/gimplify.c b/gcc/gimplify.c

>> index 2777a23eb93..1b076fdf45c 100644

>> --- a/gcc/gimplify.c

>> +++ b/gcc/gimplify.c

>> @@ -1206,8 +1206,19 @@ 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.  */

>> +      DECL_ATTRIBUTES (var)

>> +	= tree_cons (get_identifier ("use_after_scope_memory"),

> 

> Please use "use after scope memory" to make it clear it is internal

> only attribute users can't specify.

> Also, can't asan_poison_variables be performed on the same var

> multiple times (multiple labels, or switches, ...)?

> If yes, then the addition of the attribute should be guarded

> with if (!lookup_attribute ("use after scope memory", DECL_ATTRIBUTES (vars)))

> so that you don't add the attributes many times.

> 

>> +		     build_int_cst (integer_type_node, 1),

>> +		     DECL_ATTRIBUTES (var));

> 

> Please use:

> 		     integer_one_node, DECL_ATTRIBUTES (var));

> instead.

> 

>> --- a/gcc/tree-ssa.c

>> +++ b/gcc/tree-ssa.c

>> @@ -1565,6 +1565,10 @@ is_asan_mark_p (gimple *stmt)

>>        && VAR_P (TREE_OPERAND (addr, 0)))

>>      {

>>        tree var = TREE_OPERAND (addr, 0);

>> +      if (lookup_attribute ("use_after_scope_memory",

>> +			    DECL_ATTRIBUTES (var)))

>> +	return false;

> 

> See above.

> 

> Patchset is ok for trunk with these nits fixed, thanks.

> 

> 	Jakub

> 


Great, installed as r244791 and r244793. One more time, really thank
you for help, it took some time to finalize the optimization.

Martin
Thomas Schwinge Jan. 26, 2017, 8:57 a.m. UTC | #15
Hi!

On Mon, 23 Jan 2017 10:19:33 +0100, Martin Liška <mliska@suse.cz> wrote:
> --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> @@ -1,5 +1,6 @@

>  // { dg-do run }

>  // { dg-shouldfail "asan" }

> +// { dg-additional-options "-O0" }


As these tests per "gcc/testsuite/gcc.dg/asan/asan.exp" are run with
"gcc-dg-runtest", which will "cycle through a list of optimization
options as c-torture does", is it really appropriate to hard-code "-O0"
here?  Shouldn't instead be all testing be "dg-skip"ped (or similar)
unless "-O0" is in effect?

> --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c

> +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c

> @@ -1,5 +1,6 @@

>  // { dg-do run }

>  // { dg-shouldfail "asan" }

> +// { dg-additional-options "-O2 -fdump-tree-asan1" }


Likewise.

(I didn't check any other such test cases.)

> +// { dg-final { scan-tree-dump-times "= ASAN_POISON \\(\\)" 1 "asan1" } }

>  // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }

>  // { dg-output "READ of size .*" }

>  // { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" }


This is PASS for most of all tortute test options, but for "-O2 -flto
-fuse-linker-plugin -fno-fat-lto-objects" will produce:

    PASS: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
    PASS: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  execution test
    PASS: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  output pattern test, ERROR: AddressSanitizer: stack-use-after-scope on address.*(
    |
    |^M)READ of size .*.*'a' <== Memory access at offset [0-9]* is inside this variable.*
    {+UNRESOLVED: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects   scan-tree-dump-times asan1 "= ASAN_POISON \\(\\)" 1+}

(Notice UNRESOLVED.)  Is this to be expected/skipped/fixed?


Grüße
 Thomas
Jakub Jelinek Jan. 26, 2017, 10:54 a.m. UTC | #16
On Thu, Jan 26, 2017 at 09:57:14AM +0100, Thomas Schwinge wrote:
> Hi!

> 

> On Mon, 23 Jan 2017 10:19:33 +0100, Martin Liška <mliska@suse.cz> wrote:

> > --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> > +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> > @@ -1,5 +1,6 @@

> >  // { dg-do run }

> >  // { dg-shouldfail "asan" }

> > +// { dg-additional-options "-O0" }

> 

> As these tests per "gcc/testsuite/gcc.dg/asan/asan.exp" are run with

> "gcc-dg-runtest", which will "cycle through a list of optimization

> options as c-torture does", is it really appropriate to hard-code "-O0"

> here?  Shouldn't instead be all testing be "dg-skip"ped (or similar)

> unless "-O0" is in effect?


Indeed, I see
UNRESOLVED: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects   scan-tree-dump-times asan1 "= ASAN_POISON \\\\(\\\\)" 1
too.

For that kind of thing, the standard way is to add -ffat-lto-objects.
As for -O* in */asan/* tests, that is indeed a bug, most tests do it right.

The following patch should fix that, ok for trunk?

2017-01-26  Jakub Jelinek  <jakub@redhat.com>

	* c-c++-common/asan/pr63316.c: Use dg-skip-if instead of dg-options.
	* c-c++-common/asan/misalign-1.c: Likewise.
	* c-c++-common/asan/misalign-2.c: Likewise.
	* g++.dg/asan/pr69276.C: Add dg-skip-if, remove -O0 from
	dg-additional-options.
	* gcc.dg/asan/pr66314.c: Remove -Os from dg-options, add dg-skip-if.
	* gcc.dg/asan/use-after-scope-3.c: Use dg-skip-if instead of dg-options.
	* gcc.dg/asan/use-after-scope-9.c: Add dg-skip-if, remove -O2 and
	add -ffat-lto-objects from/to dg-additional-options.
	* gcc.dg/asan/use-after-scope-10.c: Add dg-skip-if, remove -O2 from
	dg-additional-options.



	Jakub--- gcc/testsuite/c-c++-common/asan/pr63316.c.jj	2014-09-24 11:13:43.000000000 +0200
+++ gcc/testsuite/c-c++-common/asan/pr63316.c	2017-01-26 11:38:32.172060026 +0100
@@ -1,6 +1,6 @@
 /* PR sanitizer/63316 */
 /* { dg-do run } */
-/* { dg-options "-fsanitize=address -O2" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
 
 #ifdef __cplusplus
 extern "C" {
--- gcc/testsuite/c-c++-common/asan/misalign-1.c.jj	2014-11-20 08:32:14.000000000 +0100
+++ gcc/testsuite/c-c++-common/asan/misalign-1.c	2017-01-26 11:37:57.964508495 +0100
@@ -1,5 +1,5 @@
 /* { dg-do run { target { ilp32 || lp64 } } } */
-/* { dg-options "-O2" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
 /* { dg-additional-options "-fno-omit-frame-pointer" { target *-*-darwin* } } */
 /* { dg-shouldfail "asan" } */
 
--- gcc/testsuite/c-c++-common/asan/misalign-2.c.jj	2014-11-20 08:32:14.000000000 +0100
+++ gcc/testsuite/c-c++-common/asan/misalign-2.c	2017-01-26 11:38:06.756393231 +0100
@@ -1,5 +1,5 @@
 /* { dg-do run { target { ilp32 || lp64 } } } */
-/* { dg-options "-O2" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } } */
 /* { dg-additional-options "-fno-omit-frame-pointer" { target *-*-darwin* } } */
 /* { dg-shouldfail "asan" } */
 
--- gcc/testsuite/g++.dg/asan/pr69276.C.jj	2016-02-04 23:14:18.000000000 +0100
+++ gcc/testsuite/g++.dg/asan/pr69276.C	2017-01-26 11:40:10.490771046 +0100
@@ -1,6 +1,7 @@
 /* { dg-do run } */
 /* { dg-shouldfail "asan" } */
-/* { dg-additional-options "-O0 -fno-lto" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" } } */
+/* { dg-additional-options "-fno-lto" } */
 
 #include <stdlib.h>
 
--- gcc/testsuite/gcc.dg/asan/pr66314.c.jj	2015-08-24 18:26:58.000000000 +0200
+++ gcc/testsuite/gcc.dg/asan/pr66314.c	2017-01-26 11:31:13.234814878 +0100
@@ -1,5 +1,6 @@
 /* { dg-do compile } */
-/* { dg-options "-std=gnu89 -Os -fprofile-arcs -fno-sanitize=all -fsanitize=kernel-address" } */
+/* { dg-options "-std=gnu89 -fprofile-arcs -fno-sanitize=all -fsanitize=kernel-address" } */
+/* { dg-skip-if "" { *-*-* }  { "*" } { "-Os" } } */
 
 char *a;
 int d;
--- gcc/testsuite/gcc.dg/asan/use-after-scope-3.c.jj	2017-01-23 18:09:36.000000000 +0100
+++ gcc/testsuite/gcc.dg/asan/use-after-scope-3.c	2017-01-26 11:36:56.510314173 +0100
@@ -1,6 +1,6 @@
 // { dg-do run }
 // { dg-shouldfail "asan" }
-// { dg-additional-options "-O0" }
+// { dg-skip-if "" { *-*-* }  { "*" } { "-O0" } }
 
 int
 main (void)
--- gcc/testsuite/gcc.dg/asan/use-after-scope-9.c.jj	2017-01-23 18:09:36.000000000 +0100
+++ gcc/testsuite/gcc.dg/asan/use-after-scope-9.c	2017-01-26 11:37:03.891217408 +0100
@@ -1,6 +1,7 @@
 // { dg-do run }
 // { dg-shouldfail "asan" }
-// { dg-additional-options "-O2 -fdump-tree-asan1" }
+// { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } }
+// { dg-additional-options "-fdump-tree-asan1 -ffat-lto-objects" }
 
 int
 main (int argc, char **argv)
--- gcc/testsuite/gcc.dg/asan/use-after-scope-10.c.jj	2017-01-23 18:09:36.000000000 +0100
+++ gcc/testsuite/gcc.dg/asan/use-after-scope-10.c	2017-01-26 11:36:43.924479176 +0100
@@ -1,6 +1,7 @@
 // { dg-do run }
 // { dg-shouldfail "asan" }
-// { dg-additional-options "-O2 -fdump-tree-asan1" }
+// { dg-skip-if "" { *-*-* }  { "*" } { "-O2" } }
+// { dg-additional-options "-fdump-tree-asan1" }
 
 int
 main (int argc, char **argv)

Thomas Schwinge Jan. 26, 2017, 8:44 p.m. UTC | #17
Hi!

On Thu, 26 Jan 2017 11:54:07 +0100, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Jan 26, 2017 at 09:57:14AM +0100, Thomas Schwinge wrote:

> > On Mon, 23 Jan 2017 10:19:33 +0100, Martin Liška <mliska@suse.cz> wrote:

> > > --- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> > > +++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c

> > > @@ -1,5 +1,6 @@

> > >  // { dg-do run }

> > >  // { dg-shouldfail "asan" }

> > > +// { dg-additional-options "-O0" }

> > 

> > As these tests per "gcc/testsuite/gcc.dg/asan/asan.exp" are run with

> > "gcc-dg-runtest", which will "cycle through a list of optimization

> > options as c-torture does", is it really appropriate to hard-code "-O0"

> > here?  Shouldn't instead be all testing be "dg-skip"ped (or similar)

> > unless "-O0" is in effect?

> 

> Indeed, I see

> UNRESOLVED: gcc.dg/asan/use-after-scope-9.c   -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects   scan-tree-dump-times asan1 "= ASAN_POISON \\\\(\\\\)" 1

> too.

> 

> For that kind of thing, the standard way is to add -ffat-lto-objects.

> As for -O* in */asan/* tests, that is indeed a bug, most tests do it right.

> 

> The following patch should fix that, ok for trunk?


Looks good to me (but I can't approve it, as you know).

One additional comment:

> --- gcc/testsuite/g++.dg/asan/pr69276.C.jj	2016-02-04 23:14:18.000000000 +0100

> +++ gcc/testsuite/g++.dg/asan/pr69276.C	2017-01-26 11:40:10.490771046 +0100

> @@ -1,6 +1,7 @@

>  /* { dg-do run } */

>  /* { dg-shouldfail "asan" } */

> -/* { dg-additional-options "-O0 -fno-lto" } */

> +/* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" } } */

> +/* { dg-additional-options "-fno-lto" } */


Probably can get rid of that "-fno-lto", too, as "-flto" is not used
together with "-O0"?


Grüße
 Thomas
Jakub Jelinek Jan. 26, 2017, 8:47 p.m. UTC | #18
On Thu, Jan 26, 2017 at 09:44:14PM +0100, Thomas Schwinge wrote:
> > The following patch should fix that, ok for trunk?

> 

> Looks good to me (but I can't approve it, as you know).


Sure, I know.
> 

> One additional comment:

> 

> > --- gcc/testsuite/g++.dg/asan/pr69276.C.jj	2016-02-04 23:14:18.000000000 +0100

> > +++ gcc/testsuite/g++.dg/asan/pr69276.C	2017-01-26 11:40:10.490771046 +0100

> > @@ -1,6 +1,7 @@

> >  /* { dg-do run } */

> >  /* { dg-shouldfail "asan" } */

> > -/* { dg-additional-options "-O0 -fno-lto" } */

> > +/* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" } } */

> > +/* { dg-additional-options "-fno-lto" } */

> 

> Probably can get rid of that "-fno-lto", too, as "-flto" is not used

> together with "-O0"?


Yeah, or:
/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */

	Jakub
diff mbox

Patch

From 66fabb9d15ebfb21e25b4fc81bad8deb6877e198 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 19 Dec 2016 15:36:11 +0100
Subject: [PATCH] Speed up use-after-scope (v2): rewrite into SSA

gcc/ChangeLog:

2016-12-22  Martin Liska  <mliska@suse.cz>

	* asan.c (create_asan_shadow_var): New function.
	(asan_expand_poison_ifn): Likewise.
	* asan.h (asan_expand_poison_ifn): New declaration.
	* internal-fn.c (expand_ASAN_POISON): Likewise.
	* internal-fn.def (ASAN_POISON): New builtin.
	* sanopt.c (pass_sanopt::execute): Expand
	asan_expand_poison_ifn.
	* tree-inline.c (copy_decl_for_dup_finish): Make function
	external.
	* tree-inline.h (copy_decl_for_dup_finish): Likewise.
	* tree-ssa.c (is_asan_mark_p): New function.
	(execute_update_addresses_taken): Rewrite local variables
	(identified just by use-after-scope as addressable) into SSA.

gcc/testsuite/ChangeLog:

2016-12-22  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/use-after-scope-3.c: Add additional flags.
	* gcc.dg/asan/use-after-scope-9.c: Likewise and grep for
	sanopt optimization for ASAN_POISON.
---
 gcc/asan.c                                    | 109 +++++++++++++++++++++++++-
 gcc/asan.h                                    |   2 +
 gcc/internal-fn.c                             |   7 ++
 gcc/internal-fn.def                           |   1 +
 gcc/sanopt.c                                  |  11 +++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c |   1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c |   2 +
 gcc/tree-inline.c                             |   2 +-
 gcc/tree-inline.h                             |   1 +
 gcc/tree-ssa.c                                |  69 +++++++++++++---
 10 files changed, 193 insertions(+), 12 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 53acff0a2fb..187934ad11b 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -32,8 +32,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "memmodel.h"
 #include "tm_p.h"
+#include "ssa.h"
 #include "stringpool.h"
-#include "tree-vrp.h"
 #include "tree-ssanames.h"
 #include "optabs.h"
 #include "emit-rtl.h"
@@ -59,6 +59,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "builtins.h"
 #include "fnmatch.h"
+#include "tree-inline.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -3055,6 +3056,112 @@  asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+/* Create ASAN shadow variable for a VAR_DECL which has been rewritten
+   into SSA.  Already seen VAR_DECLs are stored in SHADOW_VARS_MAPPING.  */
+
+static tree
+create_asan_shadow_var (tree var_decl,
+			hash_map<tree, tree> &shadow_vars_mapping)
+{
+  tree *slot = shadow_vars_mapping.get (var_decl);
+  if (slot == NULL)
+    {
+      tree shadow_var = copy_node (var_decl);
+
+      copy_body_data id;
+      memset (&id, 0, sizeof (copy_body_data));
+      id.src_fn = id.dst_fn = current_function_decl;
+      copy_decl_for_dup_finish (&id, var_decl, shadow_var);
+
+      DECL_ARTIFICIAL (shadow_var) = 1;
+      DECL_IGNORED_P (shadow_var) = 1;
+      DECL_SEEN_IN_BIND_EXPR_P (shadow_var) = 0;
+      gimple_add_tmp_var (shadow_var);
+
+      shadow_vars_mapping.put (var_decl, shadow_var);
+      return shadow_var;
+    }
+  else
+    return *slot;
+}
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+			bool *need_commit_edge_insert,
+			hash_map<tree, tree> &shadow_vars_mapping)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree shadow_var  = create_asan_shadow_var (SSA_NAME_VAR (poisoned_var),
+					     shadow_vars_mapping);
+
+  bool recover_p;
+  if (flag_sanitize & SANITIZE_USER_ADDRESS)
+    recover_p = (flag_sanitize_recover & SANITIZE_USER_ADDRESS) != 0;
+  else
+    recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_ADDRESS) != 0;
+  tree size = DECL_SIZE_UNIT (shadow_var);
+  gimple *poison_call
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node,
+						 ASAN_MARK_POISON),
+				  build_fold_addr_expr (shadow_var), size);
+
+  use_operand_p use_p;
+  imm_use_iterator imm_iter;
+  FOR_EACH_IMM_USE_FAST (use_p, 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),
+				    &nargs);
+
+      gcall *call = gimple_build_call (fun, 1,
+				       build_fold_addr_expr (shadow_var));
+      gimple_set_location (call, gimple_location (use));
+      gimple *call_to_insert = call;
+
+      /* The USE can be a gimple PHI node.  If so, insert the call on
+	 all edges leading to the PHI node.  */
+      if (is_a <gphi *> (use))
+	{
+	  gphi *phi = dyn_cast<gphi *> (use);
+	  for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+	    if (gimple_phi_arg_def (phi, i) == poisoned_var)
+	      {
+		edge e = gimple_phi_arg_edge (phi, i);
+
+		if (call_to_insert == NULL)
+		  call_to_insert = gimple_copy (call);
+
+		gsi_insert_seq_on_edge (e, call_to_insert);
+		*need_commit_edge_insert = true;
+		call_to_insert = NULL;
+	      }
+	}
+      else
+	{
+	  gimple_stmt_iterator gsi = gsi_for_stmt (use);
+	  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+	}
+    }
+
+  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
+  SSA_NAME_DEF_STMT (poisoned_var) = gimple_build_nop ();
+  gsi_replace (iter, poison_call, false);
+
+  return true;
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
diff --git a/gcc/asan.h b/gcc/asan.h
index 355a350bfeb..3800aabaf8b 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -30,6 +30,8 @@  extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
 extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool);
 extern bool asan_expand_mark_ifn (gimple_stmt_iterator *);
+extern bool asan_expand_poison_ifn (gimple_stmt_iterator *, bool *,
+				    hash_map<tree, tree> &);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index b1dbc988b9c..40f5fe6c69c 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -380,6 +380,13 @@  expand_ASAN_MARK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_POISON (internal_fn, gcall *)
+{
+  gcc_unreachable ();
+}
 
 /* This should get expanded in the tsan pass.  */
 
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 9a03e17be23..81a6bbdd15c 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -167,6 +167,7 @@  DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 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 (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)
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index ae716cffcf4..a19c3a1b6dd 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -894,6 +894,8 @@  pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  hash_map<tree, tree> shadow_vars_mapping;
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -931,6 +933,11 @@  pass_sanopt::execute (function *fun)
 		case IFN_ASAN_MARK:
 		  no_next = asan_expand_mark_ifn (&gsi);
 		  break;
+		case IFN_ASAN_POISON:
+		  no_next = asan_expand_poison_ifn (&gsi,
+						    &need_commit_edge_insert,
+						    shadow_vars_mapping);
+		  break;
 		default:
 		  break;
 		}
@@ -962,6 +969,10 @@  pass_sanopt::execute (function *fun)
 	    gsi_next (&gsi);
 	}
     }
+
+  if (need_commit_edge_insert)
+    gsi_commit_edge_inserts ();
+
   return 0;
 }
 
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
index 9aeed51a770..8b11bea9940 100644
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
@@ -1,5 +1,6 @@ 
 // { dg-do run }
 // { dg-shouldfail "asan" }
+// { dg-additional-options "-O0" }
 
 int
 main (void)
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
index 2e30deffa18..5d069dd18ea 100644
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
@@ -1,5 +1,6 @@ 
 // { dg-do run }
 // { dg-shouldfail "asan" }
+// { dg-additional-options "-O2 -fdump-tree-asan1" }
 
 int
 main (int argc, char **argv)
@@ -15,6 +16,7 @@  main (int argc, char **argv)
   return *ptr;
 }
 
+// { dg-final { scan-tree-dump-times "= ASAN_POISON \\(\\)" 1 "asan1" } }
 // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
 // { dg-output "READ of size .*" }
 // { dg-output ".*'a' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 0de0b89dbbf..7666320ec00 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -5446,7 +5446,7 @@  declare_inline_vars (tree block, tree vars)
    but now it will be in the TO_FN.  PARM_TO_VAR means enable PARM_DECL to
    VAR_DECL translation.  */
 
-static tree
+tree
 copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
 {
   /* Don't generate debug information for the copy if we wouldn't have
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index 9ca2a91f08f..3e55a0ae0e5 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -218,6 +218,7 @@  extern gimple_seq copy_gimple_seq_and_replace_locals (gimple_seq seq);
 extern bool debug_find_tree (tree, tree);
 extern tree copy_fn (tree, tree&, tree&);
 extern const char *copy_forbidden (struct function *fun);
+extern tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy);
 
 /* This is in tree-inline.c since the routine uses
    data structures from the inliner.  */
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 62eea8bb8a4..3adbef48037 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -41,6 +41,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cfgexpand.h"
 #include "tree-cfg.h"
 #include "tree-dfa.h"
+#include "asan.h"
 
 /* Pointer map of variable mappings, keyed by edge.  */
 static hash_map<edge, auto_vec<edge_var_map> > *edge_var_maps;
@@ -1550,6 +1551,30 @@  maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
     }
 }
 
+/* Return true when STMT is ASAN mark where second argument is an address
+   of a local variable.  */
+
+static bool
+is_asan_mark_p (gimple *stmt)
+{
+  if (!gimple_call_internal_p (stmt, IFN_ASAN_MARK))
+    return false;
+
+  tree addr = get_base_address (gimple_call_arg (stmt, 1));
+  if (TREE_CODE (addr) == ADDR_EXPR
+      && VAR_P (TREE_OPERAND (addr, 0)))
+    {
+      tree var = TREE_OPERAND (addr, 0);
+      unsigned addressable = TREE_ADDRESSABLE (var);
+      TREE_ADDRESSABLE (var) = 0;
+      bool r = is_gimple_reg (var);
+      TREE_ADDRESSABLE (var) = addressable;
+      return r;
+    }
+
+  return false;
+}
+
 /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
 
 void
@@ -1575,17 +1600,23 @@  execute_update_addresses_taken (void)
 	  enum gimple_code code = gimple_code (stmt);
 	  tree decl;
 
-	  if (code == GIMPLE_CALL
-	      && optimize_atomic_compare_exchange_p (stmt))
+	  if (code == GIMPLE_CALL)
 	    {
-	      /* For __atomic_compare_exchange_N if the second argument
-		 is &var, don't mark var addressable;
-		 if it becomes non-addressable, we'll rewrite it into
-		 ATOMIC_COMPARE_EXCHANGE call.  */
-	      tree arg = gimple_call_arg (stmt, 1);
-	      gimple_call_set_arg (stmt, 1, null_pointer_node);
-	      gimple_ior_addresses_taken (addresses_taken, stmt);
-	      gimple_call_set_arg (stmt, 1, arg);
+	      if (optimize_atomic_compare_exchange_p (stmt))
+		{
+		  /* For __atomic_compare_exchange_N if the second argument
+		     is &var, don't mark var addressable;
+		     if it becomes non-addressable, we'll rewrite it into
+		     ATOMIC_COMPARE_EXCHANGE call.  */
+		  tree arg = gimple_call_arg (stmt, 1);
+		  gimple_call_set_arg (stmt, 1, null_pointer_node);
+		  gimple_ior_addresses_taken (addresses_taken, stmt);
+		  gimple_call_set_arg (stmt, 1, arg);
+		}
+	      else if (is_asan_mark_p (stmt))
+		;
+	      else
+		gimple_ior_addresses_taken (addresses_taken, stmt);
 	    }
 	  else
 	    /* Note all addresses taken by the stmt.  */
@@ -1841,6 +1872,24 @@  execute_update_addresses_taken (void)
 			continue;
 		      }
 		  }
+		else if (is_asan_mark_p (stmt))
+		  {
+		    tree var = TREE_OPERAND (gimple_call_arg (stmt, 1), 0);
+		    if (bitmap_bit_p (suitable_for_renaming, DECL_UID (var)))
+		      {
+			unlink_stmt_vdef (stmt);
+			if (asan_mark_p (stmt, ASAN_MARK_POISON))
+			  {
+			    gcall *call
+			      = gimple_build_call_internal (IFN_ASAN_POISON, 0);
+			    gimple_call_set_lhs (call, var);
+			    gsi_replace (&gsi, call, GSI_SAME_STMT);
+			  }
+			else
+			  gsi_remove (&gsi, true);
+			continue;
+		      }
+		  }
 		for (i = 0; i < gimple_call_num_args (stmt); ++i)
 		  {
 		    tree *argp = gimple_call_arg_ptr (stmt, i);
-- 
2.11.0