public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH, RFC] Introduce -fsanitize=use-after-scope
@ 2016-05-06 11:04 Martin Liška
  2016-05-06 11:08 ` [PATCH] Introduce tests for -fsanitize=use-after-scope Martin Liška
                   ` (3 more replies)
  0 siblings, 4 replies; 111+ messages in thread
From: Martin Liška @ 2016-05-06 11:04 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 4419 bytes --]

Hello.

I've started working on the patch couple of month go, basically after
a brief discussion with Jakub on IRC.

I'm sending the initial version which can successfully run instrumented
tramp3d, postgresql server and Inkscape. It catches the basic set of
examples which are added in following patch.

The implementation is quite straightforward as works in following steps:

1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
and the variable is marked as addressable
3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
4) At the very end of the function, we clean up the poisoned memory
5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)
6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library

Example:

int
main (void)
{
  char *ptr;
  {
    char my_char[9];
    ptr = &my_char[0];
  }

  *(ptr+9) = 'c';
}

./a.out 
=================================================================
==12811==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffec9bcff69 at pc 0x000000400a73 bp 0x7ffec9bcfef0 sp 0x7ffec9bcfee8
WRITE of size 1 at 0x7ffec9bcff69 thread T0
    #0 0x400a72 in main (/tmp/a.out+0x400a72)
    #1 0x7f100824860f in __libc_start_main (/lib64/libc.so.6+0x2060f)
    #2 0x400868 in _start (/tmp/a.out+0x400868)

Address 0x7ffec9bcff69 is located in stack of thread T0 at offset 105 in frame
    #0 0x400945 in main (/tmp/a.out+0x400945)

  This frame has 2 object(s):
    [32, 40) 'ptr'
    [96, 105) 'my_char' <== Memory access at offset 105 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400a72) in main
Shadow bytes around the buggy address:
  0x100059371f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059371fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059371fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059371fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059371fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100059371fe0: f1 f1 f1 f1 00 f4 f4 f4 f2 f2 f2 f2 f8[f8]f4 f4
  0x100059371ff0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059372000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059372010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059372020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100059372030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==12811==ABORTING

As mentioned, it's request for comment as it still has couple of limitations:
a) VLA are not supported, which should make sense as we are unable to allocate a stack slot for that
b) we can possibly strip some instrumentation in situations where a variable is introduced in a very first BB (RTL poisoning is superfluous).
Similarly for a very last BB of a function, we can strip end of scope poisoning (and RTL unpoisoning). I'll do that incrementally.
c) We require -fstack-reuse=none option, maybe it worth to warn a user if -fsanitize=use-after-scope is provided without the option?
d) An instrumented binary is quite slow (~20x for tramp3d) as every function call produces many memory read/writes. I'm wondering whether
we should provide a faster alternative (like instrument just variables that have address taken) ?

Patch can survive bootstrap and regression tests on x86_64-linux-gnu.

Thanks for feedback.
Martin

[-- Attachment #2: 0001-Introduce-fsanitize-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 26301 bytes --]

From 242bcaf2fa7777ded33291d05a5c4c5306f849de Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize=use-after-scope

gcc/ChangeLog:

2016-05-03  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Cut the enum from here.
	(asan_poison_stack_variables): New function.
	(asan_emit_stack_protection): Poison stack variables.
	(asan_expand_mark_ifn): New function.
	* asan.h (enum asan_mark_flags): Paste here the enum from source
	file.
	(asan_sanitize_stack_p): Move function from cfgexpand.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): New function.
	* flag-types.h (enum sanitize_code): Introduce new enum value.
	* gimplify.c (asan_poison_variable): New function.
	(gimplify_bind_expr): Poison all local variables that go out
	of a scope.
	(gimplify_decl_expr): Unpoison a local vairable and mark the
	variable to live on a stack.
	(gimplify_expr): Unclobber all seen variables that can be
	poisoned due to a jump to a label.
	(gimplify_function_tree):
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def: Define ASAN_MARK.
	* opts.c: Introduce use-after-scope as a new option value
	for -fsanitize.
	* sanitizer.def: Declare two new sanitizer builtins
	(__asan_poison_stack_memory, __asan_unpoison_stack_memory).
	* sanopt.c (pass_sanopt::execute): Call expansion of ASAN_MARKs.
	* tree-inline.c (setup_one_parameter): Record local variables
	introduced by inlining.
	(copy_decl_no_change): Likewise.
	* builtins.c: Include params.h header file.
	* gimplify.c: Include params.h header file.
	* opts-global.c: Include params.h header file.
	* sancov.c: Include params.h header file.
	* sanopt.c: Include params.h header file.
	* tree-streamer-in.c: Include params.h header file.
	* tsan.c: Include params.h header file.
	* ubsan.c: Include params.h header file.
	* varasm.c: Include params.h header file.
	* cfgexpand.c (asan_sanitize_stack_p): Use
	asan_no_sanitize_address_p function.

libsanitizer/ChangeLog:

2016-05-04  Martin Liska  <mliska@suse.cz>

	* asan/asan_poisoning.cc: Do not call PoisonShadow in case
	of zero size of aligned size.

gcc/ChangeLog:

2016-05-04  Martin Liska  <mliska@suse.cz>

gcc/c-family/ChangeLog:

2016-05-04  Martin Liska  <mliska@suse.cz>

	* c-ubsan.c: Include params.h header file.

gcc/cp/ChangeLog:

2016-05-04  Martin Liska  <mliska@suse.cz>

	* decl2.c: Include params.h header file.

Partial revert + fixed.
---
 gcc/asan.c                          | 100 ++++++++++++++++++++++++++++++++----
 gcc/asan.h                          |  41 +++++++++++++++
 gcc/builtins.c                      |   1 +
 gcc/c-family/c-ubsan.c              |   1 +
 gcc/cfgexpand.c                     |   3 +-
 gcc/cp/decl2.c                      |   1 +
 gcc/flag-types.h                    |   5 +-
 gcc/gimplify.c                      |  87 +++++++++++++++++++++++++++----
 gcc/internal-fn.c                   |   9 ++++
 gcc/internal-fn.def                 |   1 +
 gcc/opts-global.c                   |   1 +
 gcc/opts.c                          |   2 +
 gcc/sancov.c                        |   1 +
 gcc/sanitizer.def                   |   4 ++
 gcc/sanopt.c                        |   5 +-
 gcc/tree-inline.c                   |  15 +++++-
 gcc/tree-streamer-in.c              |   1 +
 gcc/tree-vect-patterns.c            |   3 +-
 gcc/tsan.c                          |   1 +
 gcc/ubsan.c                         |   1 +
 gcc/varasm.c                        |   1 +
 libsanitizer/asan/asan_poisoning.cc |   5 +-
 22 files changed, 260 insertions(+), 29 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 71095fb..0c1b92a 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "stor-layout.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "dojump.h"
 #include "explow.h"
@@ -54,7 +55,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "gimple-builder.h"
 #include "ubsan.h"
-#include "params.h"
 #include "builtins.h"
 #include "fnmatch.h"
 
@@ -313,6 +313,8 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
+hash_set <tree> asan_inlined_variables;
+
 /* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
 static GTY(()) tree shadow_ptr_types[2];
@@ -320,15 +322,6 @@ static GTY(()) tree shadow_ptr_types[2];
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -1020,6 +1013,45 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
+   allocated for local variables, localted in OFFSETS.  LENGTH is number
+   of OFFSETS, BASE is the register holding the stack base,
+   against which OFFSETS array offsets are relative to.  BASE_OFFSET represents
+   an offset requested by alignment and similar stuff.  */
+
+static void
+asan_poison_stack_variables (rtx base, HOST_WIDE_INT base_offset,
+			     HOST_WIDE_INT *offsets, int length,
+			     tree *decls, bool poison)
+{
+  if (asan_sanitize_use_after_scope ())
+    for (int l = length - 2; l > 0; l -= 2)
+      {
+	tree decl = decls[l / 2 - 1];
+	if (asan_inlined_variables.contains (decl))
+	  {
+	    gcc_assert (DECL_ABSTRACT_ORIGIN (decl));
+	    continue;
+	  }
+
+	HOST_WIDE_INT var_offset = offsets[l];
+	HOST_WIDE_INT var_end_offset = offsets[l - 1];
+
+	rtx source = expand_binop (Pmode, add_optab, base,
+				   gen_int_mode
+				    (var_offset - base_offset, Pmode),
+				   NULL_RTX, 1, OPTAB_DIRECT);
+
+	rtx size = GEN_INT (var_end_offset - var_offset);
+	const char *fname = poison ?  "__asan_poison_stack_memory"
+	  :"__asan_unpoison_stack_memory";
+	rtx ret = init_one_libfunc (fname);
+	emit_library_call (ret, LCT_NORMAL, VOIDmode, 2, source, ptr_mode,
+			   size, TYPE_MODE (pointer_sized_int_node));
+      }
+}
+
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1228,6 +1260,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 	}
       cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE;
     }
+
+  /* Poison stack variables at the very beginning of a function.  */
+  asan_poison_stack_variables (base, base_offset, offsets, length,
+			       decls, true);
+
   do_pending_stack_adjust ();
 
   /* Construct epilogue sequence.  */
@@ -1309,6 +1346,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
       asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
     }
 
+  /* Unpoison stack variables at the end of a function.  As the former
+     stack memory can be reused, we have to unpoison the memory.  */
+  asan_poison_stack_variables (base, base_offset, offsets, length, decls,
+			       false);
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -2573,6 +2615,44 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  tree len = gimple_call_arg (g, 2);
+
+  HOST_WIDE_INT size_in_bytes
+    = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+				  NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_insert_before (iter, g, GSI_SAME_STMT);
+  tree base_addr = gimple_assign_lhs (g);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, len);
+  gimple_set_location (g, loc);
+  gsi_insert_before (iter, g, GSI_SAME_STMT);
+  tree sz_arg = gimple_assign_lhs (g);
+
+  tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+				    : BUILT_IN_ASAN_UNCLOBBER_N);
+  g = gimple_build_call (fun, 2, base_addr, sz_arg);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..573af2f 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,6 +37,10 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of inlined variables that cannot be poisoned by use-after-cope
+   sanitizer.  */
+extern hash_set <tree> asan_inlined_variables;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
@@ -59,6 +64,23 @@ extern alias_set_type asan_shadow_set;
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
@@ -105,4 +127,23 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return (flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
+    == SANITIZE_ADDRESS_USE_AFTER_SCOPE
+    && flag_stack_reuse == SR_NONE
+    && ASAN_STACK;
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 3d89baf..15b5553 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "value-prof.h"
 #include "builtins.h"
+#include "params.h"
 #include "asan.h"
 #include "cilk.h"
 #include "tree-chkp.h"
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index 4022bdf..1a49e58 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "ubsan.h"
 #include "c-family/c-ubsan.h"
+#include "params.h"
 #include "asan.h"
 #include "stor-layout.h"
 #include "builtins.h"
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 21f21c9..1f77cfb 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -876,8 +876,7 @@ asan_sanitize_stack_p (void)
 {
   return ((flag_sanitize & SANITIZE_ADDRESS)
 	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
+	  && !asan_no_sanitize_address_p ());
 }
 
 /* A subroutine of expand_used_vars.  Binpack the variables into
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 0ea326d..d341478 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "intl.h"
 #include "c-family/c-ada-spec.h"
+#include "params.h"
 #include "asan.h"
 
 extern cpp_reader *parse_in;
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 8201676..c7c69ba 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -253,6 +253,7 @@ enum sanitize_code {
   SANITIZE_OBJECT_SIZE = 1UL << 20,
   SANITIZE_VPTR = 1UL << 21,
   SANITIZE_BOUNDS_STRICT = 1UL << 22,
+  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -261,7 +262,9 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT,
+  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
+			| SANITIZE_USE_AFTER_SCOPE
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index e223e59..305faaa 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "params.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set <tree> asan_poisoned_variables (13);
 
 enum gimplify_omp_var_data
 {
@@ -1075,6 +1080,29 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimplify_seq_add_stmt
+    (seq_p, gimple_build_call_internal (IFN_ASAN_MARK, 3,
+					build_int_cst (integer_type_node,
+						       flags),
+					base, unit_size));
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1177,6 +1205,10 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   /* Add clobbers for all variables that go out of scope.  */
   for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
     {
+      if (asan_sanitize_use_after_scope ()
+	  && asan_poisoned_variables.contains (t))
+	asan_poisoned_variables.remove (t);
+
       if (TREE_CODE (t) == VAR_DECL
 	  && !is_global_var (t)
 	  && DECL_CONTEXT (t) == current_function_decl
@@ -1185,17 +1217,25 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  && !DECL_HAS_VALUE_EXPR_P (t)
 	  /* Only care for variables that have to be in memory.  Others
 	     will be rewritten into SSA names, hence moved to the top-level.  */
-	  && !is_gimple_reg (t)
-	  && flag_stack_reuse != SR_NONE)
+	  && !is_gimple_reg (t))
 	{
-	  tree clobber = build_constructor (TREE_TYPE (t), NULL);
-	  gimple *clobber_stmt;
-	  TREE_THIS_VOLATILE (clobber) = 1;
-	  clobber_stmt = gimple_build_assign (t, clobber);
-	  gimple_set_location (clobber_stmt, end_locus);
-	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
-
-	  if (flag_openacc && oacc_declare_returns != NULL)
+	  if (flag_stack_reuse != SR_NONE)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (t), NULL);
+	      gimple *clobber_stmt;
+	      TREE_THIS_VOLATILE (clobber) = 1;
+	      clobber_stmt = gimple_build_assign (t, clobber);
+	      gimple_set_location (clobber_stmt, end_locus);
+	      gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+	    }
+
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ())
+	      asan_poison_variable (t, true, &cleanup);
+
+	  if (flag_stack_reuse != SR_NONE
+	      && flag_openacc
+	      && oacc_declare_returns != NULL)
 	    {
 	      tree *c = oacc_declare_returns->get (t);
 	      if (c != NULL)
@@ -1467,6 +1507,21 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
 	gimplify_vla_decl (decl, seq_p);
 
+      tree unit_size = DECL_SIZE_UNIT (decl);
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && TREE_CODE (unit_size) == INTEGER_CST
+	  && DECL_NAME (decl) != NULL
+	  && IDENTIFIER_POINTER (DECL_NAME (decl)) != NULL
+	  && !TREE_STATIC (decl))
+	{
+	  TREE_ADDRESSABLE (decl) = 1;
+	  DECL_GIMPLE_REG_P (decl) = 0;
+
+	  asan_poisoned_variables.add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
+
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
 	 variables, though it would be better if the front ends would
@@ -10526,6 +10581,14 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 		      == current_function_decl);
 	  gimplify_seq_add_stmt (pre_p,
 			  gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+
+	  /* TODO: make a stable sorting of the hash table.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ())
+	    for (hash_set <tree>::iterator it
+		 = asan_poisoned_variables.begin ();
+		 it != asan_poisoned_variables.end (); ++it)
+	      asan_poison_variable (*it, false, pre_p);
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -11586,7 +11649,11 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
   bind = gimplify_body (fndecl, true);
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index e70c73a..14fd4b3 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -234,6 +234,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 a62f3e8..48b8700 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts-global.c b/gcc/opts-global.c
index b7e5232..963b542 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "toplev.h"
 #include "context.h"
+#include "params.h"
 #include "asan.h"
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 649b84b..40dcf3d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1444,6 +1444,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
   SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),
+  SANITIZER_OPT (use-after-scope, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+		 | SANITIZE_USE_AFTER_SCOPE),
   SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
   SANITIZER_OPT (thread, SANITIZE_THREAD),
   SANITIZER_OPT (leak, SANITIZE_LEAK),
diff --git a/gcc/sancov.c b/gcc/sancov.c
index f3211dd..655ff64 100644
--- a/gcc/sancov.c
+++ b/gcc/sancov.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 
 namespace {
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 2660453..43488a9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -29,9 +29,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 #include "fold-const.h"
 #include "gimple-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "ubsan.h"
-#include "params.h"
 #include "tree-hash-traits.h"
 
 
@@ -704,6 +704,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 015a907..4b6d6e2 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "builtins.h"
 #include "tree-chkp.h"
+#include "params.h"
+#include "asan.h"
 
 
 /* I'm not real happy about this, but we need to handle gimple and
@@ -3214,10 +3216,14 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
 	    }
 	}
       else
-        init_stmt = gimple_build_assign (var, rhs);
+	init_stmt = gimple_build_assign (var, rhs);
+
+      /* Record an inlined variable if we sanitize for use-after-scope.  */
+      if (asan_sanitize_use_after_scope ())
+	asan_inlined_variables.add (var);
 
       if (bb && init_stmt)
-        insert_init_stmt (id, bb, init_stmt);
+	insert_init_stmt (id, bb, init_stmt);
     }
   return init_stmt;
 }
@@ -5439,6 +5445,11 @@ copy_decl_no_change (tree decl, copy_body_data *id)
 
   copy = copy_node (decl);
 
+  if (TREE_CODE (decl) == VAR_DECL
+      && asan_inlined_variables.contains (decl)
+      && asan_sanitize_use_after_scope ())
+    asan_inlined_variables.add (copy);
+
   /* The COPY is not abstract; it will be generated in DST_FN.  */
   DECL_ABSTRACT_P (copy) = false;
   lang_hooks.dup_lang_specific_decl (copy);
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 2ad2f92..99397eb 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "ipa-chkp.h"
 #include "gomp-constants.h"
+#include "params.h"
 #include "asan.h"
 
 
diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c
index 27080d2..14a6081 100644
--- a/gcc/tree-vect-patterns.c
+++ b/gcc/tree-vect-patterns.c
@@ -3570,7 +3570,8 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
 {
   gimple *last_stmt = stmts->pop ();
   enum tree_code rhs_code;
-  tree lhs, rhs1, rhs2, tmp, rhs1_type, rhs2_type, vectype1, vectype2;
+  tree lhs = NULL_TREE, rhs1, rhs2, tmp, rhs1_type, rhs2_type;
+  tree vectype1, vectype2;
   stmt_vec_info stmt_vinfo = vinfo_for_stmt (last_stmt);
   stmt_vec_info pattern_stmt_info;
   vec_info *vinfo = stmt_vinfo->vinfo;
diff --git a/gcc/tsan.c b/gcc/tsan.c
index 47764bc..bdd002b 100644
--- a/gcc/tsan.c
+++ b/gcc/tsan.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "tree-ssa-propagate.h"
 #include "tree-ssa-loop-ivopts.h"
+#include "params.h"
 #include "tsan.h"
 #include "asan.h"
 #include "builtins.h"
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 802341e..cd4953a 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "ubsan.h"
 #include "expr.h"
+#include "params.h"
 #include "asan.h"
 #include "gimplify-me.h"
 #include "dfp.h"
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 4a7124e..47e5215 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "debug.h"
 #include "common/common-target.h"
+#include "params.h"
 #include "asan.h"
 #include "rtl-iter.h"
 
diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc
index 39f7487..bdadf37 100644
--- a/libsanitizer/asan/asan_poisoning.cc
+++ b/libsanitizer/asan/asan_poisoning.cc
@@ -292,8 +292,9 @@ uptr __asan_load_cxx_array_cookie(uptr *p) {
 static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
   if (size == 0) return;
   uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
-  PoisonShadow(addr, aligned_size,
-               do_poison ? kAsanStackUseAfterScopeMagic : 0);
+  if (aligned_size != 0)
+    PoisonShadow(addr, aligned_size,
+		 do_poison ? kAsanStackUseAfterScopeMagic : 0);
   if (size == aligned_size)
     return;
   s8 end_offset = (s8)(size - aligned_size);
-- 
2.8.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* [PATCH] Introduce tests for -fsanitize=use-after-scope
  2016-05-06 11:04 [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
@ 2016-05-06 11:08 ` Martin Liška
  2016-05-11 12:56   ` Martin Liška
  2016-05-06 11:16 ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-05-06 11:08 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 71 bytes --]

Hi.

This is a new test coverage for the new sanitizer option.

Martin

[-- Attachment #2: 0002-Introduce-tests-for-fsanitize-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 4871 bytes --]

From 753bfb3edb12c9f3fd13f320e308556f63330c97 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 4 May 2016 12:57:05 +0200
Subject: [PATCH 2/2] Introduce tests for -fsanitize=use-after-scope

gcc/testsuite/ChangeLog:

2016-05-04  Martin Liska  <mliska@suse.cz>
	* gcc.dg/asan/use-after-scope-1.c: New test.
	* gcc.dg/asan/use-after-scope-2.c: New test.
	* gcc.dg/asan/use-after-scope-3.c: New test.
	* gcc.dg/asan/use-after-scope-4.c: New test.
	* gcc.dg/asan/use-after-scope-goto-1.c: New test.
---
 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c      | 19 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c      | 48 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c      | 21 ++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c      | 17 ++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c | 22 ++++++++++
 5 files changed, 127 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c

diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
new file mode 100644
index 0000000..b4a4f52
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
@@ -0,0 +1,19 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  *(ptr+9) = 'c';
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
new file mode 100644
index 0000000..3f99fb7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }
+// { dg-shouldfail "asan" }
+
+int *bar (int *x, int *y) { return y; }
+
+int foo (void)
+{
+  char *p;
+  {
+    char a = 0;
+    p = &a;
+  }
+
+  if (*p)
+    return 1;
+  else
+    return 0;
+}
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  int a[16];
+  int *p, *q = a;
+  {
+    int b[16];
+    p = bar (a, b);
+  }
+  bar (a, q);
+  {
+    int c[16];
+    q = bar (a, c);
+  }
+  int v = *bar (a, q);
+  return v;
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'c' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
new file mode 100644
index 0000000..abaaaad
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
@@ -0,0 +1,21 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
new file mode 100644
index 0000000..7254c9c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }
+
+int
+__attribute__((no_sanitize_address))
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
new file mode 100644
index 0000000..7bb8ba4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	return 0;
+      }
+  }
+  else
+    goto label;
+
+  return 0;
+}
-- 
2.8.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 11:04 [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
  2016-05-06 11:08 ` [PATCH] Introduce tests for -fsanitize=use-after-scope Martin Liška
@ 2016-05-06 11:16 ` Martin Liška
  2016-05-06 11:48 ` Yury Gribov
  2016-05-06 12:22 ` Jakub Jelinek
  3 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-05-06 11:16 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jakub Jelinek

Hello.

One more issue I forgot to mention in the previous email:
e) As one can come up with a source code which jumps to a label within
a block scope (use-after-scope-goto-1.c):

// { dg-do run }
// { dg-additional-options "-fsanitize=use-after-scope -fstack-reuse=none" }

int main(int argc, char **argv)
{
  int a = 123;

  if (argc == 0)
  {
    int *ptr;
    label:
      {
	ptr = &a;
        *ptr = 1;
	return 0;
      }
  }
  else
    goto label;

  return 0;
}

It's necessary to record all local variables in gimplifier and possibly
emit unpoisoning code when a LABEL_EXPR is seen. That results in following gimple
output:

label:
  _20 = (unsigned long) &a;
  _21 = (unsigned long) 4;
  __builtin___asan_unpoison_stack_memory (_20, _21);
  _22 = (unsigned long) &ptr;
  _23 = (unsigned long) 8;
  __builtin___asan_unpoison_stack_memory (_22, _23);
  ptr = &a;
  ptr.0_10 = ptr;
  _24 = (unsigned long) ptr.0_10;
  _25 = _24 >> 3;
  _26 = _25 + 2147450880;
  _27 = (signed char *) _26;
  _28 = *_27;
  _29 = _28 != 0;
  _30 = _24 & 7;
  _31 = (signed char) _30;
  _32 = _31 + 3;
  _33 = _32 >= _28;
  _34 = _29 & _33;
  if (_34 != 0)
    goto <bb 5>;
  else
    goto <bb 6>;

I know that the solution is a big hammer, but it works.

Martin


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 11:04 [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
  2016-05-06 11:08 ` [PATCH] Introduce tests for -fsanitize=use-after-scope Martin Liška
  2016-05-06 11:16 ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
@ 2016-05-06 11:48 ` Yury Gribov
  2016-05-06 12:39   ` Jakub Jelinek
  2016-05-06 13:17   ` Martin Liška
  2016-05-06 12:22 ` Jakub Jelinek
  3 siblings, 2 replies; 111+ messages in thread
From: Yury Gribov @ 2016-05-06 11:48 UTC (permalink / raw)
  To: Martin Liška, GCC Patches; +Cc: Jakub Jelinek

On 05/06/2016 02:04 PM, Martin Liška wrote:
> Hello.
>
> I've started working on the patch couple of month go, basically after
> a brief discussion with Jakub on IRC.
>
> I'm sending the initial version which can successfully run instrumented
> tramp3d, postgresql server and Inkscape. It catches the basic set of
> examples which are added in following patch.
>
> The implementation is quite straightforward as works in following steps:
>
> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
> and the variable is marked as addressable
> 3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
> 4) At the very end of the function, we clean up the poisoned memory
> 5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)

Can we inline these?

> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library

Note that upstream seems to use a different cmdline interface. They 
don't have a dedicated -fsanitize=use-after-scope and instead consider 
it to be a part of -fsanitize=address (disabled by default, enabled via 
-mllvm -asan-use-after-scope=1). I'd suggest to keep this interface (or 
at least discuss with them) and use GCC's --param.

FTR here's the upstream work on this: http://reviews.llvm.org/D19347

> Example:
>
> int
> main (void)
> {
>    char *ptr;
>    {
>      char my_char[9];
>      ptr = &my_char[0];
>    }
>
>    *(ptr+9) = 'c';
> }
>
> ./a.out
> =================================================================
> ==12811==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffec9bcff69 at pc 0x000000400a73 bp 0x7ffec9bcfef0 sp 0x7ffec9bcfee8
> WRITE of size 1 at 0x7ffec9bcff69 thread T0
>      #0 0x400a72 in main (/tmp/a.out+0x400a72)
>      #1 0x7f100824860f in __libc_start_main (/lib64/libc.so.6+0x2060f)
>      #2 0x400868 in _start (/tmp/a.out+0x400868)
>
> Address 0x7ffec9bcff69 is located in stack of thread T0 at offset 105 in frame
>      #0 0x400945 in main (/tmp/a.out+0x400945)
>
>    This frame has 2 object(s):
>      [32, 40) 'ptr'
>      [96, 105) 'my_char' <== Memory access at offset 105 overflows this variable
> HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
>        (longjmp and C++ exceptions *are* supported)
> SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400a72) in main
> Shadow bytes around the buggy address:
>    0x100059371f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059371fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059371fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059371fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059371fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x100059371fe0: f1 f1 f1 f1 00 f4 f4 f4 f2 f2 f2 f2 f8[f8]f4 f4
>    0x100059371ff0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059372000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059372010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059372020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>    0x100059372030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> Shadow byte legend (one shadow byte represents 8 application bytes):
>    Addressable:           00
>    Partially addressable: 01 02 03 04 05 06 07
>    Heap left redzone:       fa
>    Heap right redzone:      fb
>    Freed heap region:       fd
>    Stack left redzone:      f1
>    Stack mid redzone:       f2
>    Stack right redzone:     f3
>    Stack partial redzone:   f4
>    Stack after return:      f5
>    Stack use after scope:   f8
>    Global redzone:          f9
>    Global init order:       f6
>    Poisoned by user:        f7
>    Container overflow:      fc
>    Array cookie:            ac
>    Intra object redzone:    bb
>    ASan internal:           fe
>    Left alloca redzone:     ca
>    Right alloca redzone:    cb
> ==12811==ABORTING
>
> As mentioned, it's request for comment as it still has couple of limitations:
> a) VLA are not supported, which should make sense as we are unable to allocate a stack slot for that

Note that we plan some work on VLA sanitization later this year 
(upstream ASan now sanitizes dynamic allocas and VLAs).

> b) we can possibly strip some instrumentation in situations where a variable is introduced in a very first BB (RTL poisoning is superfluous).
> Similarly for a very last BB of a function, we can strip end of scope poisoning (and RTL unpoisoning). I'll do that incrementally.
> c) We require -fstack-reuse=none option, maybe it worth to warn a user if -fsanitize=use-after-scope is provided without the option?

As a user, I'd prefer it to be automatically disabled when 
use-after-scope is on (unless it has been set explicitly in cmdline in 
which case we should probably issue error).

> d) An instrumented binary is quite slow (~20x for tramp3d) as every function call produces many memory read/writes. I'm wondering whether
> we should provide a faster alternative (like instrument just variables that have address taken) ?

Can this due to -fstack-reuse or to outline poisoning? The latter sounds 
particularly heavy.

> Patch can survive bootstrap and regression tests on x86_64-linux-gnu.

That's bootstrap-asan?

> Thanks for feedback.
> Martin
>

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 11:04 [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
                   ` (2 preceding siblings ...)
  2016-05-06 11:48 ` Yury Gribov
@ 2016-05-06 12:22 ` Jakub Jelinek
  2016-05-11 12:54   ` Martin Liška
  3 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-06 12:22 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Fri, May 06, 2016 at 01:04:30PM +0200, Martin Liška wrote:
> I've started working on the patch couple of month go, basically after
> a brief discussion with Jakub on IRC.
> 
> I'm sending the initial version which can successfully run instrumented
> tramp3d, postgresql server and Inkscape. It catches the basic set of
> examples which are added in following patch.
> 
> The implementation is quite straightforward as works in following steps:
> 
> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
> and the variable is marked as addressable

Not all vars have DECL_EXPRs though.

> 3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
> 4) At the very end of the function, we clean up the poisoned memory
> 5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)
> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library

> As mentioned, it's request for comment as it still has couple of limitations:
> a) VLA are not supported, which should make sense as we are unable to allocate a stack slot for that
> b) we can possibly strip some instrumentation in situations where a variable is introduced in a very first BB (RTL poisoning is superfluous).
> Similarly for a very last BB of a function, we can strip end of scope poisoning (and RTL unpoisoning). I'll do that incrementally.

Yeah.

> c) We require -fstack-reuse=none option, maybe it worth to warn a user if -fsanitize=use-after-scope is provided without the option?

This should be implicitly set by -fsanitize=use-after-scope.  Only if some
other -fstack-reuse= option is explicitly set together with
-fsanitize=use-after-scope, we should warn and reset it anyway.

> d) An instrumented binary is quite slow (~20x for tramp3d) as every function call produces many memory read/writes. I'm wondering whether
> we should provide a faster alternative (like instrument just variables that have address taken) ?

I don't see any point in instrumenting !needs_to_live_in_memory vars,
at least not those that don't need to live in memory at gimplification time.
How could one use those after scope?  They can't be accessed by
dereferencing some pointer, and the symbol itself should be unavailable for
symbol lookup after the symbol goes out of scope.
Plus obviously ~20x slowdown isn't acceptable.

Another thing is what to do with variables that are addressable at
gimplification time, but generally are made non-addressable afterwards,
such as due to optimizing away the taking of their address, inlining, etc.

Perhaps depending on how big slowdown you get after just instrumenting
needs_to_live_in_memory vars from ~ gimplification time and/or with the
possible inlining of the poisoning/unpoisoning (again, should be another
knob), at least with small sized vars, there might be another knob,
which would tell if vars that are made non-addressable during optimizations
later on should be instrumented or not.
E.g. if you ASAN_MARK all needs_to_live_in_memory vars early, you could
during the addressable determination when the knob says stuff should be
faster, but less precise, ignore the vars that are addressable just because
of the ASAN_MARK calls, and if they'd then turn to be non-addressable,
remove the corresponding ASAN_MARK calls.

> 2016-05-04  Martin Liska  <mliska@suse.cz>
> 
> 	* asan/asan_poisoning.cc: Do not call PoisonShadow in case
> 	of zero size of aligned size.

Generally, libsanitizer changes would need to go through upstream.

> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "varasm.h"
>  #include "stor-layout.h"
>  #include "tree-iterator.h"
> +#include "params.h"
>  #include "asan.h"
>  #include "dojump.h"
>  #include "explow.h"
> @@ -54,7 +55,6 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cfgloop.h"
>  #include "gimple-builder.h"
>  #include "ubsan.h"
> -#include "params.h"
>  #include "builtins.h"
>  #include "fnmatch.h"

Why do you need to move params.h around?  Does asan.h now depend on
params.h?

> +  gimplify_seq_add_stmt
> +    (seq_p, gimple_build_call_internal (IFN_ASAN_MARK, 3,
> +					build_int_cst (integer_type_node,
> +						       flags),
> +					base, unit_size));

Formatting, better introduce some temporary variables, like
  gimple *g = gimple_build_call_internal (...);
  gimplify_seq_add_stmt (seq_p, g);

> --- a/gcc/tree-vect-patterns.c
> +++ b/gcc/tree-vect-patterns.c
> @@ -3570,7 +3570,8 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
>  {
>    gimple *last_stmt = stmts->pop ();
>    enum tree_code rhs_code;
> -  tree lhs, rhs1, rhs2, tmp, rhs1_type, rhs2_type, vectype1, vectype2;
> +  tree lhs = NULL_TREE, rhs1, rhs2, tmp, rhs1_type, rhs2_type;
> +  tree vectype1, vectype2;
>    stmt_vec_info stmt_vinfo = vinfo_for_stmt (last_stmt);
>    stmt_vec_info pattern_stmt_info;
>    vec_info *vinfo = stmt_vinfo->vinfo;

How is the above related to this patch?

> +/* Return TRUE if we should instrument for use-after-scope sanity checking.  */                                                                   
> +                                                                                                                                                  
> +static inline bool                                                                                                                                
> +asan_sanitize_use_after_scope (void)                                                                                                              
> +{                                                                                                                                                 
> +  return (flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)                                                                                       
> +    == SANITIZE_ADDRESS_USE_AFTER_SCOPE                                                                                                           
> +    && flag_stack_reuse == SR_NONE                                                                                                                
> +    && ASAN_STACK;                                                                                                                                
> +}                                                                                                                                                 

Formatting, there should be ()s around the whole return expression as it
spans multiple lines, and it should be indented properly.
Plus IMHO flag_stack_reuse should be dealt with during option handling.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 11:48 ` Yury Gribov
@ 2016-05-06 12:39   ` Jakub Jelinek
  2016-05-06 13:07     ` Martin Liška
  2016-05-06 14:22     ` Yury Gribov
  2016-05-06 13:17   ` Martin Liška
  1 sibling, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-06 12:39 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Martin Liška, GCC Patches

On Fri, May 06, 2016 at 02:48:30PM +0300, Yury Gribov wrote:
> >6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
> 
> Note that upstream seems to use a different cmdline interface. They don't
> have a dedicated -fsanitize=use-after-scope and instead consider it to be a
> part of -fsanitize=address (disabled by default, enabled via -mllvm
> -asan-use-after-scope=1). I'd suggest to keep this interface (or at least
> discuss with them) and use GCC's --param.

I personally think -fsanitize=use-after-scope (which implies address
sanitization in it) is better, can upstream be convinved not to change it?

> FTR here's the upstream work on this: http://reviews.llvm.org/D19347
> 
> >Example:
> >
> >int
> >main (void)
> >{
> >   char *ptr;
> >   {
> >     char my_char[9];
> >     ptr = &my_char[0];
> >   }
> >
> >   *(ptr+9) = 'c';
> >}

Well, this testcase shows not just use after scope, but also out of bound
access.  Would be better not to combine it, at least in the majority of
testcases.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 12:39   ` Jakub Jelinek
@ 2016-05-06 13:07     ` Martin Liška
  2016-05-06 14:22     ` Yury Gribov
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-05-06 13:07 UTC (permalink / raw)
  To: Jakub Jelinek, Yury Gribov; +Cc: GCC Patches

On 05/06/2016 02:38 PM, Jakub Jelinek wrote:
> On Fri, May 06, 2016 at 02:48:30PM +0300, Yury Gribov wrote:
>>> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
>>
>> Note that upstream seems to use a different cmdline interface. They don't
>> have a dedicated -fsanitize=use-after-scope and instead consider it to be a
>> part of -fsanitize=address (disabled by default, enabled via -mllvm
>> -asan-use-after-scope=1). I'd suggest to keep this interface (or at least
>> discuss with them) and use GCC's --param.
> 
> I personally think -fsanitize=use-after-scope (which implies address
> sanitization in it) is better, can upstream be convinved not to change it?

I also incline to the original -fsanitize=use-after-scope, which is compatible
to remaining -fsanitize=... options we have in the GCC.

> 
>> FTR here's the upstream work on this: http://reviews.llvm.org/D19347
>>
>>> Example:
>>>
>>> int
>>> main (void)
>>> {
>>>   char *ptr;
>>>   {
>>>     char my_char[9];
>>>     ptr = &my_char[0];
>>>   }
>>>
>>>   *(ptr+9) = 'c';
>>> }
> 
> Well, this testcase shows not just use after scope, but also out of bound
> access.  Would be better not to combine it, at least in the majority of
> testcases.

Sure, that's a typo, should be:
  *(ptr+8) = 'c';

with:
    [96, 105) 'my_char' <== Memory access at offset 104 is inside this variable

Intention was to touch the second shadow byte for the array.

Martin

> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 11:48 ` Yury Gribov
  2016-05-06 12:39   ` Jakub Jelinek
@ 2016-05-06 13:17   ` Martin Liška
  2016-05-06 13:25     ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-05-06 13:17 UTC (permalink / raw)
  To: Yury Gribov, GCC Patches; +Cc: Jakub Jelinek

On 05/06/2016 01:48 PM, Yury Gribov wrote:
> On 05/06/2016 02:04 PM, Martin Liška wrote:
>> Hello.
>>
>> I've started working on the patch couple of month go, basically after
>> a brief discussion with Jakub on IRC.
>>
>> I'm sending the initial version which can successfully run instrumented
>> tramp3d, postgresql server and Inkscape. It catches the basic set of
>> examples which are added in following patch.
>>
>> The implementation is quite straightforward as works in following steps:
>>
>> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
>> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
>> and the variable is marked as addressable
>> 3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
>> 4) At the very end of the function, we clean up the poisoned memory
>> 5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)
> 
> Can we inline these?

Currently not as libasan is a shared library that an instrumented executable is linked with.
Possible solution would be to directly emit gimple instruction that would poison/unpoison the memory.
But it's not a trivial job which is done in the poisoning code (ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, u8 value)

> 
>> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
> 
> Note that upstream seems to use a different cmdline interface. They don't have a dedicated -fsanitize=use-after-scope and instead consider it to be a part of -fsanitize=address (disabled by default, enabled via -mllvm -asan-use-after-scope=1). I'd suggest to keep this interface (or at least discuss with them) and use GCC's --param.
> 
> FTR here's the upstream work on this: http://reviews.llvm.org/D19347

Thanks for the link, I will adapt part of the test to our test-suite.
Some of them are really interesting.

Martin

> 
>> Example:
>>
>> int
>> main (void)
>> {
>>    char *ptr;
>>    {
>>      char my_char[9];
>>      ptr = &my_char[0];
>>    }
>>
>>    *(ptr+9) = 'c';
>> }
>>
>> ./a.out
>> =================================================================
>> ==12811==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffec9bcff69 at pc 0x000000400a73 bp 0x7ffec9bcfef0 sp 0x7ffec9bcfee8
>> WRITE of size 1 at 0x7ffec9bcff69 thread T0
>>      #0 0x400a72 in main (/tmp/a.out+0x400a72)
>>      #1 0x7f100824860f in __libc_start_main (/lib64/libc.so.6+0x2060f)
>>      #2 0x400868 in _start (/tmp/a.out+0x400868)
>>
>> Address 0x7ffec9bcff69 is located in stack of thread T0 at offset 105 in frame
>>      #0 0x400945 in main (/tmp/a.out+0x400945)
>>
>>    This frame has 2 object(s):
>>      [32, 40) 'ptr'
>>      [96, 105) 'my_char' <== Memory access at offset 105 overflows this variable
>> HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
>>        (longjmp and C++ exceptions *are* supported)
>> SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400a72) in main
>> Shadow bytes around the buggy address:
>>    0x100059371f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059371fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059371fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059371fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059371fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> =>0x100059371fe0: f1 f1 f1 f1 00 f4 f4 f4 f2 f2 f2 f2 f8[f8]f4 f4
>>    0x100059371ff0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059372000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059372010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059372020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>    0x100059372030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> Shadow byte legend (one shadow byte represents 8 application bytes):
>>    Addressable:           00
>>    Partially addressable: 01 02 03 04 05 06 07
>>    Heap left redzone:       fa
>>    Heap right redzone:      fb
>>    Freed heap region:       fd
>>    Stack left redzone:      f1
>>    Stack mid redzone:       f2
>>    Stack right redzone:     f3
>>    Stack partial redzone:   f4
>>    Stack after return:      f5
>>    Stack use after scope:   f8
>>    Global redzone:          f9
>>    Global init order:       f6
>>    Poisoned by user:        f7
>>    Container overflow:      fc
>>    Array cookie:            ac
>>    Intra object redzone:    bb
>>    ASan internal:           fe
>>    Left alloca redzone:     ca
>>    Right alloca redzone:    cb
>> ==12811==ABORTING
>>
>> As mentioned, it's request for comment as it still has couple of limitations:
>> a) VLA are not supported, which should make sense as we are unable to allocate a stack slot for that
> 
> Note that we plan some work on VLA sanitization later this year (upstream ASan now sanitizes dynamic allocas and VLAs).
> 
>> b) we can possibly strip some instrumentation in situations where a variable is introduced in a very first BB (RTL poisoning is superfluous).
>> Similarly for a very last BB of a function, we can strip end of scope poisoning (and RTL unpoisoning). I'll do that incrementally.
>> c) We require -fstack-reuse=none option, maybe it worth to warn a user if -fsanitize=use-after-scope is provided without the option?
> 
> As a user, I'd prefer it to be automatically disabled when use-after-scope is on (unless it has been set explicitly in cmdline in which case we should probably issue error).
> 
>> d) An instrumented binary is quite slow (~20x for tramp3d) as every function call produces many memory read/writes. I'm wondering whether
>> we should provide a faster alternative (like instrument just variables that have address taken) ?
> 
> Can this due to -fstack-reuse or to outline poisoning? The latter sounds particularly heavy.
> 
>> Patch can survive bootstrap and regression tests on x86_64-linux-gnu.
> 
> That's bootstrap-asan?
> 
>> Thanks for feedback.
>> Martin
>>
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 13:17   ` Martin Liška
@ 2016-05-06 13:25     ` Jakub Jelinek
  2016-05-06 14:41       ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-06 13:25 UTC (permalink / raw)
  To: Martin Liška; +Cc: Yury Gribov, GCC Patches

On Fri, May 06, 2016 at 03:17:23PM +0200, Martin Liška wrote:
> On 05/06/2016 01:48 PM, Yury Gribov wrote:
> > On 05/06/2016 02:04 PM, Martin Liška wrote:
> >> I've started working on the patch couple of month go, basically after
> >> a brief discussion with Jakub on IRC.
> >>
> >> I'm sending the initial version which can successfully run instrumented
> >> tramp3d, postgresql server and Inkscape. It catches the basic set of
> >> examples which are added in following patch.
> >>
> >> The implementation is quite straightforward as works in following steps:
> >>
> >> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
> >> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
> >> and the variable is marked as addressable
> >> 3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
> >> 4) At the very end of the function, we clean up the poisoned memory
> >> 5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)
> > 
> > Can we inline these?
> 
> Currently not as libasan is a shared library that an instrumented executable is linked with.
> Possible solution would be to directly emit gimple instruction that would poison/unpoison the memory.
> But it's not a trivial job which is done in the poisoning code (ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, u8 value)

Well, we already have the gimple poisoning/unpoisoning code on RTL (emitted
after the prologue and before the epilogue), so it shouldn't be that hard.
I'd only do the most common/easy cases inline though, like 1/2/4/8/16/32
bytes long variables.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 12:39   ` Jakub Jelinek
  2016-05-06 13:07     ` Martin Liška
@ 2016-05-06 14:22     ` Yury Gribov
  2016-05-06 14:39       ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Yury Gribov @ 2016-05-06 14:22 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On 05/06/2016 03:38 PM, Jakub Jelinek wrote:
> On Fri, May 06, 2016 at 02:48:30PM +0300, Yury Gribov wrote:
>>> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
>>
>> Note that upstream seems to use a different cmdline interface. They don't
>> have a dedicated -fsanitize=use-after-scope and instead consider it to be a
>> part of -fsanitize=address (disabled by default, enabled via -mllvm
>> -asan-use-after-scope=1). I'd suggest to keep this interface (or at least
>> discuss with them) and use GCC's --param.
>
> I personally think -fsanitize=use-after-scope (which implies address
> sanitization in it) is better, can upstream be convinved not to change it?

Will that work with -fsanitize=kernel-address?

>
>> FTR here's the upstream work on this: http://reviews.llvm.org/D19347
>>
>>> Example:
>>>
>>> int
>>> main (void)
>>> {
>>>    char *ptr;
>>>    {
>>>      char my_char[9];
>>>      ptr = &my_char[0];
>>>    }
>>>
>>>    *(ptr+9) = 'c';
>>> }
>
> Well, this testcase shows not just use after scope, but also out of bound
> access.  Would be better not to combine it, at least in the majority of
> testcases.
>
> 	Jakub
>
>

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 14:22     ` Yury Gribov
@ 2016-05-06 14:39       ` Jakub Jelinek
  2016-05-10 15:03         ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-06 14:39 UTC (permalink / raw)
  To: Yury Gribov; +Cc: Martin Liška, GCC Patches

On Fri, May 06, 2016 at 05:22:46PM +0300, Yury Gribov wrote:
> On 05/06/2016 03:38 PM, Jakub Jelinek wrote:
> >On Fri, May 06, 2016 at 02:48:30PM +0300, Yury Gribov wrote:
> >>>6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
> >>
> >>Note that upstream seems to use a different cmdline interface. They don't
> >>have a dedicated -fsanitize=use-after-scope and instead consider it to be a
> >>part of -fsanitize=address (disabled by default, enabled via -mllvm
> >>-asan-use-after-scope=1). I'd suggest to keep this interface (or at least
> >>discuss with them) and use GCC's --param.
> >
> >I personally think -fsanitize=use-after-scope (which implies address
> >sanitization in it) is better, can upstream be convinved not to change it?
> 
> Will that work with -fsanitize=kernel-address?

Depends on how exactly it is defined.  It could be enabling just its own
sanitizer bit and nothing else, then users would need to use
-fsanitize=address,use-after-scope
or
-fsanitize=kernel-address,use-after-scope
(order doesn't matter), or it could enable the SANITIZE_ADDRESS
bit together with its own, and then we'd just post-option processing
(where we e.g. reject address,kernel-address) default to
SANITIZE_USER_ADDRESS if SANITIZE_ADDRESS is on together with
SANITIZE_USE_AFTER_SCOPE, but neither SANITIZE_{USER,KERNEL}_ADDRESS
is defined.
-fsanitize=address -fno-sanitize=use-after-scope
obviously shouldn't in any case disable SANITIZE_ADDRESS, similarly
-fsanitize=kernel-address -fno-sanitize=use-after-scope

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 13:25     ` Jakub Jelinek
@ 2016-05-06 14:41       ` Martin Liška
  2016-05-06 14:46         ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-05-06 14:41 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Yury Gribov, GCC Patches

On 05/06/2016 03:25 PM, Jakub Jelinek wrote:
> Well, we already have the gimple poisoning/unpoisoning code on RTL (emitted
> after the prologue and before the epilogue), so it shouldn't be that hard.
> I'd only do the most common/easy cases inline though, like 1/2/4/8/16/32
> bytes long variables.
> 
> 	Jakub

You are right, I didn't realize it earlier.
As I've collected statistics for tramp3d, poisoning code has following distribution:

4:1.62%
8:3.53%
12:94.76%

which is quite interesting that 12B are such a common size :)
Probably due to a lot of time spent in ::evaluate (MultiArgEvaluator and MultiArgEvaluator).
Considering just variables which needs_to_live_in_memory, tramp3d is still ~15x slower.

Anyway profile report tells:
    26.51%  a.out    libasan.so.3.0.0  [.] __asan::PoisonShadow
    18.49%  a.out    libasan.so.3.0.0  [.] PoisonAlignedStackMemory
     5.61%  a.out    libc-2.22.so      [.] __memset_avx2
     5.41%  a.out    a.out             [.] MultiArgEvaluator<RemoteMultiPatchEvaluatorTag>::evaluate<MultiArg2<...>
     3.56%  a.out    libasan.so.3.0.0  [.] __asan_unpoison_stack_memory
     2.69%  a.out    libasan.so.3.0.0  [.] __asan_poison_stack_memory

I'll continue working on that after weekend.

Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 14:41       ` Martin Liška
@ 2016-05-06 14:46         ` Jakub Jelinek
  0 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-06 14:46 UTC (permalink / raw)
  To: Martin Liška; +Cc: Yury Gribov, GCC Patches

On Fri, May 06, 2016 at 04:41:41PM +0200, Martin Liška wrote:
> On 05/06/2016 03:25 PM, Jakub Jelinek wrote:
> > Well, we already have the gimple poisoning/unpoisoning code on RTL (emitted
> > after the prologue and before the epilogue), so it shouldn't be that hard.
> > I'd only do the most common/easy cases inline though, like 1/2/4/8/16/32
> > bytes long variables.
> > 
> > 	Jakub
> 
> You are right, I didn't realize it earlier.
> As I've collected statistics for tramp3d, poisoning code has following distribution:
> 
> 4:1.62%
> 8:3.53%
> 12:94.76%
> 
> which is quite interesting that 12B are such a common size :)
> Probably due to a lot of time spent in ::evaluate (MultiArgEvaluator and MultiArgEvaluator).
> Considering just variables which needs_to_live_in_memory, tramp3d is still ~15x slower.

Please look at other testcases, not just tramp3d - we in the end don't want
to tune it to just tramp3d.  Pick up some 3-4 C/C++ benchmarks, tramp3d can
be one of them ;)

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 14:39       ` Jakub Jelinek
@ 2016-05-10 15:03         ` Martin Liška
  2016-05-10 15:15           ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-05-10 15:03 UTC (permalink / raw)
  To: Jakub Jelinek, Yury Gribov; +Cc: GCC Patches

On 05/06/2016 04:39 PM, Jakub Jelinek wrote:
> Depends on how exactly it is defined.  It could be enabling just its own
> sanitizer bit and nothing else, then users would need to use
> -fsanitize=address,use-after-scope
> or
> -fsanitize=kernel-address,use-after-scope

I'm inclined to the second option, where the new option would be automatically
added if a ADDRESS sanitizer is enabled (SANITIZE_{USER,KERNEL}_ADDRESS):

Is it acceptable behavior?

> (order doesn't matter), or it could enable the SANITIZE_ADDRESS
> bit together with its own, and then we'd just post-option processing
> (where we e.g. reject address,kernel-address) default to
> SANITIZE_USER_ADDRESS if SANITIZE_ADDRESS is on together with
> SANITIZE_USE_AFTER_SCOPE, but neither SANITIZE_{USER,KERNEL}_ADDRESS
> is defined.
> -fsanitize=address -fno-sanitize=use-after-scope
> obviously shouldn't in any case disable SANITIZE_ADDRESS, similarly
> -fsanitize=kernel-address -fno-sanitize=use-after-scope
> 
> 	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-10 15:03         ` Martin Liška
@ 2016-05-10 15:15           ` Jakub Jelinek
  0 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-10 15:15 UTC (permalink / raw)
  To: Martin Liška; +Cc: Yury Gribov, GCC Patches

On Tue, May 10, 2016 at 05:03:30PM +0200, Martin Liška wrote:
> On 05/06/2016 04:39 PM, Jakub Jelinek wrote:
> > Depends on how exactly it is defined.  It could be enabling just its own
> > sanitizer bit and nothing else, then users would need to use
> > -fsanitize=address,use-after-scope
> > or
> > -fsanitize=kernel-address,use-after-scope
> 
> I'm inclined to the second option, where the new option would be automatically
> added if a ADDRESS sanitizer is enabled (SANITIZE_{USER,KERNEL}_ADDRESS):
> 
> Is it acceptable behavior?

To me, yes.  But, the question is if it is acceptable to clang too.
Limiting it to a param means it will be command line option incompatible
between clang and gcc.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-06 12:22 ` Jakub Jelinek
@ 2016-05-11 12:54   ` Martin Liška
  2016-05-12 10:42     ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-05-11 12:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 9131 bytes --]

On 05/06/2016 02:22 PM, Jakub Jelinek wrote:
> On Fri, May 06, 2016 at 01:04:30PM +0200, Martin Liška wrote:
>> I've started working on the patch couple of month go, basically after
>> a brief discussion with Jakub on IRC.
>>
>> I'm sending the initial version which can successfully run instrumented
>> tramp3d, postgresql server and Inkscape. It catches the basic set of
>> examples which are added in following patch.
>>
>> The implementation is quite straightforward as works in following steps:
>>
>> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
>> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
>> and the variable is marked as addressable
> 
> Not all vars have DECL_EXPRs though.

Yeah, I've spotted one interesting example which is part of LLVM's testsuite:

struct IntHolder {
  int val;
};

const IntHolder *saved;

void save(const IntHolder &holder) {
  saved = &holder;
}

int main(int argc, char *argv[]) {
  save({10});
  int x = saved->val;  // BOOM
  return x;
}

It would be also good to handle such temporaries. Any suggestions how to handle that in gimplifier?

> 
>> 3) Similarly, BIND_EXPR is the place where we poison the variable (scope exit)
>> 4) At the very end of the function, we clean up the poisoned memory
>> 5) The builtins are expanded to call to libsanitizer run-time library (__asan_poison_stack_memory, __asan_unpoison_stack_memory)
>> 6) As the use-after-scope stuff is already included in libsanitizer, no change is needed for the library
> 
>> As mentioned, it's request for comment as it still has couple of limitations:
>> a) VLA are not supported, which should make sense as we are unable to allocate a stack slot for that
>> b) we can possibly strip some instrumentation in situations where a variable is introduced in a very first BB (RTL poisoning is superfluous).
>> Similarly for a very last BB of a function, we can strip end of scope poisoning (and RTL unpoisoning). I'll do that incrementally.
> 
> Yeah.
> 
>> c) We require -fstack-reuse=none option, maybe it worth to warn a user if -fsanitize=use-after-scope is provided without the option?
> 
> This should be implicitly set by -fsanitize=use-after-scope.  Only if some
> other -fstack-reuse= option is explicitly set together with
> -fsanitize=use-after-scope, we should warn and reset it anyway.

Handled in v2 of the patch.

> 
>> d) An instrumented binary is quite slow (~20x for tramp3d) as every function call produces many memory read/writes. I'm wondering whether
>> we should provide a faster alternative (like instrument just variables that have address taken) ?
> 
> I don't see any point in instrumenting !needs_to_live_in_memory vars,
> at least not those that don't need to live in memory at gimplification time.
> How could one use those after scope?  They can't be accessed by
> dereferencing some pointer, and the symbol itself should be unavailable for
> symbol lookup after the symbol goes out of scope.
> Plus obviously ~20x slowdown isn't acceptable.
> 
> Another thing is what to do with variables that are addressable at
> gimplification time, but generally are made non-addressable afterwards,
> such as due to optimizing away the taking of their address, inlining, etc.
> 
> Perhaps depending on how big slowdown you get after just instrumenting
> needs_to_live_in_memory vars from ~ gimplification time and/or with the
> possible inlining of the poisoning/unpoisoning (again, should be another
> knob), at least with small sized vars, there might be another knob,
> which would tell if vars that are made non-addressable during optimizations
> later on should be instrumented or not.
> E.g. if you ASAN_MARK all needs_to_live_in_memory vars early, you could
> during the addressable determination when the knob says stuff should be
> faster, but less precise, ignore the vars that are addressable just because
> of the ASAN_MARK calls, and if they'd then turn to be non-addressable,
> remove the corresponding ASAN_MARK calls.

Following the aforementioned instrumentation and utilizing direct shadow memory
instruction emission, I was able to reduce tramp3d slowdown to 3x and
postgresql server test-suite runs 2x slower.

Apart from that, second version of the patch changes:
+ fixed issues with missing stack unpoisoning; currently, I mark all VAR_DECLs that
are in ASAN_MARK internal fns and stack prologue/epilogue is emitted just for these vars
+ removed unneeded hunks (tree-vect-patterns.c and asan_poisoning.cc)
+ LABEL unpoisoning code makes stable sort for variables that were already used in the context
+ stack poisoning hasn't worked for -O1+ due to following guard in asan.c
 /* Automatic vars in the current function will be always accessible.  */
+ direct shadow memory poisoning/unpoisoning code is introduced - in both scenarios (RTL and GIMPLE),
I would appreciate feedback if storing multiple bytes is fine? What is the maximum memory wide
store mode supported by a target? How can I get such information?
+ the maximum object size handled by a direct emission is guarded by use-after-scope-direct-emission-threshold
parameter; initial value (256B) should maximally emit store of 32B


> 
>> 2016-05-04  Martin Liska  <mliska@suse.cz>
>>
>> 	* asan/asan_poisoning.cc: Do not call PoisonShadow in case
>> 	of zero size of aligned size.
> 
> Generally, libsanitizer changes would need to go through upstream.

Sure, in fact the hunk looks unneeded.

> 
>> --- a/gcc/asan.c
>> +++ b/gcc/asan.c
>> @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
>>  #include "varasm.h"
>>  #include "stor-layout.h"
>>  #include "tree-iterator.h"
>> +#include "params.h"
>>  #include "asan.h"
>>  #include "dojump.h"
>>  #include "explow.h"
>> @@ -54,7 +55,6 @@ along with GCC; see the file COPYING3.  If not see
>>  #include "cfgloop.h"
>>  #include "gimple-builder.h"
>>  #include "ubsan.h"
>> -#include "params.h"
>>  #include "builtins.h"
>>  #include "fnmatch.h"
> 
> Why do you need to move params.h around?  Does asan.h now depend on
> params.h?

Yeah, depends because of:

static inline bool
asan_sanitize_use_after_scope (void)
{
  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
	  && flag_stack_reuse == SR_NONE
	  && ASAN_STACK);
}

Where ASAN_STACK comes from params.h.


> 
>> +  gimplify_seq_add_stmt
>> +    (seq_p, gimple_build_call_internal (IFN_ASAN_MARK, 3,
>> +					build_int_cst (integer_type_node,
>> +						       flags),
>> +					base, unit_size));
> 
> Formatting, better introduce some temporary variables, like
>   gimple *g = gimple_build_call_internal (...);
>   gimplify_seq_add_stmt (seq_p, g);
> 

Done.

>> --- a/gcc/tree-vect-patterns.c
>> +++ b/gcc/tree-vect-patterns.c
>> @@ -3570,7 +3570,8 @@ vect_recog_mask_conversion_pattern (vec<gimple *> *stmts, tree *type_in,
>>  {
>>    gimple *last_stmt = stmts->pop ();
>>    enum tree_code rhs_code;
>> -  tree lhs, rhs1, rhs2, tmp, rhs1_type, rhs2_type, vectype1, vectype2;
>> +  tree lhs = NULL_TREE, rhs1, rhs2, tmp, rhs1_type, rhs2_type;
>> +  tree vectype1, vectype2;
>>    stmt_vec_info stmt_vinfo = vinfo_for_stmt (last_stmt);
>>    stmt_vec_info pattern_stmt_info;
>>    vec_info *vinfo = stmt_vinfo->vinfo;
> 
> How is the above related to this patch?

Done.

> 
>> +/* Return TRUE if we should instrument for use-after-scope sanity checking.  */                                                                   
>> +                                                                                                                                                  
>> +static inline bool                                                                                                                                
>> +asan_sanitize_use_after_scope (void)                                                                                                              
>> +{                                                                                                                                                 
>> +  return (flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)                                                                                       
>> +    == SANITIZE_ADDRESS_USE_AFTER_SCOPE                                                                                                           
>> +    && flag_stack_reuse == SR_NONE                                                                                                                
>> +    && ASAN_STACK;                                                                                                                                
>> +}                                                                                                                                                 
> 
> Formatting, there should be ()s around the whole return expression as it
> spans multiple lines, and it should be indented properly.
> Plus IMHO flag_stack_reuse should be dealt with during option handling.
> 
> 	Jakub
> 

Also done.

Thanks,
Martin

[-- Attachment #2: 0001-Introduce-fsanitize-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 33274 bytes --]

From 98b3e4fa69221f3c4539c2fe521deb45658c97aa Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize=use-after-scope

gcc/ChangeLog:

2016-05-10  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Enum is moved to a header
	file.
	(asan_init_shadow_ptr_types): Add new shadow_ptr_type.
	(get_shadow_memory_size): New function.
	(asan_poison_stack_variables): Likewise.
	(asan_emit_stack_protection): Poison and unpoison stack
	variables.
	(build_shadow_mem_access): New arguments is added.
	(instrument_derefs): Do not skip automatic variables if we
	instrument for use-after-scope.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	* asan.h (enum asan_mark_flags): Move from the source file.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* builtins.c: Include asan.h file.
	* cfgexpand.c (asan_sanitize_stack_p): Use
	asan_no_sanitize_address_p.
	* flag-types.h (enum sanitize_code): Add
	SANITIZE_ADDRESS_USE_AFTER_SCOPE enum value.
	* gimplify.c (asan_poison_variable): New function.
	(gimplify_bind_expr): Unpoison stack variables.
	(gimplify_decl_expr): Poison stack variables.
	(gimplify_expr): Unpoison stack variables that precede a label.
	(gimplify_function_tree): Guard that all local variables
	(sort_by_decl_uid): New function.
	are properly unpoisoned.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def: Declare ASAN_MARK.
	* opts-global.c: Include asan.h header file.
	* opts.c (finish_options): Handle interaction between -fstack-reuse
	and -fsanitize=use-after-scope.
	* params.def: Add new param use-after-scope-direct-emission-threshold.
	* params.h: Define the param.
	* sancov.c: Include asan.h header file.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory.
	* sanopt.c (pass_sanopt::execute): Expand ASAN_MARK internal
	fns.
	* tree-streamer-in.c: Include asan.h header file.
	* tsan.c: Likewise.
	* ubsan.c: Likewise.
	* varasm.c: Likewise.
	* hash-set.h (hash_set::empty): New function.
---
 gcc/asan.c             | 258 ++++++++++++++++++++++++++++++++++++++++++++-----
 gcc/asan.h             |  52 +++++++++-
 gcc/builtins.c         |   1 +
 gcc/c-family/c-ubsan.c |   1 +
 gcc/cfgexpand.c        |   3 +-
 gcc/cp/decl2.c         |   1 +
 gcc/flag-types.h       |   5 +-
 gcc/gimplify.c         | 118 ++++++++++++++++++++--
 gcc/hash-set.h         |   7 ++
 gcc/internal-fn.c      |   9 ++
 gcc/internal-fn.def    |   1 +
 gcc/opts-global.c      |   1 +
 gcc/opts.c             |  14 +++
 gcc/params.def         |   6 ++
 gcc/params.h           |   2 +
 gcc/sancov.c           |   1 +
 gcc/sanitizer.def      |   4 +
 gcc/sanopt.c           |   5 +-
 gcc/tree-streamer-in.c |   1 +
 gcc/tsan.c             |   1 +
 gcc/ubsan.c            |   1 +
 gcc/varasm.c           |   1 +
 22 files changed, 449 insertions(+), 44 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 71095fb..537d6c8 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "stor-layout.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "dojump.h"
 #include "explow.h"
@@ -54,7 +55,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "gimple-builder.h"
 #include "ubsan.h"
-#include "params.h"
 #include "builtins.h"
 #include "fnmatch.h"
 
@@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set <tree> asan_handled_variables(13);
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -313,22 +318,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -931,12 +927,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1020,6 +1020,91 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+get_shadow_memory_size (unsigned HOST_WIDE_INT size)
+{
+  /* Round up size of object.  */
+  unsigned HOST_WIDE_INT r;
+  if ((r = size % BITS_PER_UNIT) != 0)
+    size += BITS_PER_UNIT - r;
+
+  return size / BITS_PER_UNIT;
+}
+
+/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
+   allocated for local variables, localted in OFFSETS.  LENGTH is number
+   of OFFSETS, BASE is the register holding the stack base,
+   against which OFFSETS array offsets are relative to.  BASE_OFFSET represents
+   an offset requested by alignment and similar stuff.  */
+
+static void
+asan_poison_stack_variables (rtx shadow_base, rtx base,
+			     HOST_WIDE_INT base_offset,
+			     HOST_WIDE_INT *offsets, int length,
+			     tree *decls, bool poison)
+{
+  if (asan_sanitize_use_after_scope ())
+    for (int l = length - 2; l > 0; l -= 2)
+      {
+	tree decl = decls[l / 2 - 1];
+	HOST_WIDE_INT var_offset = offsets[l];
+	HOST_WIDE_INT var_end_offset = offsets[l - 1];
+	if (!asan_handled_variables.contains (decl))
+	  {
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      fprintf (dump_file, "Skipping stack emission for variable: %s "
+		       "(%ldB)\n",
+		       IDENTIFIER_POINTER (DECL_NAME (decl)),
+		       var_end_offset - var_offset);
+	    continue;
+	  }
+
+	rtx source = expand_binop (Pmode, add_optab, base,
+				   gen_int_mode
+				    (var_offset - base_offset, Pmode),
+				   NULL_RTX, 1, OPTAB_DIRECT);
+
+	HOST_WIDE_INT size = var_end_offset - var_offset;
+	if (size <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+	  {
+	    unsigned HOST_WIDE_INT shadow_size
+	      = get_shadow_memory_size (size);
+
+	    rtx shadow_mem = gen_rtx_MEM (SImode, shadow_base);
+	    rtx var_mem = adjust_address (shadow_mem, QImode,
+					  (var_offset - base_offset)
+					  >> ASAN_SHADOW_SHIFT);
+
+	    char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+	    for (unsigned i = 0; i < shadow_size; ++i)
+	      {
+		emit_move_insn (var_mem, gen_int_mode (c, QImode));
+		var_mem = adjust_address (var_mem, QImode, 1);
+	      }
+	  }
+	else
+	  {
+	    rtx size_rtx = GEN_INT (size);
+	    const char *fname = poison ?  "__asan_poison_stack_memory"
+	      :"__asan_unpoison_stack_memory";
+	    rtx ret = init_one_libfunc (fname);
+	    emit_library_call (ret, LCT_NORMAL, VOIDmode, 2, source, ptr_mode,
+			       size_rtx, TYPE_MODE (pointer_sized_int_node));
+	  }
+
+	if (dump_file && (dump_flags & TDF_DETAILS))
+	  fprintf (dump_file, "Emitting stack %s for variable: %s"
+		   "(%ldB)\n",
+		   poison ? "poisoning" : "unpoisoning",
+		   IDENTIFIER_POINTER (DECL_NAME (decl)),
+		   var_end_offset - var_offset);
+      }
+}
+
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1228,6 +1313,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 	}
       cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE;
     }
+
+  /* Poison stack variables at the very beginning of a function.  */
+  asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+			       decls, true);
+
   do_pending_stack_adjust ();
 
   /* Construct epilogue sequence.  */
@@ -1309,6 +1399,14 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
       asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
     }
 
+  /* Unpoison stack variables at the end of a function.  As the former
+     stack memory can be reused, we have to unpoison the memory.  */
+  asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+			       decls, false);
+
+  /* Clean-up set with instrumented stack variables.  */
+  asan_handled_variables.empty ();
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1593,7 +1691,8 @@ insert_if_then_before_iter (gcond *cond,
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_ptr = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1616,11 +1715,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_ptr)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1824,7 +1927,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && !asan_sanitize_use_after_scope ())
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2573,6 +2677,110 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 unsigned char value, unsigned bytes)
+{
+  tree shadow_ptr_type;
+
+  switch (bytes)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < bytes; ++i)
+    val |= (unsigned HOST_WIDE_INT) value << (BITS_PER_UNIT * i);
+
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  asan_handled_variables.add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+				  NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size
+	= get_shadow_memory_size (size_in_bytes);
+      char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, c, size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..7155334 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,6 +37,10 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of inlined variables that cannot be poisoned by use-after-cope
+   sanitizer.  */
+extern hash_set <tree> asan_inlined_variables;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
@@ -50,15 +55,33 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
@@ -105,4 +128,23 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
+	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
+	  && flag_stack_reuse == SR_NONE
+	  && ASAN_STACK);
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 476feb1..ee4379e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "value-prof.h"
 #include "builtins.h"
+#include "params.h"
 #include "asan.h"
 #include "cilk.h"
 #include "tree-chkp.h"
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index 4022bdf..1a49e58 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "ubsan.h"
 #include "c-family/c-ubsan.h"
+#include "params.h"
 #include "asan.h"
 #include "stor-layout.h"
 #include "builtins.h"
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 77a1964..cbb30d5 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -876,8 +876,7 @@ asan_sanitize_stack_p (void)
 {
   return ((flag_sanitize & SANITIZE_ADDRESS)
 	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
+	  && !asan_no_sanitize_address_p ());
 }
 
 /* A subroutine of expand_used_vars.  Binpack the variables into
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 0ea326d..d341478 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "intl.h"
 #include "c-family/c-ada-spec.h"
+#include "params.h"
 #include "asan.h"
 
 extern cpp_reader *parse_in;
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index dd57e16..ca1399d 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -229,6 +229,7 @@ enum sanitize_code {
   SANITIZE_OBJECT_SIZE = 1UL << 20,
   SANITIZE_VPTR = 1UL << 21,
   SANITIZE_BOUNDS_STRICT = 1UL << 22,
+  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -237,7 +238,9 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT,
+  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
+			| SANITIZE_USE_AFTER_SCOPE
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index f13980d..cd26f79 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "params.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set <tree> asan_poisoned_variables (13);
 
 enum gimplify_omp_var_data
 {
@@ -1087,6 +1092,29 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+  gimplify_seq_add_stmt (seq_p, g);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1189,6 +1217,11 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   /* Add clobbers for all variables that go out of scope.  */
   for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
     {
+      bool unpoison_var = asan_poisoned_variables.contains (t);
+      if (asan_sanitize_use_after_scope ()
+	  && unpoison_var)
+	asan_poisoned_variables.remove (t);
+
       if (TREE_CODE (t) == VAR_DECL
 	  && !is_global_var (t)
 	  && DECL_CONTEXT (t) == current_function_decl
@@ -1197,17 +1230,26 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  && !DECL_HAS_VALUE_EXPR_P (t)
 	  /* Only care for variables that have to be in memory.  Others
 	     will be rewritten into SSA names, hence moved to the top-level.  */
-	  && !is_gimple_reg (t)
-	  && flag_stack_reuse != SR_NONE)
+	  && !is_gimple_reg (t))
 	{
-	  tree clobber = build_constructor (TREE_TYPE (t), NULL);
-	  gimple *clobber_stmt;
-	  TREE_THIS_VOLATILE (clobber) = 1;
-	  clobber_stmt = gimple_build_assign (t, clobber);
-	  gimple_set_location (clobber_stmt, end_locus);
-	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
-
-	  if (flag_openacc && oacc_declare_returns != NULL)
+	  if (flag_stack_reuse != SR_NONE)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (t), NULL);
+	      gimple *clobber_stmt;
+	      TREE_THIS_VOLATILE (clobber) = 1;
+	      clobber_stmt = gimple_build_assign (t, clobber);
+	      gimple_set_location (clobber_stmt, end_locus);
+	      gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+	    }
+
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ()
+	      && unpoison_var)
+	    asan_poison_variable (t, true, &cleanup);
+
+	  if (flag_stack_reuse != SR_NONE
+	      && flag_openacc
+	      && oacc_declare_returns != NULL)
 	    {
 	      tree *c = oacc_declare_returns->get (t);
 	      if (c != NULL)
@@ -1479,6 +1521,22 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
 	gimplify_vla_decl (decl, seq_p);
 
+      tree unit_size = DECL_SIZE_UNIT (decl);
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && TREE_CODE (unit_size) == INTEGER_CST
+	  && needs_to_live_in_memory (decl)
+	  && DECL_NAME (decl) != NULL
+	  && IDENTIFIER_POINTER (DECL_NAME (decl)) != NULL
+	  && !TREE_STATIC (decl))
+	{
+	  TREE_ADDRESSABLE (decl) = 1;
+	  DECL_GIMPLE_REG_P (decl) = 0;
+
+	  asan_poisoned_variables.add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
+
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
 	 variables, though it would be better if the front ends would
@@ -10030,6 +10088,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10540,6 +10617,23 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 		      == current_function_decl);
 	  gimplify_seq_add_stmt (pre_p,
 			  gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ())
+	    {
+	      unsigned c = asan_poisoned_variables.elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set <tree>::iterator it
+		   = asan_poisoned_variables.begin ();
+		   it != asan_poisoned_variables.end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -11633,7 +11727,11 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
   bind = gimplify_body (fndecl, true);
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/hash-set.h b/gcc/hash-set.h
index 4ef4eba..05b2a98 100644
--- a/gcc/hash-set.h
+++ b/gcc/hash-set.h
@@ -65,6 +65,13 @@ public:
       m_table.remove_elt_with_hash (k, Traits::hash (k));
     }
 
+  /* Clear all elements of the hash set.  */
+
+  void empty ()
+    {
+      m_table.empty ();
+    }
+
   /* Call the call back on each pair of key and value with the passed in
      arg.  */
 
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c867ddc..680a2d3 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -235,6 +235,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 e729d85..81492ad 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts-global.c b/gcc/opts-global.c
index b7e5232..963b542 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "toplev.h"
 #include "context.h"
+#include "params.h"
 #include "asan.h"
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 0f9431a..c037a1f 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -972,6 +972,18 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Force -fstack-reuse=none in case -fsanitize=use-after-scope is enabled.  */
+  if (opts->x_flag_sanitize & SANITIZE_USE_AFTER_SCOPE)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize=use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1443,6 +1455,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
   SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),
+  SANITIZER_OPT (use-after-scope, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+		 | SANITIZE_USE_AFTER_SCOPE),
   SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
   SANITIZER_OPT (thread, SANITIZE_THREAD),
   SANITIZER_OPT (leak, SANITIZE_LEAK),
diff --git a/gcc/params.def b/gcc/params.def
index 62a1e40..0c63989 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1144,6 +1144,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+         256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 7221ab6..f8bd022 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -243,5 +243,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sancov.c b/gcc/sancov.c
index f3211dd..655ff64 100644
--- a/gcc/sancov.c
+++ b/gcc/sancov.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 
 namespace {
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 2660453..43488a9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -29,9 +29,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 #include "fold-const.h"
 #include "gimple-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "ubsan.h"
-#include "params.h"
 #include "tree-hash-traits.h"
 
 
@@ -704,6 +704,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 2ad2f92..99397eb 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "ipa-chkp.h"
 #include "gomp-constants.h"
+#include "params.h"
 #include "asan.h"
 
 
diff --git a/gcc/tsan.c b/gcc/tsan.c
index 47764bc..bdd002b 100644
--- a/gcc/tsan.c
+++ b/gcc/tsan.c
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-iterator.h"
 #include "tree-ssa-propagate.h"
 #include "tree-ssa-loop-ivopts.h"
+#include "params.h"
 #include "tsan.h"
 #include "asan.h"
 #include "builtins.h"
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index c5543f8..2b4cbee 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "ubsan.h"
 #include "expr.h"
+#include "params.h"
 #include "asan.h"
 #include "gimplify-me.h"
 #include "dfp.h"
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 4a7124e..47e5215 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "debug.h"
 #include "common/common-target.h"
+#include "params.h"
 #include "asan.h"
 #include "rtl-iter.h"
 
-- 
2.8.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Introduce tests for -fsanitize=use-after-scope
  2016-05-06 11:08 ` [PATCH] Introduce tests for -fsanitize=use-after-scope Martin Liška
@ 2016-05-11 12:56   ` Martin Liška
  0 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-05-11 12:56 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 312 bytes --]

On 05/06/2016 01:07 PM, Martin Liška wrote:
> Hi.
> 
> This is a new test coverage for the new sanitizer option.
> 
> Martin

Hello.

This is second version of tests. I fixed a test where a variable overflowed and
couple of tests were adopted from LLVM's testsuite (basically rewritten from scratch).

Martin

[-- Attachment #2: 0002-Introduce-tests-for-fsanitize-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 8489 bytes --]

From 7dd04d12a4bf04ac18dca266f44b18e39e1d711f Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 4 May 2016 12:57:05 +0200
Subject: [PATCH 2/2] Introduce tests for -fsanitize=use-after-scope

gcc/testsuite/ChangeLog:

2016-05-10  Martin Liska  <mliska@suse.cz>

	* g++.dg/asan/use-after-scope-1.C: New test.
	* g++.dg/asan/use-after-scope-2.C: New test.
	* gcc.dg/asan/use-after-scope-1.c: New test.
	* gcc.dg/asan/use-after-scope-2.c: New test.
	* gcc.dg/asan/use-after-scope-3.c: New test.
	* gcc.dg/asan/use-after-scope-4.c: New test.
	* gcc.dg/asan/use-after-scope-5.c: New test.
	* gcc.dg/asan/use-after-scope-goto-1.c: New test.
---
 gcc/testsuite/g++.dg/asan/use-after-scope-1.C      | 22 ++++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-2.C      | 41 ++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c      | 19 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c      | 48 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c      | 21 ++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c      | 17 ++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c      | 28 +++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c | 47 +++++++++++++++++++++
 8 files changed, 243 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-1.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-2.C
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c

diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
new file mode 100644
index 0000000..ed61aed
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+#include <functional>
+
+int main() {
+  std::function<int()> function;
+  {
+    int v = 0;
+    function = [&v]()
+    {
+      return v;
+    };
+  }
+  return function();
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'v' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
new file mode 100644
index 0000000..d82bc88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
@@ -0,0 +1,41 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+#include <stdio.h>
+
+struct Test
+{
+  Test ()
+    {
+      my_value = 0;
+    }
+
+  ~Test ()
+    {
+      fprintf (stderr, "Value: %d\n", *my_value);
+    }
+
+  void init (int *v)
+    {
+      my_value = v;
+    }
+
+  int *my_value;
+};
+
+int main(int argc, char **argv)
+{
+  Test t;
+
+  {
+    int x = argc;
+    t.init(&x);
+  }
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
new file mode 100644
index 0000000..1420416
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
@@ -0,0 +1,19 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  return *(ptr+8);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
new file mode 100644
index 0000000..96f0082
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
@@ -0,0 +1,48 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+int *bar (int *x, int *y) { return y; }
+
+int foo (void)
+{
+  char *p;
+  {
+    char a = 0;
+    p = &a;
+  }
+
+  if (*p)
+    return 1;
+  else
+    return 0;
+}
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  int a[16];
+  int *p, *q = a;
+  {
+    int b[16];
+    p = bar (a, b);
+  }
+  bar (a, q);
+  {
+    int c[16];
+    q = bar (a, c);
+  }
+  int v = *bar (a, q);
+  return v;
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'c' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
new file mode 100644
index 0000000..5241f37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
@@ -0,0 +1,21 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
new file mode 100644
index 0000000..d50ce5f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+
+int
+__attribute__((no_sanitize_address))
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
new file mode 100644
index 0000000..bcfbb1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
@@ -0,0 +1,28 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope" }
+// { dg-shouldfail "asan" }
+
+int *ptr;
+
+__attribute__((always_inline))
+inline static void
+foo(int v)
+{
+  int values[10];
+  for (unsigned i = 0; i < 10; i++)
+    values[i] = v;
+
+  ptr = &values[3];
+}
+
+int
+main (int argc, char **argv)
+{
+  foo (argc);
+
+  return *ptr;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'values' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
new file mode 100644
index 0000000..32d5680
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-additional-options "-fsanitize=use-after-scope -fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+  int b = 123;
+  int c = 123;
+  int d = 123;
+  int e = 123;
+  int f = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    int *ptr2;
+    int *ptr3;
+    int *ptr4;
+    int *ptr5;
+    int *ptr6;
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	ptr2 = &b;
+        *ptr2 = 1;
+	ptr3 = &c;
+        *ptr3 = 1;
+	ptr4 = &d;
+        *ptr4 = 1;
+	ptr5 = &e;
+        *ptr5 = 1;
+	ptr6 = &f;
+        *ptr6 = 1;
+	return 0;
+      }
+  }
+  else
+    goto label;
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &c, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &e, 4\\);" 2 "asan0" } }  */
-- 
2.8.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-11 12:54   ` Martin Liška
@ 2016-05-12 10:42     ` Jakub Jelinek
  2016-05-12 14:12       ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-05-12 10:42 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Wed, May 11, 2016 at 02:54:01PM +0200, Martin Liška wrote:
> On 05/06/2016 02:22 PM, Jakub Jelinek wrote:
> > On Fri, May 06, 2016 at 01:04:30PM +0200, Martin Liška wrote:
> >> I've started working on the patch couple of month go, basically after
> >> a brief discussion with Jakub on IRC.
> >>
> >> I'm sending the initial version which can successfully run instrumented
> >> tramp3d, postgresql server and Inkscape. It catches the basic set of
> >> examples which are added in following patch.
> >>
> >> The implementation is quite straightforward as works in following steps:
> >>
> >> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
> >> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
> >> and the variable is marked as addressable
> > 
> > Not all vars have DECL_EXPRs though.

Just random comments from quick skim, need to find enough spare time to
actually try it and see how it works.

> Yeah, I've spotted one interesting example which is part of LLVM's testsuite:
> 
> struct IntHolder {
>   int val;
> };
> 
> const IntHolder *saved;
> 
> void save(const IntHolder &holder) {
>   saved = &holder;
> }
> 
> int main(int argc, char *argv[]) {
>   save({10});
>   int x = saved->val;  // BOOM
>   return x;
> }
> 
> It would be also good to handle such temporaries. Any suggestions how to handle that in gimplifier?

Dunno, guess you need to do something in the FE for it already (talk to
Jason?).  At least in *.original dump there is already:
  <<cleanup_point <<< Unknown tree: expr_stmt
  save ((const struct IntHolder &) &TARGET_EXPR <D.2263, {.val=10}>) >>>>>;
    int x = (int) saved->val;
  return <retval> = x;
and the info on where the D.2263 temporary goes out of scope is lost.

> Apart from that, second version of the patch changes:
> + fixed issues with missing stack unpoisoning; currently, I mark all VAR_DECLs that
> are in ASAN_MARK internal fns and stack prologue/epilogue is emitted just for these vars
> + removed unneeded hunks (tree-vect-patterns.c and asan_poisoning.cc)
> + LABEL unpoisoning code makes stable sort for variables that were already used in the context
> + stack poisoning hasn't worked for -O1+ due to following guard in asan.c
>  /* Automatic vars in the current function will be always accessible.  */
> + direct shadow memory poisoning/unpoisoning code is introduced - in both scenarios (RTL and GIMPLE),
> I would appreciate feedback if storing multiple bytes is fine? What is the maximum memory wide
> store mode supported by a target? How can I get such information?
> + the maximum object size handled by a direct emission is guarded by use-after-scope-direct-emission-threshold
> parameter; initial value (256B) should maximally emit store of 32B

Would be better if user visible param was in bytes rather than bits IMHO.

> Yeah, depends because of:
> 
> static inline bool
> asan_sanitize_use_after_scope (void)
> {
>   return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
> 	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
> 	  && flag_stack_reuse == SR_NONE
> 	  && ASAN_STACK);
> }
> 
> Where ASAN_STACK comes from params.h.

I'd prefer just prototype the function in the header and define in asan.c
or some other source file.  Or maybe split it, do the important case
(flag_sanitize check) inline and call out of line function for the rest.
Why do you check flag_stack_reuse?  I thought you'd arrange for it to be
different when -fsanitize=use-after-scope?

> @@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
>  static bool asan_shadow_offset_computed;
>  static vec<char *> sanitized_sections;
>  
> +/* Set of variable declarations that are going to be guarded by
> +   use-after-scope sanitizer.  */
> +
> +static hash_set <tree> asan_handled_variables(13);

Not sure about the formatting here, don't we use xxx<arg> instead of xxx <arg>
?  And I'd expect space before (.
> @@ -1020,6 +1020,91 @@ asan_function_start (void)
>  			 current_function_funcdef_no);
>  }
>  
> +/* Return number of shadow bytes that are occupied by a local variable
> +   of SIZE bytes.  */
> +
> +static unsigned HOST_WIDE_INT
> +get_shadow_memory_size (unsigned HOST_WIDE_INT size)
> +{
> +  /* Round up size of object.  */
> +  unsigned HOST_WIDE_INT r;
> +  if ((r = size % BITS_PER_UNIT) != 0)
> +    size += BITS_PER_UNIT - r;

Isn't there a ROUND_UP macro?

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-12 10:42     ` Jakub Jelinek
@ 2016-05-12 14:12       ` Martin Liška
  2016-08-12 12:42         ` Martin Liška
                           ` (2 more replies)
  0 siblings, 3 replies; 111+ messages in thread
From: Martin Liška @ 2016-05-12 14:12 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 5207 bytes --]

On 05/12/2016 12:41 PM, Jakub Jelinek wrote:
> On Wed, May 11, 2016 at 02:54:01PM +0200, Martin Liška wrote:
>> On 05/06/2016 02:22 PM, Jakub Jelinek wrote:
>>> On Fri, May 06, 2016 at 01:04:30PM +0200, Martin Liška wrote:
>>>> I've started working on the patch couple of month go, basically after
>>>> a brief discussion with Jakub on IRC.
>>>>
>>>> I'm sending the initial version which can successfully run instrumented
>>>> tramp3d, postgresql server and Inkscape. It catches the basic set of
>>>> examples which are added in following patch.
>>>>
>>>> The implementation is quite straightforward as works in following steps:
>>>>
>>>> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
>>>> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
>>>> and the variable is marked as addressable
>>>
>>> Not all vars have DECL_EXPRs though.
> 
> Just random comments from quick skim, need to find enough spare time to
> actually try it and see how it works.
> 
>> Yeah, I've spotted one interesting example which is part of LLVM's testsuite:
>>
>> struct IntHolder {
>>   int val;
>> };
>>
>> const IntHolder *saved;
>>
>> void save(const IntHolder &holder) {
>>   saved = &holder;
>> }
>>
>> int main(int argc, char *argv[]) {
>>   save({10});
>>   int x = saved->val;  // BOOM
>>   return x;
>> }
>>
>> It would be also good to handle such temporaries. Any suggestions how to handle that in gimplifier?
> 
> Dunno, guess you need to do something in the FE for it already (talk to
> Jason?).  At least in *.original dump there is already:
>   <<cleanup_point <<< Unknown tree: expr_stmt
>   save ((const struct IntHolder &) &TARGET_EXPR <D.2263, {.val=10}>) >>>>>;
>     int x = (int) saved->val;
>   return <retval> = x;
> and the info on where the D.2263 temporary goes out of scope is lost.

Thanks for sample, I will ask Jason to help me with that.

> 
>> Apart from that, second version of the patch changes:
>> + fixed issues with missing stack unpoisoning; currently, I mark all VAR_DECLs that
>> are in ASAN_MARK internal fns and stack prologue/epilogue is emitted just for these vars
>> + removed unneeded hunks (tree-vect-patterns.c and asan_poisoning.cc)
>> + LABEL unpoisoning code makes stable sort for variables that were already used in the context
>> + stack poisoning hasn't worked for -O1+ due to following guard in asan.c
>>  /* Automatic vars in the current function will be always accessible.  */
>> + direct shadow memory poisoning/unpoisoning code is introduced - in both scenarios (RTL and GIMPLE),
>> I would appreciate feedback if storing multiple bytes is fine? What is the maximum memory wide
>> store mode supported by a target? How can I get such information?
>> + the maximum object size handled by a direct emission is guarded by use-after-scope-direct-emission-threshold
>> parameter; initial value (256B) should maximally emit store of 32B
> 
> Would be better if user visible param was in bytes rather than bits IMHO.
> 

Well, the size of an object is in bytes, but as we map every 8 (yeah, that's configurable, I'm quite curious about
real respecting of ASAN_SHADOW_SHIFT) bytes of real memory to
a single byte in shadow memory, thus the division by 8 is needed.

>> Yeah, depends because of:
>>
>> static inline bool
>> asan_sanitize_use_after_scope (void)
>> {
>>   return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
>> 	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
>> 	  && flag_stack_reuse == SR_NONE
>> 	  && ASAN_STACK);
>> }
>>
>> Where ASAN_STACK comes from params.h.
> 
> I'd prefer just prototype the function in the header and define in asan.c
> or some other source file.  Or maybe split it, do the important case
> (flag_sanitize check) inline and call out of line function for the rest.
> Why do you check flag_stack_reuse?  I thought you'd arrange for it to be
> different when -fsanitize=use-after-scope?

Right, the sanitization does not relate to flag_stack, thus removing the dependency,
we can remove need of including params.h in various places.

> 
>> @@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
>>  static bool asan_shadow_offset_computed;
>>  static vec<char *> sanitized_sections;
>>  
>> +/* Set of variable declarations that are going to be guarded by
>> +   use-after-scope sanitizer.  */
>> +
>> +static hash_set <tree> asan_handled_variables(13);
> 
> Not sure about the formatting here, don't we use xxx<arg> instead of xxx <arg>
> ?  And I'd expect space before (.

Yeah, done.

>> @@ -1020,6 +1020,91 @@ asan_function_start (void)
>>  			 current_function_funcdef_no);
>>  }
>>  
>> +/* Return number of shadow bytes that are occupied by a local variable
>> +   of SIZE bytes.  */
>> +
>> +static unsigned HOST_WIDE_INT
>> +get_shadow_memory_size (unsigned HOST_WIDE_INT size)
>> +{
>> +  /* Round up size of object.  */
>> +  unsigned HOST_WIDE_INT r;
>> +  if ((r = size % BITS_PER_UNIT) != 0)
>> +    size += BITS_PER_UNIT - r;
> 
> Isn't there a ROUND_UP macro?

Thanks. I was looking for the macro.

I'm sending v3 where all of aforementioned notes were handled.

Thanks,
Martin

> 
> 	Jakub
> 


[-- Attachment #2: 0001-Introduce-fsanitize-use-after-scope-v3.patch --]
[-- Type: text/x-patch, Size: 30326 bytes --]

From 59ac662acb13510305c6db84c4c5273dab7b4fcc Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/3] Introduce -fsanitize=use-after-scope

gcc/ChangeLog:

2016-05-10  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Enum is moved to a header
	file.
	(asan_init_shadow_ptr_types): Add new shadow_ptr_type.
	(get_shadow_memory_size): New function.
	(asan_poison_stack_variables): Likewise.
	(asan_emit_stack_protection): Poison and unpoison stack
	variables.
	(build_shadow_mem_access): New arguments is added.
	(instrument_derefs): Do not skip automatic variables if we
	instrument for use-after-scope.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	* asan.h (enum asan_mark_flags): Move from the source file.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* cfgexpand.c (asan_sanitize_stack_p): Use
	asan_no_sanitize_address_p.
	* flag-types.h (enum sanitize_code): Add
	SANITIZE_ADDRESS_USE_AFTER_SCOPE enum value.
	* gimplify.c (asan_poison_variable): New function.
	(gimplify_bind_expr): Unpoison stack variables.
	(gimplify_decl_expr): Poison stack variables.
	(gimplify_expr): Unpoison stack variables that precede a label.
	(gimplify_function_tree): Guard that all local variables
	(sort_by_decl_uid): New function.
	are properly unpoisoned.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def: Declare ASAN_MARK.
	* opts.c (finish_options): Handle interaction between -fstack-reuse
	and -fsanitize=use-after-scope.
	* params.def: Add new param use-after-scope-direct-emission-threshold.
	* params.h: Define the param.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory.
	* sanopt.c (pass_sanopt::execute): Expand ASAN_MARK internal
	fns.
	* hash-set.h (hash_set::empty): New function.
---
 gcc/asan.c          | 253 ++++++++++++++++++++++++++++++++++++++++++++++------
 gcc/asan.h          |  46 ++++++++--
 gcc/cfgexpand.c     |  15 ++--
 gcc/flag-types.h    |   5 +-
 gcc/gimplify.c      | 118 +++++++++++++++++++++---
 gcc/hash-set.h      |   7 ++
 gcc/internal-fn.c   |   9 ++
 gcc/internal-fn.def |   1 +
 gcc/opts.c          |  14 +++
 gcc/params.def      |   6 ++
 gcc/params.h        |   2 +
 gcc/sanitizer.def   |   4 +
 gcc/sanopt.c        |   3 +
 13 files changed, 436 insertions(+), 47 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 71095fb..38a7c90 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> asan_handled_variables (13);
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -313,22 +318,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -342,7 +338,7 @@ struct asan_mem_ref
   HOST_WIDE_INT access_size;
 };
 
-object_allocator <asan_mem_ref> asan_mem_ref_pool ("asan_mem_ref");
+object_allocator<asan_mem_ref> asan_mem_ref_pool ("asan_mem_ref");
 
 /* Initializes an instance of asan_mem_ref.  */
 
@@ -931,12 +927,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1020,6 +1020,86 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+get_shadow_memory_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, BITS_PER_UNIT) / BITS_PER_UNIT;
+}
+
+/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
+   allocated for local variables, localted in OFFSETS.  LENGTH is number
+   of OFFSETS, BASE is the register holding the stack base,
+   against which OFFSETS array offsets are relative to.  BASE_OFFSET represents
+   an offset requested by alignment and similar stuff.  */
+
+static void
+asan_poison_stack_variables (rtx shadow_base, rtx base,
+			     HOST_WIDE_INT base_offset,
+			     HOST_WIDE_INT *offsets, int length,
+			     tree *decls, bool poison)
+{
+  if (asan_sanitize_use_after_scope ())
+    for (int l = length - 2; l > 0; l -= 2)
+      {
+	tree decl = decls[l / 2 - 1];
+	HOST_WIDE_INT var_offset = offsets[l];
+	HOST_WIDE_INT var_end_offset = offsets[l - 1];
+	if (!asan_handled_variables.contains (decl))
+	  {
+	    if (dump_file && (dump_flags & TDF_DETAILS))
+	      fprintf (dump_file, "Skipping stack emission for variable: %s "
+		       "(%ldB)\n",
+		       IDENTIFIER_POINTER (DECL_NAME (decl)),
+		       var_end_offset - var_offset);
+	    continue;
+	  }
+
+	rtx source = expand_binop (Pmode, add_optab, base,
+				   gen_int_mode
+				    (var_offset - base_offset, Pmode),
+				   NULL_RTX, 1, OPTAB_DIRECT);
+
+	HOST_WIDE_INT size = var_end_offset - var_offset;
+	if (size <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+	  {
+	    unsigned HOST_WIDE_INT shadow_size
+	      = get_shadow_memory_size (size);
+
+	    rtx shadow_mem = gen_rtx_MEM (SImode, shadow_base);
+	    rtx var_mem = adjust_address (shadow_mem, QImode,
+					  (var_offset - base_offset)
+					  >> ASAN_SHADOW_SHIFT);
+
+	    char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+	    for (unsigned i = 0; i < shadow_size; ++i)
+	      {
+		emit_move_insn (var_mem, gen_int_mode (c, QImode));
+		var_mem = adjust_address (var_mem, QImode, 1);
+	      }
+	  }
+	else
+	  {
+	    rtx size_rtx = GEN_INT (size);
+	    const char *fname = poison ?  "__asan_poison_stack_memory"
+	      :"__asan_unpoison_stack_memory";
+	    rtx ret = init_one_libfunc (fname);
+	    emit_library_call (ret, LCT_NORMAL, VOIDmode, 2, source, ptr_mode,
+			       size_rtx, TYPE_MODE (pointer_sized_int_node));
+	  }
+
+	if (dump_file && (dump_flags & TDF_DETAILS))
+	  fprintf (dump_file, "Emitting stack %s for variable: %s"
+		   "(%ldB)\n",
+		   poison ? "poisoning" : "unpoisoning",
+		   IDENTIFIER_POINTER (DECL_NAME (decl)),
+		   var_end_offset - var_offset);
+      }
+}
+
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1228,6 +1308,11 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 	}
       cur_shadow_byte = ASAN_STACK_MAGIC_MIDDLE;
     }
+
+  /* Poison stack variables at the very beginning of a function.  */
+  asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+			       decls, true);
+
   do_pending_stack_adjust ();
 
   /* Construct epilogue sequence.  */
@@ -1309,6 +1394,14 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
       asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
     }
 
+  /* Unpoison stack variables at the end of a function.  As the former
+     stack memory can be reused, we have to unpoison the memory.  */
+  asan_poison_stack_variables (shadow_base, base, base_offset, offsets, length,
+			       decls, false);
+
+  /* Clean-up set with instrumented stack variables.  */
+  asan_handled_variables.empty ();
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1593,7 +1686,8 @@ insert_if_then_before_iter (gcond *cond,
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_ptr = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1616,11 +1710,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_ptr)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1824,7 +1922,8 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && !asan_sanitize_use_after_scope ())
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2573,6 +2672,110 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 unsigned char value, unsigned bytes)
+{
+  tree shadow_ptr_type;
+
+  switch (bytes)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < bytes; ++i)
+    val |= (unsigned HOST_WIDE_INT) value << (BITS_PER_UNIT * i);
+
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  asan_handled_variables.add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+				  NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size
+	= get_shadow_memory_size (size_in_bytes);
+      char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, c, size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..a38df9e 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -50,15 +51,33 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
@@ -105,4 +124,21 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
+	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE);
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 77a1964..262226f 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -876,8 +876,7 @@ asan_sanitize_stack_p (void)
 {
   return ((flag_sanitize & SANITIZE_ADDRESS)
 	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
+	  && !asan_no_sanitize_address_p ());
 }
 
 /* A subroutine of expand_used_vars.  Binpack the variables into
@@ -941,7 +940,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1132,7 +1132,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
 	  base = virtual_stack_vars_rtx;
-	  if (asan_sanitize_stack_p () && pred)
+	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
+	      && pred)
 	    {
 	      HOST_WIDE_INT prev_offset
 		= align_base (frame_offset,
@@ -1503,7 +1504,9 @@ defer_stack_allocation (tree var, bool toplevel)
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
      Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || asan_sanitize_use_after_scope ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -2203,7 +2206,7 @@ expand_used_vars (void)
 	    expand_stack_vars (stack_protect_decl_phase_2, &data);
 	}
 
-      if (asan_sanitize_stack_p ())
+      if (asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
 	/* Phase 3, any partitions that need asan protection
 	   in addition to phase 1 and 2.  */
 	expand_stack_vars (asan_decl_phase_3, &data);
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index dd57e16..ca1399d 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -229,6 +229,7 @@ enum sanitize_code {
   SANITIZE_OBJECT_SIZE = 1UL << 20,
   SANITIZE_VPTR = 1UL << 21,
   SANITIZE_BOUNDS_STRICT = 1UL << 22,
+  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -237,7 +238,9 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT,
+  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
+			| SANITIZE_USE_AFTER_SCOPE
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index c433a84..5db69e2 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "params.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> asan_poisoned_variables (13);
 
 enum gimplify_omp_var_data
 {
@@ -1087,6 +1092,29 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+  gimplify_seq_add_stmt (seq_p, g);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1189,6 +1217,11 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
   /* Add clobbers for all variables that go out of scope.  */
   for (t = BIND_EXPR_VARS (bind_expr); t ; t = DECL_CHAIN (t))
     {
+      bool unpoison_var = asan_poisoned_variables.contains (t);
+      if (asan_sanitize_use_after_scope ()
+	  && unpoison_var)
+	asan_poisoned_variables.remove (t);
+
       if (TREE_CODE (t) == VAR_DECL
 	  && !is_global_var (t)
 	  && DECL_CONTEXT (t) == current_function_decl
@@ -1197,17 +1230,26 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 	  && !DECL_HAS_VALUE_EXPR_P (t)
 	  /* Only care for variables that have to be in memory.  Others
 	     will be rewritten into SSA names, hence moved to the top-level.  */
-	  && !is_gimple_reg (t)
-	  && flag_stack_reuse != SR_NONE)
+	  && !is_gimple_reg (t))
 	{
-	  tree clobber = build_constructor (TREE_TYPE (t), NULL);
-	  gimple *clobber_stmt;
-	  TREE_THIS_VOLATILE (clobber) = 1;
-	  clobber_stmt = gimple_build_assign (t, clobber);
-	  gimple_set_location (clobber_stmt, end_locus);
-	  gimplify_seq_add_stmt (&cleanup, clobber_stmt);
-
-	  if (flag_openacc && oacc_declare_returns != NULL)
+	  if (flag_stack_reuse != SR_NONE)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (t), NULL);
+	      gimple *clobber_stmt;
+	      TREE_THIS_VOLATILE (clobber) = 1;
+	      clobber_stmt = gimple_build_assign (t, clobber);
+	      gimple_set_location (clobber_stmt, end_locus);
+	      gimplify_seq_add_stmt (&cleanup, clobber_stmt);
+	    }
+
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ()
+	      && unpoison_var)
+	    asan_poison_variable (t, true, &cleanup);
+
+	  if (flag_stack_reuse != SR_NONE
+	      && flag_openacc
+	      && oacc_declare_returns != NULL)
 	    {
 	      tree *c = oacc_declare_returns->get (t);
 	      if (c != NULL)
@@ -1479,6 +1521,22 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
 	gimplify_vla_decl (decl, seq_p);
 
+      tree unit_size = DECL_SIZE_UNIT (decl);
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && TREE_CODE (unit_size) == INTEGER_CST
+	  && needs_to_live_in_memory (decl)
+	  && DECL_NAME (decl) != NULL
+	  && IDENTIFIER_POINTER (DECL_NAME (decl)) != NULL
+	  && !TREE_STATIC (decl))
+	{
+	  TREE_ADDRESSABLE (decl) = 1;
+	  DECL_GIMPLE_REG_P (decl) = 0;
+
+	  asan_poisoned_variables.add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
+
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
 	 variables, though it would be better if the front ends would
@@ -10030,6 +10088,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10540,6 +10617,23 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 		      == current_function_decl);
 	  gimplify_seq_add_stmt (pre_p,
 			  gimple_build_label (LABEL_EXPR_LABEL (*expr_p)));
+
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ())
+	    {
+	      unsigned c = asan_poisoned_variables.elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set<tree>::iterator it
+		   = asan_poisoned_variables.begin ();
+		   it != asan_poisoned_variables.end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -11633,7 +11727,11 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
   bind = gimplify_body (fndecl, true);
+  gcc_checking_assert (!asan_sanitize_use_after_scope ()
+		       || asan_poisoned_variables.elements () == 0);
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/hash-set.h b/gcc/hash-set.h
index 4ef4eba..05b2a98 100644
--- a/gcc/hash-set.h
+++ b/gcc/hash-set.h
@@ -65,6 +65,13 @@ public:
       m_table.remove_elt_with_hash (k, Traits::hash (k));
     }
 
+  /* Clear all elements of the hash set.  */
+
+  void empty ()
+    {
+      m_table.empty ();
+    }
+
   /* Call the call back on each pair of key and value with the passed in
      arg.  */
 
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index c867ddc..680a2d3 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -235,6 +235,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 e729d85..81492ad 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts.c b/gcc/opts.c
index 63d41ca..bf5da1f 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -972,6 +972,18 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Force -fstack-reuse=none in case -fsanitize=use-after-scope is enabled.  */
+  if (opts->x_flag_sanitize & SANITIZE_USE_AFTER_SCOPE)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize=use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1443,6 +1455,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
   SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),
+  SANITIZER_OPT (use-after-scope, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+		 | SANITIZE_USE_AFTER_SCOPE),
   SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
   SANITIZER_OPT (thread, SANITIZE_THREAD),
   SANITIZER_OPT (leak, SANITIZE_LEAK),
diff --git a/gcc/params.def b/gcc/params.def
index 62a1e40..0c63989 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1144,6 +1144,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+         256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 7221ab6..f8bd022 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -243,5 +243,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 2660453..4436705 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -704,6 +704,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
-- 
2.8.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-12 14:12       ` Martin Liška
@ 2016-08-12 12:42         ` Martin Liška
  2016-08-18 13:36         ` Jakub Jelinek
  2016-09-03 15:23         ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Jakub Jelinek
  2 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-08-12 12:42 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

PING^1

> On 05/12/2016 12:41 PM, Jakub Jelinek wrote:
>> On Wed, May 11, 2016 at 02:54:01PM +0200, Martin Liška wrote:
>>> On 05/06/2016 02:22 PM, Jakub Jelinek wrote:
>>>> On Fri, May 06, 2016 at 01:04:30PM +0200, Martin Liška wrote:
>>>>> I've started working on the patch couple of month go, basically after
>>>>> a brief discussion with Jakub on IRC.
>>>>>
>>>>> I'm sending the initial version which can successfully run instrumented
>>>>> tramp3d, postgresql server and Inkscape. It catches the basic set of
>>>>> examples which are added in following patch.
>>>>>
>>>>> The implementation is quite straightforward as works in following steps:
>>>>>
>>>>> 1) Every local variable stack slot is poisoned at the very beginning of a function (RTL emission)
>>>>> 2) In gimplifier, once we spot a DECL_EXPR, a variable is unpoisoned (by emitting ASAN_MARK builtin)
>>>>> and the variable is marked as addressable
>>>>
>>>> Not all vars have DECL_EXPRs though.
>>
>> Just random comments from quick skim, need to find enough spare time to
>> actually try it and see how it works.
>>
>>> Yeah, I've spotted one interesting example which is part of LLVM's testsuite:
>>>
>>> struct IntHolder {
>>>   int val;
>>> };
>>>
>>> const IntHolder *saved;
>>>
>>> void save(const IntHolder &holder) {
>>>   saved = &holder;
>>> }
>>>
>>> int main(int argc, char *argv[]) {
>>>   save({10});
>>>   int x = saved->val;  // BOOM
>>>   return x;
>>> }
>>>
>>> It would be also good to handle such temporaries. Any suggestions how to handle that in gimplifier?
>>
>> Dunno, guess you need to do something in the FE for it already (talk to
>> Jason?).  At least in *.original dump there is already:
>>   <<cleanup_point <<< Unknown tree: expr_stmt
>>   save ((const struct IntHolder &) &TARGET_EXPR <D.2263, {.val=10}>) >>>>>;
>>     int x = (int) saved->val;
>>   return <retval> = x;
>> and the info on where the D.2263 temporary goes out of scope is lost.
> 
> Thanks for sample, I will ask Jason to help me with that.
> 
>>
>>> Apart from that, second version of the patch changes:
>>> + fixed issues with missing stack unpoisoning; currently, I mark all VAR_DECLs that
>>> are in ASAN_MARK internal fns and stack prologue/epilogue is emitted just for these vars
>>> + removed unneeded hunks (tree-vect-patterns.c and asan_poisoning.cc)
>>> + LABEL unpoisoning code makes stable sort for variables that were already used in the context
>>> + stack poisoning hasn't worked for -O1+ due to following guard in asan.c
>>>  /* Automatic vars in the current function will be always accessible.  */
>>> + direct shadow memory poisoning/unpoisoning code is introduced - in both scenarios (RTL and GIMPLE),
>>> I would appreciate feedback if storing multiple bytes is fine? What is the maximum memory wide
>>> store mode supported by a target? How can I get such information?
>>> + the maximum object size handled by a direct emission is guarded by use-after-scope-direct-emission-threshold
>>> parameter; initial value (256B) should maximally emit store of 32B
>>
>> Would be better if user visible param was in bytes rather than bits IMHO.
>>
> 
> Well, the size of an object is in bytes, but as we map every 8 (yeah, that's configurable, I'm quite curious about
> real respecting of ASAN_SHADOW_SHIFT) bytes of real memory to
> a single byte in shadow memory, thus the division by 8 is needed.
> 
>>> Yeah, depends because of:
>>>
>>> static inline bool
>>> asan_sanitize_use_after_scope (void)
>>> {
>>>   return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
>>> 	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
>>> 	  && flag_stack_reuse == SR_NONE
>>> 	  && ASAN_STACK);
>>> }
>>>
>>> Where ASAN_STACK comes from params.h.
>>
>> I'd prefer just prototype the function in the header and define in asan.c
>> or some other source file.  Or maybe split it, do the important case
>> (flag_sanitize check) inline and call out of line function for the rest.
>> Why do you check flag_stack_reuse?  I thought you'd arrange for it to be
>> different when -fsanitize=use-after-scope?
> 
> Right, the sanitization does not relate to flag_stack, thus removing the dependency,
> we can remove need of including params.h in various places.
> 
>>
>>> @@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
>>>  static bool asan_shadow_offset_computed;
>>>  static vec<char *> sanitized_sections;
>>>  
>>> +/* Set of variable declarations that are going to be guarded by
>>> +   use-after-scope sanitizer.  */
>>> +
>>> +static hash_set <tree> asan_handled_variables(13);
>>
>> Not sure about the formatting here, don't we use xxx<arg> instead of xxx <arg>
>> ?  And I'd expect space before (.
> 
> Yeah, done.
> 
>>> @@ -1020,6 +1020,91 @@ asan_function_start (void)
>>>  			 current_function_funcdef_no);
>>>  }
>>>  
>>> +/* Return number of shadow bytes that are occupied by a local variable
>>> +   of SIZE bytes.  */
>>> +
>>> +static unsigned HOST_WIDE_INT
>>> +get_shadow_memory_size (unsigned HOST_WIDE_INT size)
>>> +{
>>> +  /* Round up size of object.  */
>>> +  unsigned HOST_WIDE_INT r;
>>> +  if ((r = size % BITS_PER_UNIT) != 0)
>>> +    size += BITS_PER_UNIT - r;
>>
>> Isn't there a ROUND_UP macro?
> 
> Thanks. I was looking for the macro.
> 
> I'm sending v3 where all of aforementioned notes were handled.
> 
> Thanks,
> Martin
> 
>>
>> 	Jakub
>>
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-12 14:12       ` Martin Liška
  2016-08-12 12:42         ` Martin Liška
@ 2016-08-18 13:36         ` Jakub Jelinek
  2016-10-03  9:27           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  2016-09-03 15:23         ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Jakub Jelinek
  2 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-08-18 13:36 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, May 12, 2016 at 04:12:21PM +0200, Martin Liška wrote:
> --- a/gcc/asan.c
> +++ b/gcc/asan.c
> @@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
>  static bool asan_shadow_offset_computed;
>  static vec<char *> sanitized_sections;
>  
> +/* Set of variable declarations that are going to be guarded by
> +   use-after-scope sanitizer.  */
> +
> +static hash_set<tree> asan_handled_variables (13);

Doesn't this introduce yet another global ctor?  If yes (and we
unfortunately have already way too many), I'd strongly prefer to avoid that,
use pointer to hash_set<tree> or something similar.

> +/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
> +   allocated for local variables, localted in OFFSETS.  LENGTH is number
> +   of OFFSETS, BASE is the register holding the stack base,
> +   against which OFFSETS array offsets are relative to.  BASE_OFFSET represents
> +   an offset requested by alignment and similar stuff.  */
> +
> +static void
> +asan_poison_stack_variables (rtx shadow_base, rtx base,
> +			     HOST_WIDE_INT base_offset,
> +			     HOST_WIDE_INT *offsets, int length,
> +			     tree *decls, bool poison)
> +{
> +  if (asan_sanitize_use_after_scope ())
> +    for (int l = length - 2; l > 0; l -= 2)
> +      {

I think this is unfortunate, it leads to:
        movl    $-235802127, 2147450880(%rax)
        movl    $-185335552, 2147450884(%rax)
        movl    $-202116109, 2147450888(%rax)
        movb    $-8, 2147450884(%rax)
        movb    $-8, 2147450885(%rax)
(e.g. on use-after-scope-1.c).
The asan_emit_stack_protection function already walks all the
entries in the offsets array in both of the
  for (l = length; l; l -= 2)
loops, so please handle the initial poisoning and final unpoisoning there
as well.  The goal is that for variables that you want poison-after-scope
at the start of the function (btw, I've noticed that current SVN LLVM
doesn't bother with it and thus doesn't track "use before scope" (before the
scope is entered for the first time, maybe we shouldn't either, that would
catch only compiler bugs rather than user code bugs, right?)) have 0xf8
on all corresponding bytes including the one that would otherwise have 0x01
through 0x07.  When unpoisoning at the end of the function, again you should
combine that with unpoisoning of the red zone and partial zone bytes plus
the last 0x01 through 0x07, etc.

Plus, as I've mentioned before, it would be nice to optimize - for ASAN_MARK
unpoison appearing strictly before (i.e. dominating) the first (non-shadow) memory read
or write in the function (explicit or possible through function calls etc.)
you really don't need to unpoison (depending on whether we follow LLVM as
mentioned above then it can be removed without anything, or the decl needs
to be somehow marked and tell asan_emit_stack_protection it shouldn't poison
it at the start), and for ASAN_MARK poisoning appearing after the last
load/store in the function (post dominating those, you don't care about
noreturn though) you can combine that (remove the ASAN_MARK) with letting
asan_emit_stack_protection know it doesn't need to unpoison.

> +	    char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
> +	    for (unsigned i = 0; i < shadow_size; ++i)
> +	      {
> +		emit_move_insn (var_mem, gen_int_mode (c, QImode));
> +		var_mem = adjust_address (var_mem, QImode, 1);

When you combine it with the loop, you can also use the infrastructure to
handle it 4 bytes at a time.

Another thing I've noticed is that the inline expansion of
__asan_unpoison_stack_memory you emit looks buggy.
In use-after-scope-1.c I see:
  _9 = (unsigned long) &my_char;
  _10 = _9 >> 3;
  _11 = _10 + 2147450880;
  _12 = (signed char *) _11;
  MEM[(short int *)_12] = 0;

That would be fine only for 16 byte long my_char, but we have instead 9 byte
one.  So I believe in that case we need to store
0x00, 0x01 bytes, for little endian thus 0x0100.  You could use for it
a function similarly to asan_shadow_cst, just build INTEGER_CST rather than
CONST_INT out of it.  In general, poisioning is storing 0xf8 to all affected
shadow bytes, unpoisioning should restore the state what we would emit
without use-after-scope sanitization, which is all but the last byte 0, and
the last byte 0 only if the var size is a multiple of 8, otherwise number
of valid bytes (1-7).

As for the option, it seems clang uses now
-fsanitize-address-use-after-scope option, while I don't like that much, if
they have already released some version with that option or if they are
unwilling to change, I'd go with their option.

> +         if (flag_stack_reuse != SR_NONE
> +             && flag_openacc
> +             && oacc_declare_returns != NULL)

This actually looks like preexisting OpenACC bug, I doubt the OpenACC
behavior should depend on -fstack-reuse= setting.

+      bool unpoison_var = asan_poisoned_variables.contains (t);
+      if (asan_sanitize_use_after_scope ()
+         && unpoison_var)
+       asan_poisoned_variables.remove (t);

Similarly to asan_handled_variables, I'd prefer it to be a pointer to
hash_set or something similar, so that it costs as few as possible for the
general case (no sanitization).  Similarly, querying the hash_set even for
no use-after-scope sanitization looks wrong.

+         if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())

I would say if asan_sanitize_stack_p () is false, then we should not be
doing use-after-scope sanitization (error if user requested that
explicitly).

Don't remember if I've mentioned it earlier, but for vars that are
TREE_ADDRESSABLE only because of ASAN_MARK calls, we should probably turn
them non-addressable and remove those ASAN_MARK calls, those shouldn't leak.
You can have a look at the r237814 change for how similarly
compare and exchange is special cased for the
addressables discovery (though, the ASAN_MARK case would be easier, just
drop it rather than turn it into something different).

I think I miss some testsuite coverage for what has been discussed, stuff
like:
  switch (x)
    {
      int a;
    case 1:
      int b;
      foo (&a);
      foo (&b);
      /* FALLTHRU */
    case 2:
      int c;
      foo (&a);
      foo (&b);
      foo (&c);
...
    }

On use-after-scope-goto-1.c it seems LLVM does a better job, there the goto
doesn't cross the declaration of any of the a/b/c/d/e/f vars, so doesn't
need unpoisioning of them; and another testcase is:
int main(int argc, char **argv)
{
  if (argc == 0)
  {
    int a;
    int b;
    int c;
    int d;
    int e;
    int f;
    int *ptr;
    int *ptr2;
    int *ptr3;
    int *ptr4;
    int *ptr5;
    int *ptr6;
    label:
      {
        a = 123; b = 123; c = 123; d = 123; e = 123; f = 123;
	ptr = &a;
        *ptr = 1;
	ptr2 = &b;
        *ptr2 = 1;
	ptr3 = &c;
        *ptr3 = 1;
	ptr4 = &d;
        *ptr4 = 1;
	ptr5 = &e;
        *ptr5 = 1;
	ptr6 = &f;
        *ptr6 = 1;
	return 0;
      }
  }
  else
    goto label;

  return 0;
}

I believe the C/C++ FEs have warnings/errors for crossing initialization of
a variable with goto, can't that infrastructure be used also for discovery
of what variables needs unpoisoning when entering a scope through a goto?
Of course for something like setjmp (if we need to do anything about it at
all, maybe longjmp just clears it), or non-local or computed goto we
probably have to be conservative, but for the common case of local goto
can't we figure out what needs to be unpoisoned either in the FEs, or during
gimplification, and emit the unpoisoning not at the label, but on the edge
between the goto and the label?  

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope
  2016-05-12 14:12       ` Martin Liška
  2016-08-12 12:42         ` Martin Liška
  2016-08-18 13:36         ` Jakub Jelinek
@ 2016-09-03 15:23         ` Jakub Jelinek
  2 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-09-03 15:23 UTC (permalink / raw)
  To: Martin Liška, Jason Merrill; +Cc: GCC Patches

On Thu, May 12, 2016 at 04:12:21PM +0200, Martin Liška wrote:
> > Dunno, guess you need to do something in the FE for it already (talk to
> > Jason?).  At least in *.original dump there is already:
> >   <<cleanup_point <<< Unknown tree: expr_stmt
> >   save ((const struct IntHolder &) &TARGET_EXPR <D.2263, {.val=10}>) >>>>>;
> >     int x = (int) saved->val;
> >   return <retval> = x;
> > and the info on where the D.2263 temporary goes out of scope is lost.
> 
> Thanks for sample, I will ask Jason to help me with that.

Actually, I believe this is all available to the gimplifier.
Primarily look at gimplify_target_expr, which if
gimplify_ctxp->in_cleanup_point_expr
emits a D.NNNNN ={v} {CLOBBER};
stmt as cleanup to be added at that corresponding CLEANUP_POINT_EXPR.
And also study gimplify_cleanup_point_expr and gimple_push_cleanup.

I bet you want to emit for use-after-scope sanitization in
gimplify_target_expr next to the conditional which adds the clobber also
(for gimplify_ctxp->in_cleanup_point_expr only) also addition of ASAN_MASK
for the poisoning.  And with the same guard also (again, for if (init) case
only, i.e. the first time the TARGET_EXPR is encountered) before the
gimplification of the init the unpoisoning of the temporary.
Maybe initially ignore VLA temporaries, those would be harder to handle,
and probably have to be dealt together with user VLA/alloca address
sanitization.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-08-18 13:36         ` Jakub Jelinek
@ 2016-10-03  9:27           ` Martin Liška
  2016-10-03  9:30             ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope Martin Liška
                               ` (2 more replies)
  0 siblings, 3 replies; 111+ messages in thread
From: Martin Liška @ 2016-10-03  9:27 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 11483 bytes --]

On 08/18/2016 03:36 PM, Jakub Jelinek wrote:
> On Thu, May 12, 2016 at 04:12:21PM +0200, Martin Liška wrote:
>> --- a/gcc/asan.c
>> +++ b/gcc/asan.c
>> @@ -243,6 +243,11 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
>>  static bool asan_shadow_offset_computed;
>>  static vec<char *> sanitized_sections;
>>  
>> +/* Set of variable declarations that are going to be guarded by
>> +   use-after-scope sanitizer.  */
>> +
>> +static hash_set<tree> asan_handled_variables (13);
> 
> Doesn't this introduce yet another global ctor?  If yes (and we
> unfortunately have already way too many), I'd strongly prefer to avoid that,
> use pointer to hash_set<tree> or something similar.

Hello

It does, I did pointer in second version of that patch.

> 
>> +/* Depending on POISON flag, emit a call to poison (or unpoison) stack memory
>> +   allocated for local variables, localted in OFFSETS.  LENGTH is number
>> +   of OFFSETS, BASE is the register holding the stack base,
>> +   against which OFFSETS array offsets are relative to.  BASE_OFFSET represents
>> +   an offset requested by alignment and similar stuff.  */
>> +
>> +static void
>> +asan_poison_stack_variables (rtx shadow_base, rtx base,
>> +			     HOST_WIDE_INT base_offset,
>> +			     HOST_WIDE_INT *offsets, int length,
>> +			     tree *decls, bool poison)
>> +{
>> +  if (asan_sanitize_use_after_scope ())
>> +    for (int l = length - 2; l > 0; l -= 2)
>> +      {
> 
> I think this is unfortunate, it leads to:
>         movl    $-235802127, 2147450880(%rax)
>         movl    $-185335552, 2147450884(%rax)
>         movl    $-202116109, 2147450888(%rax)
>         movb    $-8, 2147450884(%rax)
>         movb    $-8, 2147450885(%rax)
> (e.g. on use-after-scope-1.c).
> The asan_emit_stack_protection function already walks all the
> entries in the offsets array in both of the
>   for (l = length; l; l -= 2)
> loops, so please handle the initial poisoning and final unpoisoning there
> as well.  The goal is that for variables that you want poison-after-scope
> at the start of the function (btw, I've noticed that current SVN LLVM
> doesn't bother with it and thus doesn't track "use before scope" (before the
> scope is entered for the first time, maybe we shouldn't either, that would
> catch only compiler bugs rather than user code bugs, right?)) have 0xf8
> on all corresponding bytes including the one that would otherwise have 0x01
> through 0x07.  When unpoisoning at the end of the function, again you should
> combine that with unpoisoning of the red zone and partial zone bytes plus
> the last 0x01 through 0x07, etc.

I also decided to not to handle "use before scope" issues and thus I do not poison
stack variables at the very beginning of a function.

As you noticed, the format stack poisoning/unpoisoning code was kind of ugly.
Current unpoisoning code (trunk version) basically clears the whole shadow memory
for a stack frame except local variables that are not touched by use-after-scope machinery.
That eventually leads to a bit easier code, producing the shadow clearing stuff.

> 
> Plus, as I've mentioned before, it would be nice to optimize - for ASAN_MARK
> unpoison appearing strictly before (i.e. dominating) the first (non-shadow) memory read
> or write in the function (explicit or possible through function calls etc.)
> you really don't need to unpoison (depending on whether we follow LLVM as
> mentioned above then it can be removed without anything, or the decl needs
> to be somehow marked and tell asan_emit_stack_protection it shouldn't poison
> it at the start), and for ASAN_MARK poisoning appearing after the last
> load/store in the function (post dominating those, you don't care about
> noreturn though) you can combine that (remove the ASAN_MARK) with letting
> asan_emit_stack_protection know it doesn't need to unpoison.

Fully agree with that approach, however I would be happy to do that as a follow-up as
it's not going to so trivial..

> 
>> +	    char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
>> +	    for (unsigned i = 0; i < shadow_size; ++i)
>> +	      {
>> +		emit_move_insn (var_mem, gen_int_mode (c, QImode));
>> +		var_mem = adjust_address (var_mem, QImode, 1);
> 
> When you combine it with the loop, you can also use the infrastructure to
> handle it 4 bytes at a time.

Current implementation can handle up to 4 bytes at a time. I'm wondering we can
do even better for targets with 64-bits memory stores? How can one get such
info about a target?

> 
> Another thing I've noticed is that the inline expansion of
> __asan_unpoison_stack_memory you emit looks buggy.
> In use-after-scope-1.c I see:
>   _9 = (unsigned long) &my_char;
>   _10 = _9 >> 3;
>   _11 = _10 + 2147450880;
>   _12 = (signed char *) _11;
>   MEM[(short int *)_12] = 0;
> 
> That would be fine only for 16 byte long my_char, but we have instead 9 byte
> one.  So I believe in that case we need to store
> 0x00, 0x01 bytes, for little endian thus 0x0100.  You could use for it
> a function similarly to asan_shadow_cst, just build INTEGER_CST rather than
> CONST_INT out of it.  In general, poisioning is storing 0xf8 to all affected
> shadow bytes, unpoisioning should restore the state what we would emit
> without use-after-scope sanitization, which is all but the last byte 0, and
> the last byte 0 only if the var size is a multiple of 8, otherwise number
> of valid bytes (1-7).

Fixed in the newer patch.

> 
> As for the option, it seems clang uses now
> -fsanitize-address-use-after-scope option, while I don't like that much, if
> they have already released some version with that option or if they are
> unwilling to change, I'd go with their option.

I also do not like the option, but 3.9.0 has already the functionality. Thus,
I'm copying LLVM behavior.

> 
>> +         if (flag_stack_reuse != SR_NONE
>> +             && flag_openacc
>> +             && oacc_declare_returns != NULL)
> 
> This actually looks like preexisting OpenACC bug, I doubt the OpenACC
> behavior should depend on -fstack-reuse= setting.

The generated diff for this hunk is bit misleading, I simplified that
in the second version.

> 
> +      bool unpoison_var = asan_poisoned_variables.contains (t);
> +      if (asan_sanitize_use_after_scope ()
> +         && unpoison_var)
> +       asan_poisoned_variables.remove (t);
> 
> Similarly to asan_handled_variables, I'd prefer it to be a pointer to
> hash_set or something similar, so that it costs as few as possible for the
> general case (no sanitization).  Similarly, querying the hash_set even for
> no use-after-scope sanitization looks wrong.

Sure, fixed.

> 
> +         if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
> 
> I would say if asan_sanitize_stack_p () is false, then we should not be
> doing use-after-scope sanitization (error if user requested that
> explicitly).

Done by adding '&& ASAN_STACK' to asan_sanitize_use_after_scope.

> 
> Don't remember if I've mentioned it earlier, but for vars that are
> TREE_ADDRESSABLE only because of ASAN_MARK calls, we should probably turn
> them non-addressable and remove those ASAN_MARK calls, those shouldn't leak.
> You can have a look at the r237814 change for how similarly
> compare and exchange is special cased for the
> addressables discovery (though, the ASAN_MARK case would be easier, just
> drop it rather than turn it into something different).

I like the approach to not to handle local variables that have address not taken.
My implementation in gimplify.c handles only vars with TREE_ADDRESSABLE set to true,
which should cover all variables which have an address taken in the original code.
I'm aware of cases where one can hit another variable by *(&local_var1 + magic_offset) = 12345,
but that would make the check even more slower.

> 
> I think I miss some testsuite coverage for what has been discussed, stuff
> like:
>   switch (x)
>     {	
>       int a;
>     case 1:
>       int b;
>       foo (&a);
>       foo (&b);
>       /* FALLTHRU */
>     case 2:
>       int c;
>       foo (&a);
>       foo (&b);
>       foo (&c);
> ...
>     }

One can't write such code because of:

use-after-scope-switch.c: In function ‘main’:
use-after-scope-switch.c:16:7: error: a label can only be part of a statement and a declaration is not a statement
       int b;
       ^~~
use-after-scope-switch.c:21:7: error: a label can only be part of a statement and a declaration is not a statement
       int c;
       ^~~

> 
> On use-after-scope-goto-1.c it seems LLVM does a better job, there the goto
> doesn't cross the declaration of any of the a/b/c/d/e/f vars, so doesn't
> need unpoisioning of them; and another testcase is:
> int main(int argc, char **argv)
> {
>   if (argc == 0)
>   {
>     int a;
>     int b;
>     int c;
>     int d;
>     int e;
>     int f;
>     int *ptr;
>     int *ptr2;
>     int *ptr3;
>     int *ptr4;
>     int *ptr5;
>     int *ptr6;
>     label:
>       {
>         a = 123; b = 123; c = 123; d = 123; e = 123; f = 123;
> 	ptr = &a;
>         *ptr = 1;
> 	ptr2 = &b;
>         *ptr2 = 1;
> 	ptr3 = &c;
>         *ptr3 = 1;
> 	ptr4 = &d;
>         *ptr4 = 1;
> 	ptr5 = &e;
>         *ptr5 = 1;
> 	ptr6 = &f;
>         *ptr6 = 1;
> 	return 0;
>       }
>   }
>   else
>     goto label;
> 
>   return 0;
> }
> 
> I believe the C/C++ FEs have warnings/errors for crossing initialization of
> a variable with goto, can't that infrastructure be used also for discovery
> of what variables needs unpoisoning when entering a scope through a goto?
> Of course for something like setjmp (if we need to do anything about it at
> all, maybe longjmp just clears it), or non-local or computed goto we
> probably have to be conservative, but for the common case of local goto
> can't we figure out what needs to be unpoisoned either in the FEs, or during
> gimplification, and emit the unpoisoning not at the label, but on the edge
> between the goto and the label?  

I know that this is still a challenging area. As I've read C/C++ FE, we really have
data structures that are used to identify cross initialization in gotos like:

-Wjump-misses-init
cross.c: In function ‘main’:
cross.c:46:5: warning: jump skips variable initialization [-Wjump-misses-init]
     goto label;
     ^~~~
cross.c:25:7: note: label ‘label’ defined here
       label:
       ^~~~~
cross.c:13:16: note: ‘a’ declared here
       struct A a = { 123 };

However, it's connected to scopes and I'm not sure whether this data structures
are up to date during gimplification and whether on can find a mapping between
LABEL_DECLs and these scopes.

As an initial implementation, I decided to register all LABEL_DECLs that are either
used in goto, or have address taken. For those, I defensively generate unpoisoning
code. Hope it's reasonable initial implementation?

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests. Apart from that,
following build also succeeded:
./configure --with-build-config="bootstrap-asan" --disable-multilib --disable-werror

Here are some numbers showing how slower is sanitization compared to former -fsanitize=address:
postgres (make check): 26.2 vs. 28.5s
cray (800x600): 9.1s vs. 9.5s
tramp3d: 5x times slower :/ (well, the problem here is that it uses many functions passing args by reference
(all these have address taken); and it does in general very many function calls (stack {un}poisoning cost)).

Martin

> 
> 	Jakub
> 


[-- Attachment #2: 0002-Introduce-fsanitize-address-use-after-scope-v2.patch --]
[-- Type: text/x-patch, Size: 45698 bytes --]

From 60e11b3eb6bba54312e28459d13aeeab56c43c5a Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 2/3] Introduce -fsanitize-address-use-after-scope

gcc/c-family/ChangeLog:

2016-09-27  Martin Liska  <mliska@suse.cz>

	* c-common.c (warn_for_unused_label): Save all labels used
	in goto or in &label;
	* c-ubsan.c: Include params.h before asan.h is used.

gcc/cp/ChangeLog:

2016-09-27  Martin Liska  <mliska@suse.cz>

	* decl2.c: Include params.h before asan.h is used.

gcc/ChangeLog:

2016-09-27  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Move the enum to header file.
	(asan_init_shadow_ptr_types): Make type creation more generic.
	(shadow_mem_size): New function.
	(asan_emit_stack_protection): Use newly added ASAN_SHADOW_GRANULARITY.
	Rewritten stack unpoisoning code.
	(build_shadow_mem_access): Add new argument return_address.
	(instrument_derefs): Instrument local variables if use after scope
	sanitization is enabled.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	* asan.h (enum asan_mark_flags): Moved here from asan.c
	(asan_protect_stack_decl): Protect all declaration that need
	to live in memory.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* builtins.c: Include params.h before asan.h is used.
	* cfgexpand.c (asan_sanitize_stack_p): Use asan_no_sanitize_address_p.
	(partition_stack_vars): Consider asan_sanitize_use_after_scope in
	condition.
	(expand_stack_vars): Likewise.
	(defer_stack_allocation): Likewise.
	(expand_used_vars): Likewise.
	* common.opt (-fsanitize-address-use-after-scope): New option.
	* doc/invoke.texi (use-after-scope-direct-emission-threshold):
	Explain the parameter.
	* flag-types.h (enum sanitize_code): Define SANITIZE_USE_AFTER_SCOPE.
	* gimplify.c (build_asan_poison_call_expr): New function.
	(asan_poison_variable): Likewise.
	(gimplify_bind_expr): Generate poisoning/unpoisoning for local
	variables that have address taken.
	(gimplify_decl_expr): Likewise.
	(gimplify_target_expr): Likewise for C++ temporaries.
	(sort_by_decl_uid): New function.
	(gimplify_expr): Unpoison all variables for a label we can jump
	from outside of a scope.
	(gimplify_function_tree): Clear asan_poisoned_variables.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def (ASAN_MARK): Declare.
	* opts-global.c: Include params.h before asan.h is used.
	* opts.c (finish_options): Handle -fstack-reuse if
	-fsanitize-address-use-after-scope is enabled.
	(common_handle_option): Enable address sanitization if
	-fsanitize-address-use-after-scope is enabled.
	* params.def (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD):
	New parameter.
	* params.h: Likewise.
	* sancov.c (pass_sanopt::execute): Handle IFN_ASAN_MARK.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory functions.
	* tree-streamer-in.c: Include params.h before asan.h is used.
	* tsan.c: Likewise.
	* ubsan.c: Likewise.
	* varasm.c: Likewise.
---
 gcc/asan.c              | 276 +++++++++++++++++++++++++++++++++++++++---------
 gcc/asan.h              |  54 ++++++++--
 gcc/builtins.c          |   1 +
 gcc/c-family/c-common.c |   9 ++
 gcc/c-family/c-ubsan.c  |   1 +
 gcc/cfgexpand.c         |  15 +--
 gcc/common.opt          |   3 +
 gcc/cp/decl2.c          |   1 +
 gcc/doc/invoke.texi     |  15 ++-
 gcc/flag-types.h        |   5 +-
 gcc/gimplify.c          | 201 ++++++++++++++++++++++++++++++++---
 gcc/internal-fn.c       |   9 ++
 gcc/internal-fn.def     |   1 +
 gcc/opts-global.c       |   1 +
 gcc/opts.c              |  30 +++++-
 gcc/params.def          |   6 ++
 gcc/params.h            |   2 +
 gcc/sancov.c            |   1 +
 gcc/sanitizer.def       |   4 +
 gcc/sanopt.c            |   5 +-
 gcc/tree-streamer-in.c  |   1 +
 gcc/tsan.c              |   1 +
 gcc/ubsan.c             |   1 +
 gcc/varasm.c            |   1 +
 24 files changed, 558 insertions(+), 86 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 4fe2447..4563c37 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "stor-layout.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "dojump.h"
 #include "explow.h"
@@ -244,6 +245,13 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> *asan_handled_variables = NULL;
+
+hash_set <tree> *asan_used_labels = NULL;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -314,22 +322,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -932,12 +931,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1021,6 +1024,15 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+shadow_mem_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1046,7 +1058,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
-  HOST_WIDE_INT last_offset, last_size;
+  HOST_WIDE_INT last_offset;
   int l;
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
@@ -1204,10 +1216,10 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 				       (aoff - prev_offset)
 				       >> ASAN_SHADOW_SHIFT);
 	  prev_offset = aoff;
-	  for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT))
+	  for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
 	    if (aoff < offset)
 	      {
-		if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1)
+		if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
 		  shadow_bytes[i] = 0;
 		else
 		  shadow_bytes[i] = offset - aoff;
@@ -1281,35 +1293,62 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
 
-  prev_offset = base_offset;
+  /* Unpoison shadow memory of a stack at the very end of a function.
+     As we're poisoning stack variables at the end of their scope,
+     shadow memory must be properly unpoisoned here.  The easiest approach
+     would be to collect all variables that should not be unpoisoned and
+     we unpoison shadow memory of the whole stack except ranges
+     occupied by these variables.  */
   last_offset = base_offset;
-  last_size = 0;
-  for (l = length; l; l -= 2)
+  HOST_WIDE_INT current_offset = last_offset;
+  if (length)
     {
-      offset = base_offset + ((offsets[l - 1] - base_offset)
-			     & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-      if (last_offset + last_size != offset)
+      HOST_WIDE_INT var_end_offset = 0;
+      HOST_WIDE_INT stack_start = offsets[length - 1];
+      gcc_assert (last_offset == stack_start);
+
+      for (int l = length - 2; l > 0; l -= 2)
 	{
-	  shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				       (last_offset - prev_offset)
-				       >> ASAN_SHADOW_SHIFT);
-	  prev_offset = last_offset;
-	  asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
-	  last_offset = offset;
-	  last_size = 0;
+	  HOST_WIDE_INT var_offset = offsets[l];
+	  current_offset = var_offset;
+	  var_end_offset = offsets[l - 1];
+	  HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset,
+					     BITS_PER_UNIT);
+
+	  /* Should we unpoison the variable?  */
+	  if (asan_handled_variables != NULL
+	      && asan_handled_variables->contains (decl))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		fprintf (dump_file, "Skipping stack emission for variable: %s "
+			 "(%ldB)\n",
+			 IDENTIFIER_POINTER (DECL_NAME (decl)),
+			 var_end_offset - var_offset);
+
+	      unsigned HOST_WIDE_INT s
+		= shadow_mem_size (current_offset - last_offset);
+	      asan_clear_shadow (shadow_mem, s);
+	      HOST_WIDE_INT shift
+		= shadow_mem_size (current_offset - last_offset + rounded_size);
+	      shadow_mem = adjust_address (shadow_mem, VOIDmode, shift);
+	      last_offset = var_offset + rounded_size;
+	      current_offset = last_offset;
+	    }
+
 	}
-      last_size += base_offset + ((offsets[l - 2] - base_offset)
-				  & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
-		   - offset;
-    }
-  if (last_size)
-    {
-      shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				   (last_offset - prev_offset)
-				   >> ASAN_SHADOW_SHIFT);
-      asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
+
+      /* Handle last redzone.  */
+      current_offset = offsets[0];
+      asan_clear_shadow (shadow_mem,
+			 shadow_mem_size (current_offset - last_offset));
     }
 
+  /* Clean-up set with instrumented stack variables.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1589,12 +1628,14 @@ insert_if_then_before_iter (gcond *cond,
   gsi_insert_after (&cond_insert_point, cond, GSI_NEW_STMT);
 }
 
-/* Build
-   (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
+/* Build (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().
+   If RETURN_ADDRESS is set to true, return memory location instread
+   of a value in the shadow memory.  */
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_address = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1617,11 +1658,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_address)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1825,7 +1870,9 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && (!asan_sanitize_use_after_scope ()
+		  || !TREE_ADDRESSABLE (inner)))
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2575,6 +2622,131 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Poison or unpoison (depending on IS_CLOBBER variable) shadow memory based
+   on SHADOW address.  Newly added statements will be added to ITER with
+   given location LOC.  We mark SIZE bytes in shadow memory, where
+   LAST_CHUNK_SIZE is greater than zero in situation where we are at the
+   end of a variable.  */
+
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 bool is_clobber, unsigned size,
+			 unsigned last_chunk_size)
+{
+  tree shadow_ptr_type;
+
+  switch (size)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < size; ++i)
+    {
+      unsigned char shadow_c = c;
+      if (i == size- 1 && last_chunk_size && !is_clobber)
+	shadow_c = last_chunk_size;
+      val |= (unsigned HOST_WIDE_INT) shadow_c << (BITS_PER_UNIT * i);
+    }
+
+  /* Handle last chunk in unpoisoning.  */
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (asan_handled_variables == NULL)
+    asan_handled_variables = new hash_set<tree> (16);
+  asan_handled_variables->add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+				  NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  /* Generate direct emission if size_in_bytes is small.  */
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes);
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  unsigned HOST_WIDE_INT last_chunk_size = 0;
+	  unsigned HOST_WIDE_INT s = (offset + size) * ASAN_SHADOW_GRANULARITY;
+	  if (s > size_in_bytes)
+	    last_chunk_size = ASAN_SHADOW_GRANULARITY - (s - size_in_bytes);
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, is_clobber,
+				   size, last_chunk_size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..11e9391 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,9 +37,14 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of labels that are either used in a goto, or their address
+   has been taken.  */
+extern hash_set <tree> *asan_used_labels;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
+#define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT)
 
 /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
@@ -50,21 +56,39 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
 asan_protect_stack_decl (tree decl)
 {
-  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
+  return DECL_P (decl) && TREE_ADDRESSABLE (decl);
 }
 
 /* Return the size of padding needed to insert after a protected
@@ -105,4 +129,22 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
+	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
+	  && ASAN_STACK);
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 35cb109..78f8ab4 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -60,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "value-prof.h"
 #include "builtins.h"
+#include "params.h"
 #include "asan.h"
 #include "cilk.h"
 #include "tree-chkp.h"
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 2652259..47f361b 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -48,6 +48,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "substring-locations.h"
 #include "spellcheck.h"
+#include "params.h"
+#include "asan.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
 
@@ -12026,6 +12028,13 @@ warn_for_unused_label (tree label)
       else
         warning (OPT_Wunused_label, "label %q+D declared but not defined", label);
     }
+  else if (asan_sanitize_use_after_scope ())
+    {
+      if (asan_used_labels == NULL)
+	asan_used_labels = new hash_set<tree> (16);
+
+      asan_used_labels->add (label);
+    }
 }
 
 /* Warn for division by zero according to the value of DIVISOR.  LOC
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index 4022bdf..1a49e58 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-common.h"
 #include "ubsan.h"
 #include "c-family/c-ubsan.h"
+#include "params.h"
 #include "asan.h"
 #include "stor-layout.h"
 #include "builtins.h"
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 4190f7f..1b2e090 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -875,8 +875,7 @@ asan_sanitize_stack_p (void)
 {
   return ((flag_sanitize & SANITIZE_ADDRESS)
 	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
+	  && !asan_no_sanitize_address_p ());
 }
 
 /* A subroutine of expand_used_vars.  Binpack the variables into
@@ -940,7 +939,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1127,7 +1127,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
 	  base = virtual_stack_vars_rtx;
-	  if (asan_sanitize_stack_p () && pred)
+	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
+	      && pred)
 	    {
 	      HOST_WIDE_INT prev_offset
 		= align_base (frame_offset,
@@ -1514,7 +1515,9 @@ defer_stack_allocation (tree var, bool toplevel)
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
      Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ()
+      || asan_sanitize_use_after_scope ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
@@ -2212,7 +2215,7 @@ expand_used_vars (void)
 	    expand_stack_vars (stack_protect_decl_phase_2, &data);
 	}
 
-      if (asan_sanitize_stack_p ())
+      if (asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
 	/* Phase 3, any partitions that need asan protection
 	   in addition to phase 1 and 2.  */
 	expand_stack_vars (asan_decl_phase_3, &data);
diff --git a/gcc/common.opt b/gcc/common.opt
index 0e01577..1cdb1eb 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -964,6 +964,9 @@ fsanitize-recover
 Common Report
 This switch is deprecated; use -fsanitize-recover= instead.
 
+fsanitize-address-use-after-scope
+Common Driver Report Var(flag_sanitize_address_use_after_scope) Init(0)
+
 fsanitize-undefined-trap-on-error
 Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
 Use trap instead of a library function for undefined behavior sanitization.
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 4bdac94a..07bab4c 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "intl.h"
 #include "c-family/c-ada-spec.h"
+#include "params.h"
 #include "asan.h"
 
 extern cpp_reader *parse_in;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8a84e4f..46a3074 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10040,6 +10040,10 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item use-after-scope-direct-emission-threshold
+If size of a local variables in bytes is smaller of equal to this number,
+direct instruction emission is utilized to poison and unpoison local variables.
+
 @item chkp-max-ctor-size
 Static constructors generated by Pointer Bounds Checker may become very
 large and significantly increase compile time at optimization level
@@ -10250,6 +10254,7 @@ thread-safe code.
 Enable AddressSanitizer, a fast memory error detector.
 Memory access instructions are instrumented to detect
 out-of-bounds and use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/sanitizers/wiki/AddressSanitizer} for
 more details.  The run-time behavior can be influenced using the
 @env{ASAN_OPTIONS} environment variable.  When set to @code{help=1},
@@ -10261,6 +10266,7 @@ The option can't be combined with @option{-fsanitize=thread}.
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
 @item -fsanitize=thread
@@ -10460,8 +10466,8 @@ except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
 @option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
 @option{-fsanitize=bounds-strict},
 @option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
-For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
-for which this feature is experimental.
+For these sanitizers error recovery is turned on by default,
+except @option{-fsanitize=address}, for which this feature is experimental.
 @option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
 accepted, the former enables recovery for all sanitizers that support it,
 the latter disables recovery for all sanitizers that support it.
@@ -10483,6 +10489,11 @@ Similarly @option{-fno-sanitize-recover} is equivalent to
 -fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
 @end smallexample
 
+@item -fsanitize-address-use-after-scope
+@opindex fsanitize-address-use-after-scope
+Enable sanitization of local variables to detect use-after-scope bugs.
+The option sets @option{-fstack-reuse} to @samp{none}.
+
 @item -fsanitize-undefined-trap-on-error
 @opindex fsanitize-undefined-trap-on-error
 The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 816df6b..6f1bc1e 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -229,6 +229,7 @@ enum sanitize_code {
   SANITIZE_OBJECT_SIZE = 1UL << 20,
   SANITIZE_VPTR = 1UL << 21,
   SANITIZE_BOUNDS_STRICT = 1UL << 22,
+  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -237,7 +238,9 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT,
+  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
+			| SANITIZE_USE_AFTER_SCOPE
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 66bb8be..3eb1602 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "params.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> *asan_poisoned_variables = NULL;
 
 enum gimplify_omp_var_data
 {
@@ -1088,6 +1093,79 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */
+
+static tree
+build_asan_poison_call_expr (tree decl)
+{
+  /* Do not poison variables that have size equal to zero.  */
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  if (zerop (unit_size))
+    return NULL_TREE;
+
+  tree base = build_fold_addr_expr (decl);
+
+  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
+				       void_type_node, 3,
+				       build_int_cst (integer_type_node,
+						      ASAN_MARK_CLOBBER),
+				       base, unit_size);
+}
+
+/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
+   on POISON flag, shadow memory of a DECL variable.  The call will be
+   put on location identified by IT iterator, where BEFORE flag drives
+   position where the stmt will be put.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
+		      bool before)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  /* Do not poison variables that have size equal to zero.  */
+  if (zerop (unit_size))
+    return;
+
+  /* It's necessary to have all stack variables aligned to ASAN granularity
+     bytes.  */
+  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+
+  if (before)
+    gsi_insert_before (it, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (it, g, GSI_NEW_STMT);
+}
+
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  gimple_stmt_iterator it = gsi_last (*seq_p);
+  bool before = false;
+
+  if (gsi_end_p (it))
+    before = true;
+
+  asan_poison_variable (decl, poison, &it, before);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1228,6 +1306,14 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 		}
 	    }
 	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && asan_poisoned_variables != NULL
+	  && asan_poisoned_variables->contains (t))
+	{
+	  asan_poisoned_variables->remove (t);
+	  asan_poison_variable (t, true, &cleanup);
+	}
     }
 
   if (ret_clauses)
@@ -1472,13 +1558,27 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
   if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
     {
       tree init = DECL_INITIAL (decl);
+      bool is_vla = false;
 
       if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
 	  || (!TREE_STATIC (decl)
 	      && flag_stack_check == GENERIC_STACK_CHECK
 	      && compare_tree_int (DECL_SIZE_UNIT (decl),
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
-	gimplify_vla_decl (decl, seq_p);
+	{
+	  gimplify_vla_decl (decl, seq_p);
+	  is_vla = true;
+	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && !is_vla
+	  && TREE_ADDRESSABLE (decl)
+	  && !TREE_STATIC (decl))
+	{
+	  asan_poisoned_variables->add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
 
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
@@ -6158,6 +6258,9 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   tree init = TARGET_EXPR_INITIAL (targ);
   enum gimplify_status ret;
 
+  bool unpoison_empty_seq = false;
+  gimple_stmt_iterator unpoison_it;
+
   if (init)
     {
       tree cleanup = NULL_TREE;
@@ -6171,7 +6274,14 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  gimplify_vla_decl (temp, pre_p);
 	}
       else
-	gimple_add_tmp_var (temp);
+	{
+	  /* Save location where we need to place unpoisoning.  It's possible
+	     that a variable will be converted to needs_to_live_in_memory.  */
+	  unpoison_it = gsi_last (*pre_p);
+	  unpoison_empty_seq = gsi_end_p (unpoison_it);
+
+	  gimple_add_tmp_var (temp);
+	}
 
       /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
 	 expression is supposed to initialize the slot.  */
@@ -6207,20 +6317,34 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       /* Add a clobber for the temporary going out of scope, like
 	 gimplify_bind_expr.  */
       if (gimplify_ctxp->in_cleanup_point_expr
-	  && needs_to_live_in_memory (temp)
-	  && flag_stack_reuse == SR_ALL)
-	{
-	  tree clobber = build_constructor (TREE_TYPE (temp),
-					    NULL);
-	  TREE_THIS_VOLATILE (clobber) = true;
-	  clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
-	  if (cleanup)
-	    cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
-			      clobber);
-	  else
-	    cleanup = clobber;
-	}
+	  && needs_to_live_in_memory (temp))
+	{
+	  if (flag_stack_reuse == SR_ALL)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (temp),
+						NULL);
+	      TREE_THIS_VOLATILE (clobber) = true;
+	      clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+	      if (cleanup)
+		cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+				  clobber);
+	      else
+		cleanup = clobber;
+	    }
+	  if (asan_sanitize_use_after_scope ())
+	    {
+	      tree asan_cleanup = build_asan_poison_call_expr (temp);
+	      if (asan_cleanup)
+		{
+		  if (unpoison_empty_seq)
+		    unpoison_it = gsi_start (*pre_p);
 
+		  asan_poison_variable (temp, false, &unpoison_it,
+					unpoison_empty_seq);
+		  gimple_push_cleanup (temp, asan_cleanup, false, pre_p);
+		}
+	    }
+	}
       if (cleanup)
 	gimple_push_cleanup (temp, cleanup, false, pre_p);
 
@@ -10720,6 +10844,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10811,6 +10954,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   location_t saved_location;
   enum gimplify_status ret;
   gimple_stmt_iterator pre_last_gsi, post_last_gsi;
+  tree label;
 
   save_expr = *expr_p;
   if (save_expr == NULL_TREE)
@@ -11226,6 +11370,30 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	case LABEL_EXPR:
 	  ret = gimplify_label_expr (expr_p, pre_p);
+	  label = LABEL_EXPR_LABEL (*expr_p);
+	  gcc_assert (decl_function_context (label) == current_function_decl);
+
+	  /* If the label is used in a goto statement, or address of the label
+	     is taken, we need to unpoison all variables that were seen so far.
+	     Doing so would prevent us from reporting a false positives.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && !asan_no_sanitize_address_p ()
+	      && asan_used_labels != NULL
+	      && asan_used_labels->contains (label))
+	    {
+	      unsigned c = asan_poisoned_variables->elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set<tree>::iterator it
+		   = asan_poisoned_variables->begin ();
+		   it != asan_poisoned_variables->end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -12323,7 +12491,10 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
+  delete asan_poisoned_variables;
+  asan_poisoned_variables = NULL;
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 029a534..69b9a8e 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -236,6 +236,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 d4fbdb2..09f065f 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts-global.c b/gcc/opts-global.c
index b7e5232..963b542 100644
--- a/gcc/opts-global.c
+++ b/gcc/opts-global.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "plugin.h"
 #include "toplev.h"
 #include "context.h"
+#include "params.h"
 #include "asan.h"
 
 typedef const char *const_char_p; /* For DEF_VEC_P.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 45f1f89..9e017c2 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -978,6 +978,19 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
+     is enabled.  */
+  if (opts->x_flag_sanitize & SANITIZE_USE_AFTER_SCOPE)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize-address-use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1452,9 +1465,10 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
-  SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
-  SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
-		 true),
+  SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+			   | SANITIZE_USE_AFTER_SCOPE), true),
+  SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS
+				  | SANITIZE_USE_AFTER_SCOPE), true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
   SANITIZER_OPT (leak, SANITIZE_LEAK, false),
   SANITIZER_OPT (shift, SANITIZE_SHIFT, true),
@@ -1781,6 +1795,16 @@ common_handle_option (struct gcc_options *opts,
       /* Deferred.  */
       break;
 
+    case OPT_fsanitize_address_use_after_scope:
+	{
+	  if (value)
+	    opts->x_flag_sanitize |= SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS
+	      | SANITIZE_USE_AFTER_SCOPE;
+	  else
+	    opts->x_flag_sanitize &= ~SANITIZE_USE_AFTER_SCOPE;
+	  break;
+	}
+
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
diff --git a/gcc/params.def b/gcc/params.def
index 79b7dd4..f3c5c5c 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1156,6 +1156,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+	 256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 97c8d56..0a2905c 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -244,5 +244,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sancov.c b/gcc/sancov.c
index f3211dd..655ff64 100644
--- a/gcc/sancov.c
+++ b/gcc/sancov.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "tree-pass.h"
 #include "tree-iterator.h"
+#include "params.h"
 #include "asan.h"
 
 namespace {
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 27c43da..17b4189 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -29,9 +29,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-pretty-print.h"
 #include "fold-const.h"
 #include "gimple-iterator.h"
+#include "params.h"
 #include "asan.h"
 #include "ubsan.h"
-#include "params.h"
 #include "tree-hash-traits.h"
 #include "gimple-ssa.h"
 #include "tree-phinodes.h"
@@ -728,6 +728,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 671ce31..18a8c96 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "ipa-chkp.h"
 #include "gomp-constants.h"
+#include "params.h"
 #include "asan.h"
 
 
diff --git a/gcc/tsan.c b/gcc/tsan.c
index cc19474..7cafbf5 100644
--- a/gcc/tsan.c
+++ b/gcc/tsan.c
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-propagate.h"
 #include "tree-ssa-loop-ivopts.h"
 #include "tsan.h"
+#include "params.h"
 #include "asan.h"
 #include "builtins.h"
 #include "target.h"
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index d3bd8e3..c16d503 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "ubsan.h"
 #include "expr.h"
+#include "params.h"
 #include "asan.h"
 #include "gimplify-me.h"
 #include "dfp.h"
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 93aba78..d21557c 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "debug.h"
 #include "common/common-target.h"
+#include "params.h"
 #include "asan.h"
 #include "rtl-iter.h"
 
-- 
2.9.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope
  2016-10-03  9:27           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
@ 2016-10-03  9:30             ` Martin Liška
  2016-11-07 10:04               ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope (v3) Martin Liška
  2016-10-03  9:39             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Jakub Jelinek
  2016-10-07 11:13             ` Jakub Jelinek
  2 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-10-03  9:30 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 91 bytes --]

Following patch adjusts expected test dumps and also introduces various
new tests.

Martin

[-- Attachment #2: 0003-Introduce-tests-for-fsanitize-address-use-after-scop.patch --]
[-- Type: text/x-patch, Size: 18360 bytes --]

From 4ddafab1e533a1d3580d2f883955d61fe23aa353 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 19 Sep 2016 17:39:29 +0200
Subject: [PATCH 3/3] Introduce tests for -fsanitize-address-use-after-scope

gcc/testsuite/ChangeLog:

2016-09-26  Martin Liska  <mliska@suse.cz>

	* c-c++-common/asan/force-inline-opt0-1.c: Disable
	-f-sanitize-address-use-after-scope.
	* c-c++-common/asan/inc.c: Change number of expected ASAN_CHECK
	internal fn calls.
	* g++.dg/asan/use-after-scope-1.C: New test.
	* g++.dg/asan/use-after-scope-2.C: Likewise.
	* g++.dg/asan/use-after-scope-3.C: Likewise.
	* g++.dg/asan/use-after-scope-types-1.C: Likewise.
	* g++.dg/asan/use-after-scope-types-2.C: Likewise.
	* g++.dg/asan/use-after-scope-types-3.C: Likewise.
	* g++.dg/asan/use-after-scope-types-4.C: Likewise.
	* g++.dg/asan/use-after-scope-types-5.C: Likewise.
	* g++.dg/asan/use-after-scope-types.h: Likewise.
	* gcc.dg/asan/use-after-scope-1.c: Likewise.
	* gcc.dg/asan/use-after-scope-2.c: Likewise.
	* gcc.dg/asan/use-after-scope-3.c: Likewise.
	* gcc.dg/asan/use-after-scope-4.c: Likewise.
	* gcc.dg/asan/use-after-scope-5.c: Likewise.
	* gcc.dg/asan/use-after-scope-6.c: Likewise.
	* gcc.dg/asan/use-after-scope-7.c: Likewise.
	* gcc.dg/asan/use-after-scope-8.c: Likewise.
	* gcc.dg/asan/use-after-scope-goto-1.c: Likewise.
	* gcc.dg/asan/use-after-scope-goto-2.c: Likewise.
---
 .../c-c++-common/asan/force-inline-opt0-1.c        |  1 +
 gcc/testsuite/c-c++-common/asan/inc.c              |  3 +-
 gcc/testsuite/g++.dg/asan/use-after-scope-1.C      | 21 ++++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-2.C      | 40 ++++++++++++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-3.C      | 22 ++++++++++
 .../g++.dg/asan/use-after-scope-types-1.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-2.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-3.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-4.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-5.C          | 17 ++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-types.h  | 30 ++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c      | 18 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c      | 47 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c      | 20 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c      | 16 ++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c      | 27 +++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-6.c      | 15 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-7.c      | 15 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c      | 14 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c | 47 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c | 25 ++++++++++++
 21 files changed, 445 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-1.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-2.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-3.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types.h
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c

diff --git a/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c b/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
index 0576155..2e156f7 100644
--- a/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
+++ b/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
@@ -2,6 +2,7 @@
    (before and after inlining) */
 
 /* { dg-do compile } */
+/* { dg-options "-fno-sanitize-address-use-after-scope" } */
 /* { dg-final { scan-assembler-not "__asan_report_load" } } */
 
 __attribute__((always_inline))
diff --git a/gcc/testsuite/c-c++-common/asan/inc.c b/gcc/testsuite/c-c++-common/asan/inc.c
index 5abf373..98121d2 100644
--- a/gcc/testsuite/c-c++-common/asan/inc.c
+++ b/gcc/testsuite/c-c++-common/asan/inc.c
@@ -16,5 +16,6 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "ASAN_" 1 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_" 4 "asan0" } }  */
 /* { dg-final { scan-tree-dump "ASAN_CHECK \\(.*, 4\\);" "asan0" } }  */
+/* { dg-final { scan-tree-dump "ASAN_CHECK \\(.*, 8\\);" "asan0" } }  */
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
new file mode 100644
index 0000000..fd875ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
@@ -0,0 +1,21 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include <functional>
+
+int main() {
+  std::function<int()> function;
+  {
+    int v = 0;
+    function = [&v]()
+    {
+      return v;
+    };
+  }
+  return function();
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'v' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
new file mode 100644
index 0000000..92a4bd1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
@@ -0,0 +1,40 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include <stdio.h>
+
+struct Test
+{
+  Test ()
+    {
+      my_value = 0;
+    }
+
+  ~Test ()
+    {
+      fprintf (stderr, "Value: %d\n", *my_value);
+    }
+
+  void init (int *v)
+    {
+      my_value = v;
+    }
+
+  int *my_value;
+};
+
+int main(int argc, char **argv)
+{
+  Test t;
+
+  {
+    int x = argc;
+    t.init(&x);
+  }
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-3.C b/gcc/testsuite/g++.dg/asan/use-after-scope-3.C
new file mode 100644
index 0000000..172f374
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-3.C
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+struct IntHolder {
+  int val;
+};
+
+const IntHolder *saved;
+
+void save(const IntHolder &holder) {
+  saved = &holder;
+}
+
+int main(int argc, char *argv[]) {
+  save({10});
+  int x = saved->val;  // BOOM
+  return x;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'<unknown>' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
new file mode 100644
index 0000000..bedcfa4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<bool>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
new file mode 100644
index 0000000..75a01d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<float>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
new file mode 100644
index 0000000..3350c69
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<void *>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
new file mode 100644
index 0000000..dd06e94
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<std::vector<std::string>>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 8 at" }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
new file mode 100644
index 0000000..42abc2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<char[1000]>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types.h b/gcc/testsuite/g++.dg/asan/use-after-scope-types.h
new file mode 100644
index 0000000..b96b02b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types.h
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+template <class T> struct Ptr {
+  void Store(T *ptr) { t = ptr; }
+
+  void Access() { *t = {}; }
+
+  T *t;
+};
+
+template <class T, size_t N> struct Ptr<T[N]> {
+  using Type = T[N];
+  void Store(Type *ptr) { t = *ptr; }
+
+  void Access() { *t = {}; }
+
+  T *t;
+};
+
+template <class T> __attribute__((noinline)) void test() {
+  Ptr<T> ptr;
+  {
+    T x;
+    ptr.Store(&x);
+  }
+
+  ptr.Access();
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
new file mode 100644
index 0000000..bdbc97b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
@@ -0,0 +1,18 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  return *(ptr+8);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
new file mode 100644
index 0000000..dedb734
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int *bar (int *x, int *y) { return y; }
+
+int foo (void)
+{
+  char *p;
+  {
+    char a = 0;
+    p = &a;
+  }
+
+  if (*p)
+    return 1;
+  else
+    return 0;
+}
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  int a[16];
+  int *p, *q = a;
+  {
+    int b[16];
+    p = bar (a, b);
+  }
+  bar (a, q);
+  {
+    int c[16];
+    q = bar (a, c);
+  }
+  int v = *bar (a, q);
+  return v;
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'c' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
new file mode 100644
index 0000000..9aeed51
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
@@ -0,0 +1,20 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
new file mode 100644
index 0000000..77d7052
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -0,0 +1,16 @@
+// { dg-do run }
+
+int
+__attribute__((no_sanitize_address))
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
new file mode 100644
index 0000000..b53712d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
@@ -0,0 +1,27 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int *ptr;
+
+__attribute__((always_inline))
+inline static void
+foo(int v)
+{
+  int values[10];
+  for (unsigned i = 0; i < 10; i++)
+    values[i] = v;
+
+  ptr = &values[3];
+}
+
+int
+main (int argc, char **argv)
+{
+  foo (argc);
+
+  return *ptr;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'values' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
new file mode 100644
index 0000000..bb13cec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-additional-options "--param asan-stack=0" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  *ptr = 'c';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
new file mode 100644
index 0000000..4115205
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-additional-options "-fno-sanitize-address-use-after-scope" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  *ptr = 'c';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
new file mode 100644
index 0000000..b204206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int
+fn1 ()
+{
+  int x = 123;
+  register int a asm("rdi") = 123;
+
+  return x * x;
+}
+
+/* { dg-final { scan-tree-dump-not "ASAN_CHECK" "asan0" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
new file mode 100644
index 0000000..c47a5e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+  int b = 123;
+  int c = 123;
+  int d = 123;
+  int e = 123;
+  int f = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    int *ptr2;
+    int *ptr3;
+    int *ptr4;
+    int *ptr5;
+    int *ptr6;
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	ptr2 = &b;
+        *ptr2 = 1;
+	ptr3 = &c;
+        *ptr3 = 1;
+	ptr4 = &d;
+        *ptr4 = 1;
+	ptr5 = &e;
+        *ptr5 = 1;
+	ptr6 = &f;
+        *ptr6 = 1;
+	return 0;
+      }
+  }
+  else
+    goto label;
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &c, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &e, 4\\);" 2 "asan0" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c
new file mode 100644
index 0000000..73ef4e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    /* The label is not used in &label or goto label.  Thus '&a' should be
+       marked just once.  */
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	return 0;
+      }
+  }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, 4\\);" 1 "asan0" } }  */
-- 
2.9.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-03  9:27           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  2016-10-03  9:30             ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope Martin Liška
@ 2016-10-03  9:39             ` Jakub Jelinek
  2016-10-07 11:13             ` Jakub Jelinek
  2 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-10-03  9:39 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Mon, Oct 03, 2016 at 11:27:38AM +0200, Martin Liška wrote:
> > Plus, as I've mentioned before, it would be nice to optimize - for ASAN_MARK
> > unpoison appearing strictly before (i.e. dominating) the first (non-shadow) memory read
> > or write in the function (explicit or possible through function calls etc.)
> > you really don't need to unpoison (depending on whether we follow LLVM as
> > mentioned above then it can be removed without anything, or the decl needs
> > to be somehow marked and tell asan_emit_stack_protection it shouldn't poison
> > it at the start), and for ASAN_MARK poisoning appearing after the last
> > load/store in the function (post dominating those, you don't care about
> > noreturn though) you can combine that (remove the ASAN_MARK) with letting
> > asan_emit_stack_protection know it doesn't need to unpoison.
> 
> Fully agree with that approach, however I would be happy to do that as a follow-up as
> it's not going to so trivial..

Ok.

> >> +	    char c = poison ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
> >> +	    for (unsigned i = 0; i < shadow_size; ++i)
> >> +	      {
> >> +		emit_move_insn (var_mem, gen_int_mode (c, QImode));
> >> +		var_mem = adjust_address (var_mem, QImode, 1);
> > 
> > When you combine it with the loop, you can also use the infrastructure to
> > handle it 4 bytes at a time.
> 
> Current implementation can handle up to 4 bytes at a time. I'm wondering we can
> do even better for targets with 64-bits memory stores? How can one get such
> info about a target?

It is not just the question of whether the target has fast 64-bit memory
stores, but also whether the constants you want to store are reasonably
cheap.  E.g. on x86_64, movabsq is kind of expensive, so storing 64-bit 0
is cheap, but storing 64-bit 0xfdfdfdfdfdfdfdfdULL might be better done as 2
32-bit stores, perhaps both for speed and size.

> > 
> > Another thing I've noticed is that the inline expansion of
> > __asan_unpoison_stack_memory you emit looks buggy.
> > In use-after-scope-1.c I see:
> >   _9 = (unsigned long) &my_char;
> >   _10 = _9 >> 3;
> >   _11 = _10 + 2147450880;
> >   _12 = (signed char *) _11;
> >   MEM[(short int *)_12] = 0;
> > 
> > That would be fine only for 16 byte long my_char, but we have instead 9 byte
> > one.  So I believe in that case we need to store
> > 0x00, 0x01 bytes, for little endian thus 0x0100.  You could use for it
> > a function similarly to asan_shadow_cst, just build INTEGER_CST rather than
> > CONST_INT out of it.  In general, poisioning is storing 0xf8 to all affected
> > shadow bytes, unpoisioning should restore the state what we would emit
> > without use-after-scope sanitization, which is all but the last byte 0, and
> > the last byte 0 only if the var size is a multiple of 8, otherwise number
> > of valid bytes (1-7).
> 
> Fixed in the newer patch.
> 
> > 
> > As for the option, it seems clang uses now
> > -fsanitize-address-use-after-scope option, while I don't like that much, if
> > they have already released some version with that option or if they are
> > unwilling to change, I'd go with their option.
> 
> I also do not like the option, but 3.9.0 has already the functionality. Thus,
> I'm copying LLVM behavior.
> 
> > 
> >> +         if (flag_stack_reuse != SR_NONE
> >> +             && flag_openacc
> >> +             && oacc_declare_returns != NULL)
> > 
> > This actually looks like preexisting OpenACC bug, I doubt the OpenACC
> > behavior should depend on -fstack-reuse= setting.
> 
> The generated diff for this hunk is bit misleading, I simplified that
> in the second version.
> 
> > 
> > +      bool unpoison_var = asan_poisoned_variables.contains (t);
> > +      if (asan_sanitize_use_after_scope ()
> > +         && unpoison_var)
> > +       asan_poisoned_variables.remove (t);
> > 
> > Similarly to asan_handled_variables, I'd prefer it to be a pointer to
> > hash_set or something similar, so that it costs as few as possible for the
> > general case (no sanitization).  Similarly, querying the hash_set even for
> > no use-after-scope sanitization looks wrong.
> 
> Sure, fixed.
> 
> > 
> > +         if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
> > 
> > I would say if asan_sanitize_stack_p () is false, then we should not be
> > doing use-after-scope sanitization (error if user requested that
> > explicitly).
> 
> Done by adding '&& ASAN_STACK' to asan_sanitize_use_after_scope.
> 
> > 
> > Don't remember if I've mentioned it earlier, but for vars that are
> > TREE_ADDRESSABLE only because of ASAN_MARK calls, we should probably turn
> > them non-addressable and remove those ASAN_MARK calls, those shouldn't leak.
> > You can have a look at the r237814 change for how similarly
> > compare and exchange is special cased for the
> > addressables discovery (though, the ASAN_MARK case would be easier, just
> > drop it rather than turn it into something different).
> 
> I like the approach to not to handle local variables that have address not taken.
> My implementation in gimplify.c handles only vars with TREE_ADDRESSABLE set to true,
> which should cover all variables which have an address taken in the original code.
> I'm aware of cases where one can hit another variable by *(&local_var1 + magic_offset) = 12345,
> but that would make the check even more slower.
> 
> > 
> > I think I miss some testsuite coverage for what has been discussed, stuff
> > like:
> >   switch (x)
> >     {	
> >       int a;
> >     case 1:
> >       int b;
> >       foo (&a);
> >       foo (&b);
> >       /* FALLTHRU */
> >     case 2:
> >       int c;
> >       foo (&a);
> >       foo (&b);
> >       foo (&c);
> > ...
> >     }
> 
> One can't write such code because of:
> 
> use-after-scope-switch.c: In function ‘main’:
> use-after-scope-switch.c:16:7: error: a label can only be part of a statement and a declaration is not a statement
>        int b;
>        ^~~
> use-after-scope-switch.c:21:7: error: a label can only be part of a statement and a declaration is not a statement
>        int c;
>        ^~~

So try:
  switch (x)
    {	
      int a;
    case 1:;
      int b;
      foo (&a);
      foo (&b);
      /* FALLTHRU */
    case 2:;
      int c;
      foo (&a);
      foo (&b);
      foo (&c);
...
    }
then?

> I know that this is still a challenging area. As I've read C/C++ FE, we really have
> data structures that are used to identify cross initialization in gotos like:
> 
> -Wjump-misses-init
> cross.c: In function ‘main’:
> cross.c:46:5: warning: jump skips variable initialization [-Wjump-misses-init]
>      goto label;
>      ^~~~
> cross.c:25:7: note: label ‘label’ defined here
>        label:
>        ^~~~~
> cross.c:13:16: note: ‘a’ declared here
>        struct A a = { 123 };
> 
> However, it's connected to scopes and I'm not sure whether this data structures
> are up to date during gimplification and whether on can find a mapping between
> LABEL_DECLs and these scopes.
> 
> As an initial implementation, I decided to register all LABEL_DECLs that are either
> used in goto, or have address taken. For those, I defensively generate unpoisoning
> code. Hope it's reasonable initial implementation?

I guess we can live with that for initial implementation, but would be nice
to get it improved in stage1 or stage3.

I'll try to look at the new patch in detail soon.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-03  9:27           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  2016-10-03  9:30             ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope Martin Liška
  2016-10-03  9:39             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Jakub Jelinek
@ 2016-10-07 11:13             ` Jakub Jelinek
  2016-10-12 14:08               ` Martin Liška
  2 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-10-07 11:13 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

Hi!

Ok, first let me list some needed follow-ups that don't need to be handled
right away:
- r237814-like changes for ASAN_MARK
- optimization to remove ASAN_MARK unpoisoning at the start of the function
- optimization to remove ASAN_MARK poisoning at the end of function (and
  remove epilogue unpoisoning)
- optimization to remove ASAN_MARK unpoisoning followed by ASAN_MARK poisoning
  or vice versa if there are no memory accesses in between
- try to improve the goto handling

On Mon, Oct 03, 2016 at 11:27:38AM +0200, Martin Liška wrote:
> +	      if (dump_file && (dump_flags & TDF_DETAILS))
> +		fprintf (dump_file, "Skipping stack emission for variable: %s "
> +			 "(%ldB)\n",

This message is weird.  I would have thought you are informing the user
that you are unpoisoning the stack for the specified variable.

> +			 IDENTIFIER_POINTER (DECL_NAME (decl)),

Can't DECL_NAME be NULL?

> +			 var_end_offset - var_offset);
> +  for (unsigned i = 0; i < size; ++i)
> +    {
> +      unsigned char shadow_c = c;
> +      if (i == size- 1 && last_chunk_size && !is_clobber)

Wrong formatting, space before - missing.

> +  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
> +				  NOP_EXPR, base);

Incorrect indentation.

> +
> +/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
> +
> +static inline bool
> +asan_sanitize_use_after_scope (void)
> +{
> +  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
> +	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
> +	  && ASAN_STACK);
> +}

This looks wrong.  IMHO you really don't want to use ASAN_STACK in asan.h, because it
requires params.h.  I think what would be best is to prototype
asan_sanitize_stack_p in asan.h, remove static inline from its definition,
and have asan.h
static inline bool
asan_sanitize_use_after_scope (void)
{
  return (flag_sanitize_use_after_scope && asan_sanitize_stack_p ();
}
That way, for the common case of no sanitization it would be inline and
fast.

> +static inline bool
> +asan_no_sanitize_address_p (void)
> +{
> +  return lookup_attribute ("no_sanitize_address",
> +			   DECL_ATTRIBUTES (current_function_decl));
> +}

And you could avoid doing this.

> +	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())

And then just drop asan_sanitize_use_after_scope () from cases like this,
because really asan_sanitize_use_after_scope () must imply
asan_sanitize_stack_p ().

> @@ -1127,7 +1127,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
>        if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>  	{
>  	  base = virtual_stack_vars_rtx;
> -	  if (asan_sanitize_stack_p () && pred)
> +	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())

Likewise.
> +	      && pred)

> @@ -1514,7 +1515,9 @@ defer_stack_allocation (tree var, bool toplevel)
>    /* If stack protection is enabled, *all* stack variables must be deferred,
>       so that we can re-order the strings to the top of the frame.
>       Similarly for Address Sanitizer.  */
> -  if (flag_stack_protect || asan_sanitize_stack_p ())
> +  if (flag_stack_protect
> +      || asan_sanitize_stack_p ()
> +      || asan_sanitize_use_after_scope ())

And again.

>      return true;
>  
>    unsigned int align = TREE_CODE (var) == SSA_NAME
> @@ -2212,7 +2215,7 @@ expand_used_vars (void)
>  	    expand_stack_vars (stack_protect_decl_phase_2, &data);
>  	}
>  
> -      if (asan_sanitize_stack_p ())
> +      if (asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())

And again.

> --- a/gcc/flag-types.h
> +++ b/gcc/flag-types.h
> @@ -229,6 +229,7 @@ enum sanitize_code {
>    SANITIZE_OBJECT_SIZE = 1UL << 20,
>    SANITIZE_VPTR = 1UL << 21,
>    SANITIZE_BOUNDS_STRICT = 1UL << 22,
> +  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
>    SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
>  		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
>  		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
> @@ -237,7 +238,9 @@ enum sanitize_code {
>  		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
>  		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
>    SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
> -			| SANITIZE_BOUNDS_STRICT
> +			| SANITIZE_BOUNDS_STRICT,
> +  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
> +			| SANITIZE_USE_AFTER_SCOPE

Looking at this, as -fsanitize-use-after-scope is a separate option, it
shouldn't mess up anything in the SANITIZE_* flags.  So just use a
flag_sanitize_use_after_scope var for that.  It isn't something where you'd
decide differently on it e.g. for -fno-sanitize= of -fsanitize-recover= etc.

> @@ -1228,6 +1306,14 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>  		}
>  	    }
>  	}
> +
> +      if (asan_sanitize_use_after_scope ()
> +	  && asan_poisoned_variables != NULL
> +	  && asan_poisoned_variables->contains (t))

Do you really need the asan_sanitize_use_after_scope () ?  Won't the
hash_set not be populated at all if it isn't true?

> +      if (asan_sanitize_use_after_scope ()
> +	  && !asan_no_sanitize_address_p ()

You wouldn't need to check !asan_no_sanitize_address_p ()
separately here.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-07 11:13             ` Jakub Jelinek
@ 2016-10-12 14:08               ` Martin Liška
  2016-10-21 14:26                 ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-10-12 14:08 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 6605 bytes --]

On 10/07/2016 01:13 PM, Jakub Jelinek wrote:
> Hi!
> 

Hi Jakub.

Again thanks for detailed review, you found many improvements.

> Ok, first let me list some needed follow-ups that don't need to be handled
> right away:
> - r237814-like changes for ASAN_MARK

Yes, that's missing. I'm wondering whether the same approach would be viable as
the {un}poisoning happens during gimplification. Thus it generates &var expressions
which verifier won't be happy about?

> - optimization to remove ASAN_MARK unpoisoning at the start of the function

As current implementation does not poison variables at the very beginning of
a functions (RTL stack frame emission), it won't be needed.

> - optimization to remove ASAN_MARK poisoning at the end of function (and
>   remove epilogue unpoisoning)
> - optimization to remove ASAN_MARK unpoisoning followed by ASAN_MARK poisoning
>   or vice versa if there are no memory accesses in between

Yep, both are not handled and are very similar from my perspective: pairing
poisoning and unpoisoning pair which are not interfered by a memory operation
in between (in dominator meaning of word).
I'm wondering whether can be done effectively as we would need to visit all BBs
in a dominance domain (get_all_dominated_blocks) and check for the memory operations.
One improvement can be set of BBs that do not have any memory operations (excluding
ASAN_CHECK, ASAN_MARK builtins), but it can be still expensive to simplify poisoning
for all local variables. Or am I wrong?

> - try to improve the goto handling

Works for me to be target for stage3.

I'm sending a new version where all mentioned notes should be fixed.

Thanks,
Martin


> 
> On Mon, Oct 03, 2016 at 11:27:38AM +0200, Martin Liška wrote:
>> +	      if (dump_file && (dump_flags & TDF_DETAILS))
>> +		fprintf (dump_file, "Skipping stack emission for variable: %s "
>> +			 "(%ldB)\n",
> 
> This message is weird.  I would have thought you are informing the user
> that you are unpoisoning the stack for the specified variable.
> 
>> +			 IDENTIFIER_POINTER (DECL_NAME (decl)),
> 
> Can't DECL_NAME be NULL?
> 
>> +			 var_end_offset - var_offset);
>> +  for (unsigned i = 0; i < size; ++i)
>> +    {
>> +      unsigned char shadow_c = c;
>> +      if (i == size- 1 && last_chunk_size && !is_clobber)
> 
> Wrong formatting, space before - missing.
> 
>> +  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
>> +				  NOP_EXPR, base);
> 
> Incorrect indentation.
> 
>> +
>> +/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
>> +
>> +static inline bool
>> +asan_sanitize_use_after_scope (void)
>> +{
>> +  return ((flag_sanitize & SANITIZE_ADDRESS_USE_AFTER_SCOPE)
>> +	  == SANITIZE_ADDRESS_USE_AFTER_SCOPE
>> +	  && ASAN_STACK);
>> +}
> 
> This looks wrong.  IMHO you really don't want to use ASAN_STACK in asan.h, because it
> requires params.h.  I think what would be best is to prototype
> asan_sanitize_stack_p in asan.h, remove static inline from its definition,
> and have asan.h
> static inline bool
> asan_sanitize_use_after_scope (void)
> {
>   return (flag_sanitize_use_after_scope && asan_sanitize_stack_p ();
> }
> That way, for the common case of no sanitization it would be inline and
> fast.
> 
>> +static inline bool
>> +asan_no_sanitize_address_p (void)
>> +{
>> +  return lookup_attribute ("no_sanitize_address",
>> +			   DECL_ATTRIBUTES (current_function_decl));
>> +}
> 
> And you could avoid doing this.
> 
>> +	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
> 
> And then just drop asan_sanitize_use_after_scope () from cases like this,
> because really asan_sanitize_use_after_scope () must imply
> asan_sanitize_stack_p ().
> 
>> @@ -1127,7 +1127,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
>>        if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
>>  	{
>>  	  base = virtual_stack_vars_rtx;
>> -	  if (asan_sanitize_stack_p () && pred)
>> +	  if ((asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
> 
> Likewise.
>> +	      && pred)
> 
>> @@ -1514,7 +1515,9 @@ defer_stack_allocation (tree var, bool toplevel)
>>    /* If stack protection is enabled, *all* stack variables must be deferred,
>>       so that we can re-order the strings to the top of the frame.
>>       Similarly for Address Sanitizer.  */
>> -  if (flag_stack_protect || asan_sanitize_stack_p ())
>> +  if (flag_stack_protect
>> +      || asan_sanitize_stack_p ()
>> +      || asan_sanitize_use_after_scope ())
> 
> And again.
> 
>>      return true;
>>  
>>    unsigned int align = TREE_CODE (var) == SSA_NAME
>> @@ -2212,7 +2215,7 @@ expand_used_vars (void)
>>  	    expand_stack_vars (stack_protect_decl_phase_2, &data);
>>  	}
>>  
>> -      if (asan_sanitize_stack_p ())
>> +      if (asan_sanitize_stack_p () || asan_sanitize_use_after_scope ())
> 
> And again.
> 
>> --- a/gcc/flag-types.h
>> +++ b/gcc/flag-types.h
>> @@ -229,6 +229,7 @@ enum sanitize_code {
>>    SANITIZE_OBJECT_SIZE = 1UL << 20,
>>    SANITIZE_VPTR = 1UL << 21,
>>    SANITIZE_BOUNDS_STRICT = 1UL << 22,
>> +  SANITIZE_USE_AFTER_SCOPE = 1UL << 23,
>>    SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
>>  		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
>>  		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
>> @@ -237,7 +238,9 @@ enum sanitize_code {
>>  		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
>>  		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
>>    SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
>> -			| SANITIZE_BOUNDS_STRICT
>> +			| SANITIZE_BOUNDS_STRICT,
>> +  SANITIZE_ADDRESS_USE_AFTER_SCOPE = SANITIZE_ADDRESS
>> +			| SANITIZE_USE_AFTER_SCOPE
> 
> Looking at this, as -fsanitize-use-after-scope is a separate option, it
> shouldn't mess up anything in the SANITIZE_* flags.  So just use a
> flag_sanitize_use_after_scope var for that.  It isn't something where you'd
> decide differently on it e.g. for -fno-sanitize= of -fsanitize-recover= etc.
> 
>> @@ -1228,6 +1306,14 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
>>  		}
>>  	    }
>>  	}
>> +
>> +      if (asan_sanitize_use_after_scope ()
>> +	  && asan_poisoned_variables != NULL
>> +	  && asan_poisoned_variables->contains (t))
> 
> Do you really need the asan_sanitize_use_after_scope () ?  Won't the
> hash_set not be populated at all if it isn't true?
> 
>> +      if (asan_sanitize_use_after_scope ()
>> +	  && !asan_no_sanitize_address_p ()
> 
> You wouldn't need to check !asan_no_sanitize_address_p ()
> separately here.
> 
> 	Jakub
> 


[-- Attachment #2: 0001-Introduce-fsanitize-address-use-after-scope-v3.patch --]
[-- Type: text/x-patch, Size: 39973 bytes --]

From c383a3f9259400dfecaa99d901ce2b59fc30cea1 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize-address-use-after-scope

gcc/c-family/ChangeLog:

2016-09-27  Martin Liska  <mliska@suse.cz>

	* c-common.c (warn_for_unused_label): Save all labels used
	in goto or in &label;

gcc/ChangeLog:

2016-09-27  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Move the enum to header file.
	(asan_init_shadow_ptr_types): Make type creation more generic.
	(shadow_mem_size): New function.
	(asan_emit_stack_protection): Use newly added ASAN_SHADOW_GRANULARITY.
	Rewritten stack unpoisoning code.
	(build_shadow_mem_access): Add new argument return_address.
	(instrument_derefs): Instrument local variables if use after scope
	sanitization is enabled.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	* asan.h (enum asan_mark_flags): Moved here from asan.c
	(asan_protect_stack_decl): Protect all declaration that need
	to live in memory.
	(asan_sanitize_use_after_scope): New function.
	* cfgexpand.c(partition_stack_vars): Consider
	asan_sanitize_use_after_scope in condition.
	(expand_stack_vars): Likewise.
	(defer_stack_allocation): Likewise.
	* common.opt (-fsanitize-address-use-after-scope): New option.
	* doc/invoke.texi (use-after-scope-direct-emission-threshold):
	Explain the parameter.
	* gimplify.c (build_asan_poison_call_expr): New function.
	(asan_poison_variable): Likewise.
	(gimplify_bind_expr): Generate poisoning/unpoisoning for local
	variables that have address taken.
	(gimplify_decl_expr): Likewise.
	(gimplify_target_expr): Likewise for C++ temporaries.
	(sort_by_decl_uid): New function.
	(gimplify_expr): Unpoison all variables for a label we can jump
	from outside of a scope.
	(gimplify_function_tree): Clear asan_poisoned_variables.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def (ASAN_MARK): Declare.
	* opts.c (finish_options): Handle -fstack-reuse if
	-fsanitize-address-use-after-scope is enabled.
	(common_handle_option): Enable address sanitization if
	-fsanitize-address-use-after-scope is enabled.
	* params.def (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD):
	New parameter.
	* params.h: Likewise.
	* sancov.c (pass_sanopt::execute): Handle IFN_ASAN_MARK.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory functions.
---
 gcc/asan.c              | 287 +++++++++++++++++++++++++++++++++++++++---------
 gcc/asan.h              |  48 +++++++-
 gcc/c-family/c-common.c |   8 ++
 gcc/cfgexpand.c         |  18 +--
 gcc/common.opt          |   3 +
 gcc/doc/invoke.texi     |  15 ++-
 gcc/gimplify.c          | 197 ++++++++++++++++++++++++++++++---
 gcc/internal-fn.c       |   9 ++
 gcc/internal-fn.def     |   1 +
 gcc/opts.c              |  27 ++++-
 gcc/params.def          |   6 +
 gcc/params.h            |   2 +
 gcc/sanitizer.def       |   4 +
 gcc/sanopt.c            |   3 +
 14 files changed, 537 insertions(+), 91 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 3a4b5f7..ec85c40 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -244,6 +244,13 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> *asan_handled_variables = NULL;
+
+hash_set <tree> *asan_used_labels = NULL;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -286,6 +293,15 @@ set_sanitized_sections (const char *sections)
     }
 }
 
+bool
+asan_sanitize_stack_p (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS)
+	  && ASAN_STACK
+	  && !lookup_attribute ("no_sanitize_address",
+				DECL_ATTRIBUTES (current_function_decl)));
+}
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -314,22 +330,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -932,12 +939,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1021,6 +1032,15 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+shadow_mem_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1046,7 +1066,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
-  HOST_WIDE_INT last_offset, last_size;
+  HOST_WIDE_INT last_offset;
   int l;
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
@@ -1204,10 +1224,10 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 				       (aoff - prev_offset)
 				       >> ASAN_SHADOW_SHIFT);
 	  prev_offset = aoff;
-	  for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT))
+	  for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
 	    if (aoff < offset)
 	      {
-		if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1)
+		if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
 		  shadow_bytes[i] = 0;
 		else
 		  shadow_bytes[i] = offset - aoff;
@@ -1281,35 +1301,65 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
 
-  prev_offset = base_offset;
+  /* Unpoison shadow memory of a stack at the very end of a function.
+     As we're poisoning stack variables at the end of their scope,
+     shadow memory must be properly unpoisoned here.  The easiest approach
+     would be to collect all variables that should not be unpoisoned and
+     we unpoison shadow memory of the whole stack except ranges
+     occupied by these variables.  */
   last_offset = base_offset;
-  last_size = 0;
-  for (l = length; l; l -= 2)
+  HOST_WIDE_INT current_offset = last_offset;
+  if (length)
     {
-      offset = base_offset + ((offsets[l - 1] - base_offset)
-			     & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-      if (last_offset + last_size != offset)
+      HOST_WIDE_INT var_end_offset = 0;
+      HOST_WIDE_INT stack_start = offsets[length - 1];
+      gcc_assert (last_offset == stack_start);
+
+      for (int l = length - 2; l > 0; l -= 2)
 	{
-	  shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				       (last_offset - prev_offset)
-				       >> ASAN_SHADOW_SHIFT);
-	  prev_offset = last_offset;
-	  asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
-	  last_offset = offset;
-	  last_size = 0;
+	  HOST_WIDE_INT var_offset = offsets[l];
+	  current_offset = var_offset;
+	  var_end_offset = offsets[l - 1];
+	  HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset,
+					     BITS_PER_UNIT);
+
+	  /* Should we unpoison the variable?  */
+	  if (asan_handled_variables != NULL
+	      && asan_handled_variables->contains (decl))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  const char *n = DECL_NAME (decl)
+		    ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<unknown>";
+		  fprintf (dump_file, "Unpoisoning shadow stack for variable: "
+			   "%s (%" PRId64 "B)\n", n,
+			   var_end_offset - var_offset);
+		}
+
+	      unsigned HOST_WIDE_INT s
+		= shadow_mem_size (current_offset - last_offset);
+	      asan_clear_shadow (shadow_mem, s);
+	      HOST_WIDE_INT shift
+		= shadow_mem_size (current_offset - last_offset + rounded_size);
+	      shadow_mem = adjust_address (shadow_mem, VOIDmode, shift);
+	      last_offset = var_offset + rounded_size;
+	      current_offset = last_offset;
+	    }
+
 	}
-      last_size += base_offset + ((offsets[l - 2] - base_offset)
-				  & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
-		   - offset;
-    }
-  if (last_size)
-    {
-      shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				   (last_offset - prev_offset)
-				   >> ASAN_SHADOW_SHIFT);
-      asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
+
+      /* Handle last redzone.  */
+      current_offset = offsets[0];
+      asan_clear_shadow (shadow_mem,
+			 shadow_mem_size (current_offset - last_offset));
     }
 
+  /* Clean-up set with instrumented stack variables.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1589,12 +1639,14 @@ insert_if_then_before_iter (gcond *cond,
   gsi_insert_after (&cond_insert_point, cond, GSI_NEW_STMT);
 }
 
-/* Build
-   (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
+/* Build (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().
+   If RETURN_ADDRESS is set to true, return memory location instread
+   of a value in the shadow memory.  */
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_address = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1617,11 +1669,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_address)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1825,7 +1881,9 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && (!asan_sanitize_use_after_scope ()
+		  || !TREE_ADDRESSABLE (inner)))
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2575,6 +2633,131 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Poison or unpoison (depending on IS_CLOBBER variable) shadow memory based
+   on SHADOW address.  Newly added statements will be added to ITER with
+   given location LOC.  We mark SIZE bytes in shadow memory, where
+   LAST_CHUNK_SIZE is greater than zero in situation where we are at the
+   end of a variable.  */
+
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 bool is_clobber, unsigned size,
+			 unsigned last_chunk_size)
+{
+  tree shadow_ptr_type;
+
+  switch (size)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < size; ++i)
+    {
+      unsigned char shadow_c = c;
+      if (i == size - 1 && last_chunk_size && !is_clobber)
+	shadow_c = last_chunk_size;
+      val |= (unsigned HOST_WIDE_INT) shadow_c << (BITS_PER_UNIT * i);
+    }
+
+  /* Handle last chunk in unpoisoning.  */
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (asan_handled_variables == NULL)
+    asan_handled_variables = new hash_set<tree> (16);
+  asan_handled_variables->add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  /* Generate direct emission if size_in_bytes is small.  */
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes);
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  unsigned HOST_WIDE_INT last_chunk_size = 0;
+	  unsigned HOST_WIDE_INT s = (offset + size) * ASAN_SHADOW_GRANULARITY;
+	  if (s > size_in_bytes)
+	    last_chunk_size = ASAN_SHADOW_GRANULARITY - (s - size_in_bytes);
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, is_clobber,
+				   size, last_chunk_size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..9201815 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,9 +37,14 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of labels that are either used in a goto, or their address
+   has been taken.  */
+extern hash_set <tree> *asan_used_labels;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
+#define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT)
 
 /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
@@ -50,21 +56,39 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
+{
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
+
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
 asan_protect_stack_decl (tree decl)
 {
-  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
+  return DECL_P (decl) && TREE_ADDRESSABLE (decl);
 }
 
 /* Return the size of padding needed to insert after a protected
@@ -81,6 +105,9 @@ extern bool set_asan_shadow_offset (const char *);
 
 extern void set_sanitized_sections (const char *);
 
+/* Return TRUE if stack sanitization should be performed.  */
+extern bool asan_sanitize_stack_p (void);
+
 /* Return TRUE if builtin with given FCODE will be intercepted by
    libasan.  */
 
@@ -105,4 +132,13 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index dbdb276..05bfcdc 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "substring-locations.h"
 #include "spellcheck.h"
+#include "asan.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
 
@@ -12040,6 +12041,13 @@ warn_for_unused_label (tree label)
       else
         warning (OPT_Wunused_label, "label %q+D declared but not defined", label);
     }
+  else if (asan_sanitize_use_after_scope ())
+    {
+      if (asan_used_labels == NULL)
+	asan_used_labels = new hash_set<tree> (16);
+
+      asan_used_labels->add (label);
+    }
 }
 
 /* Warn for division by zero according to the value of DIVISOR.  LOC
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index c7a2faa..a2baa60 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -867,18 +867,6 @@ union_stack_vars (size_t a, size_t b)
     }
 }
 
-/* Return true if the current function should have its stack frame
-   protected by address sanitizer.  */
-
-static inline bool
-asan_sanitize_stack_p (void)
-{
-  return ((flag_sanitize & SANITIZE_ADDRESS)
-	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
-}
-
 /* A subroutine of expand_used_vars.  Binpack the variables into
    partitions constrained by the interference graph.  The overall
    algorithm used is as follows:
@@ -940,7 +928,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1514,7 +1503,8 @@ defer_stack_allocation (tree var, bool toplevel)
   /* If stack protection is enabled, *all* stack variables must be deferred,
      so that we can re-order the strings to the top of the frame.
      Similarly for Address Sanitizer.  */
-  if (flag_stack_protect || asan_sanitize_stack_p ())
+  if (flag_stack_protect
+      || asan_sanitize_stack_p ())
     return true;
 
   unsigned int align = TREE_CODE (var) == SSA_NAME
diff --git a/gcc/common.opt b/gcc/common.opt
index 3425c19..35ec562 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -964,6 +964,9 @@ fsanitize-recover
 Common Report
 This switch is deprecated; use -fsanitize-recover= instead.
 
+fsanitize-address-use-after-scope
+Common Driver Report Var(flag_sanitize_address_use_after_scope) Init(0)
+
 fsanitize-undefined-trap-on-error
 Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
 Use trap instead of a library function for undefined behavior sanitization.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b7a32b6..dfb57de 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10077,6 +10077,10 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item use-after-scope-direct-emission-threshold
+If size of a local variables in bytes is smaller of equal to this number,
+direct instruction emission is utilized to poison and unpoison local variables.
+
 @item chkp-max-ctor-size
 Static constructors generated by Pointer Bounds Checker may become very
 large and significantly increase compile time at optimization level
@@ -10287,6 +10291,7 @@ thread-safe code.
 Enable AddressSanitizer, a fast memory error detector.
 Memory access instructions are instrumented to detect
 out-of-bounds and use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/sanitizers/wiki/AddressSanitizer} for
 more details.  The run-time behavior can be influenced using the
 @env{ASAN_OPTIONS} environment variable.  When set to @code{help=1},
@@ -10298,6 +10303,7 @@ The option can't be combined with @option{-fsanitize=thread}.
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
 @item -fsanitize=thread
@@ -10497,8 +10503,8 @@ except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
 @option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
 @option{-fsanitize=bounds-strict},
 @option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
-For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
-for which this feature is experimental.
+For these sanitizers error recovery is turned on by default,
+except @option{-fsanitize=address}, for which this feature is experimental.
 @option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
 accepted, the former enables recovery for all sanitizers that support it,
 the latter disables recovery for all sanitizers that support it.
@@ -10520,6 +10526,11 @@ Similarly @option{-fno-sanitize-recover} is equivalent to
 -fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
 @end smallexample
 
+@item -fsanitize-address-use-after-scope
+@opindex fsanitize-address-use-after-scope
+Enable sanitization of local variables to detect use-after-scope bugs.
+The option sets @option{-fstack-reuse} to @samp{none}.
+
 @item -fsanitize-undefined-trap-on-error
 @opindex fsanitize-undefined-trap-on-error
 The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 0b1a0be..e58e3c2 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> *asan_poisoned_variables = NULL;
 
 enum gimplify_omp_var_data
 {
@@ -1088,6 +1092,79 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */
+
+static tree
+build_asan_poison_call_expr (tree decl)
+{
+  /* Do not poison variables that have size equal to zero.  */
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  if (zerop (unit_size))
+    return NULL_TREE;
+
+  tree base = build_fold_addr_expr (decl);
+
+  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
+				       void_type_node, 3,
+				       build_int_cst (integer_type_node,
+						      ASAN_MARK_CLOBBER),
+				       base, unit_size);
+}
+
+/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
+   on POISON flag, shadow memory of a DECL variable.  The call will be
+   put on location identified by IT iterator, where BEFORE flag drives
+   position where the stmt will be put.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
+		      bool before)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  /* Do not poison variables that have size equal to zero.  */
+  if (zerop (unit_size))
+    return;
+
+  /* It's necessary to have all stack variables aligned to ASAN granularity
+     bytes.  */
+  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+
+  if (before)
+    gsi_insert_before (it, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (it, g, GSI_NEW_STMT);
+}
+
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  gimple_stmt_iterator it = gsi_last (*seq_p);
+  bool before = false;
+
+  if (gsi_end_p (it))
+    before = true;
+
+  asan_poison_variable (decl, poison, &it, before);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1228,6 +1305,13 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 		}
 	    }
 	}
+
+      if (asan_poisoned_variables != NULL
+	  && asan_poisoned_variables->contains (t))
+	{
+	  asan_poisoned_variables->remove (t);
+	  asan_poison_variable (t, true, &cleanup);
+	}
     }
 
   if (ret_clauses)
@@ -1472,13 +1556,26 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
   if (VAR_P (decl) && !DECL_EXTERNAL (decl))
     {
       tree init = DECL_INITIAL (decl);
+      bool is_vla = false;
 
       if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
 	  || (!TREE_STATIC (decl)
 	      && flag_stack_check == GENERIC_STACK_CHECK
 	      && compare_tree_int (DECL_SIZE_UNIT (decl),
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
-	gimplify_vla_decl (decl, seq_p);
+	{
+	  gimplify_vla_decl (decl, seq_p);
+	  is_vla = true;
+	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && !is_vla
+	  && TREE_ADDRESSABLE (decl)
+	  && !TREE_STATIC (decl))
+	{
+	  asan_poisoned_variables->add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
 
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
@@ -6162,6 +6259,9 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   tree init = TARGET_EXPR_INITIAL (targ);
   enum gimplify_status ret;
 
+  bool unpoison_empty_seq = false;
+  gimple_stmt_iterator unpoison_it;
+
   if (init)
     {
       tree cleanup = NULL_TREE;
@@ -6175,7 +6275,14 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  gimplify_vla_decl (temp, pre_p);
 	}
       else
-	gimple_add_tmp_var (temp);
+	{
+	  /* Save location where we need to place unpoisoning.  It's possible
+	     that a variable will be converted to needs_to_live_in_memory.  */
+	  unpoison_it = gsi_last (*pre_p);
+	  unpoison_empty_seq = gsi_end_p (unpoison_it);
+
+	  gimple_add_tmp_var (temp);
+	}
 
       /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
 	 expression is supposed to initialize the slot.  */
@@ -6211,20 +6318,34 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       /* Add a clobber for the temporary going out of scope, like
 	 gimplify_bind_expr.  */
       if (gimplify_ctxp->in_cleanup_point_expr
-	  && needs_to_live_in_memory (temp)
-	  && flag_stack_reuse == SR_ALL)
-	{
-	  tree clobber = build_constructor (TREE_TYPE (temp),
-					    NULL);
-	  TREE_THIS_VOLATILE (clobber) = true;
-	  clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
-	  if (cleanup)
-	    cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
-			      clobber);
-	  else
-	    cleanup = clobber;
-	}
+	  && needs_to_live_in_memory (temp))
+	{
+	  if (flag_stack_reuse == SR_ALL)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (temp),
+						NULL);
+	      TREE_THIS_VOLATILE (clobber) = true;
+	      clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+	      if (cleanup)
+		cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+				  clobber);
+	      else
+		cleanup = clobber;
+	    }
+	  if (asan_sanitize_use_after_scope ())
+	    {
+	      tree asan_cleanup = build_asan_poison_call_expr (temp);
+	      if (asan_cleanup)
+		{
+		  if (unpoison_empty_seq)
+		    unpoison_it = gsi_start (*pre_p);
 
+		  asan_poison_variable (temp, false, &unpoison_it,
+					unpoison_empty_seq);
+		  gimple_push_cleanup (temp, asan_cleanup, false, pre_p);
+		}
+	    }
+	}
       if (cleanup)
 	gimple_push_cleanup (temp, cleanup, false, pre_p);
 
@@ -10724,6 +10845,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10815,6 +10955,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   location_t saved_location;
   enum gimplify_status ret;
   gimple_stmt_iterator pre_last_gsi, post_last_gsi;
+  tree label;
 
   save_expr = *expr_p;
   if (save_expr == NULL_TREE)
@@ -11230,6 +11371,29 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	case LABEL_EXPR:
 	  ret = gimplify_label_expr (expr_p, pre_p);
+	  label = LABEL_EXPR_LABEL (*expr_p);
+	  gcc_assert (decl_function_context (label) == current_function_decl);
+
+	  /* If the label is used in a goto statement, or address of the label
+	     is taken, we need to unpoison all variables that were seen so far.
+	     Doing so would prevent us from reporting a false positives.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && asan_used_labels != NULL
+	      && asan_used_labels->contains (label))
+	    {
+	      unsigned c = asan_poisoned_variables->elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set<tree>::iterator it
+		   = asan_poisoned_variables->begin ();
+		   it != asan_poisoned_variables->end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -12327,7 +12491,10 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
+  delete asan_poisoned_variables;
+  asan_poisoned_variables = NULL;
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 029a534..69b9a8e 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -236,6 +236,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 d4fbdb2..09f065f 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts.c b/gcc/opts.c
index 90e6186..0099d41 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -977,6 +977,25 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+     enabled.  */
+  if (opts->x_flag_sanitize
+      && !opts_set->x_flag_sanitize_address_use_after_scope)
+    opts->x_flag_sanitize_address_use_after_scope = true;
+
+  /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
+     is enabled.  */
+  if (opts->x_flag_sanitize_address_use_after_scope)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize-address-use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1448,8 +1467,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
-  SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
-  SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
+  SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
   SANITIZER_OPT (leak, SANITIZE_LEAK, false),
@@ -1777,6 +1796,10 @@ common_handle_option (struct gcc_options *opts,
       /* Deferred.  */
       break;
 
+    case OPT_fsanitize_address_use_after_scope:
+      opts->x_flag_sanitize_address_use_after_scope = value;
+      break;
+
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
diff --git a/gcc/params.def b/gcc/params.def
index 79b7dd4..f3c5c5c 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1156,6 +1156,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+	 256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 97c8d56..0a2905c 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -244,5 +244,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 27c43da..ff069f8 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -728,6 +728,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
-- 
2.9.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-12 14:08               ` Martin Liška
@ 2016-10-21 14:26                 ` Jakub Jelinek
  2016-10-25 13:18                   ` Martin Liška
  2016-10-27 14:40                   ` Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-10-21 14:26 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Wed, Oct 12, 2016 at 04:07:53PM +0200, Martin Liška wrote:
> > Ok, first let me list some needed follow-ups that don't need to be handled
> > right away:
> > - r237814-like changes for ASAN_MARK
> 
> Yes, that's missing. I'm wondering whether the same approach would be viable as
> the {un}poisoning happens during gimplification. Thus it generates &var expressions
> which verifier won't be happy about?

Sure, it uses &var.  The trick is that the addressable sub-pass then ignores
the taking of the address just in ASAN_MARK, and if some var is determined
to be addressable solely because of ASAN_MARK &var uses and no other reason,
the ASAN_MARK would be dropped and variable rewritten into SSA form.

> > - optimization to remove ASAN_MARK unpoisoning at the start of the function
> 
> As current implementation does not poison variables at the very beginning of
> a functions (RTL stack frame emission), it won't be needed.

But you still ASAN_MARK unpoison the vars when they get into scope, right?
And those can be removed if the optimizers could prove that the area has not
been poisoned yet since the beginning of the function.

> > - optimization to remove ASAN_MARK poisoning at the end of function (and
> >   remove epilogue unpoisoning)
> > - optimization to remove ASAN_MARK unpoisoning followed by ASAN_MARK poisoning
> >   or vice versa if there are no memory accesses in between
> 
> Yep, both are not handled and are very similar from my perspective: pairing
> poisoning and unpoisoning pair which are not interfered by a memory operation
> in between (in dominator meaning of word).
> I'm wondering whether can be done effectively as we would need to visit all BBs
> in a dominance domain (get_all_dominated_blocks) and check for the memory operations.
> One improvement can be set of BBs that do not have any memory operations (excluding
> ASAN_CHECK, ASAN_MARK builtins), but it can be still expensive to simplify poisoning
> for all local variables. Or am I wrong?

My memory is weak, but isn't this something the sanopt pass
(sanopt_optimize) is already doing similarly for e.g. ASAN_CHECK, UBSAN_NULL
and UBSAN_VPTR checks?  For ASAN_MARK, you actually don't care about any
calls, those shouldn't unpoison or poison again the vars under compiler's
back.

> > - try to improve the goto handling
> 
> Works for me to be target for stage3.

Sure.

> 2016-09-27  Martin Liska  <mliska@suse.cz>

Likely newer date :)

> 	* c-common.c (warn_for_unused_label): Save all labels used
> 	in goto or in &label;

&label.
instead?

> +	      if (dump_file && (dump_flags & TDF_DETAILS))
> +		{
> +		  const char *n = DECL_NAME (decl)
> +		    ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<unknown>";

Bad formatting.

		  const char *n = (DECL_NAME (decl)
				   ? IDENTIFIER_POINTER (DECL_NAME (decl))
				   : "<unknown>");
or
		  const char *n
		    = (DECL_NAME (decl)
		       ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<unknown>");

>  /* Return true if DECL should be guarded on the stack.  */
>  
>  static inline bool
>  asan_protect_stack_decl (tree decl)
>  {
> -  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
> +  return DECL_P (decl) && TREE_ADDRESSABLE (decl);
>  }

Can you explain this change?  It won't affect just
-fsanitize-use-after-scope, and goes in both directions, adds some
decls that weren't previously protected and removes others that were
previously protected.

Is the removal of !DECL_ARTIFICIAL so that you can use-after-scope
verify the C++ TARGET_EXPR temporaries?  Do we need to stack protect
those even for -fno-sanitize-use-after-scope?
I'm not 100% sure if we keep TREE_ADDRESSABLE meaningful for variables that
need to live in memory for other reasons until expansion.

So, I wonder if we don't want && (TREE_ADDRESSABLE (decl) || !DECL_ARTIFICAL (decl))
or just DECL_P (decl); or conditionalize something on
-fsanitize-use-after-scope.  Perhaps the change is good as is, just want to
point out that it affects also other -fsanitize=address modes in both
ways (instruments something that hasn't been before, and stops instrumenting
something that has been before).

> @@ -1514,7 +1503,8 @@ defer_stack_allocation (tree var, bool toplevel)
>    /* If stack protection is enabled, *all* stack variables must be deferred,
>       so that we can re-order the strings to the top of the frame.
>       Similarly for Address Sanitizer.  */
> -  if (flag_stack_protect || asan_sanitize_stack_p ())
> +  if (flag_stack_protect
> +      || asan_sanitize_stack_p ())
>      return true;

This hunk isn't needed, if all the conditions fit on one line,
one line is better.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-21 14:26                 ` Jakub Jelinek
@ 2016-10-25 13:18                   ` Martin Liška
  2016-10-27 14:40                   ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-10-25 13:18 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On 10/21/2016 04:26 PM, Jakub Jelinek wrote:
> My memory is weak, but isn't this something the sanopt pass
> (sanopt_optimize) is already doing similarly for e.g. ASAN_CHECK, UBSAN_NULL
> and UBSAN_VPTR checks?  For ASAN_MARK, you actually don't care about any
> calls, those shouldn't unpoison or poison again the vars under compiler's
> back.

Hi Jakub.

Your memory is not weak, it's exactly what to pass does. I've spent quite some
time reading and understanding the optimization (and finding PR78106) and looks
very similar to what we have for ASAN_MARK. However it's more complicated due
to existence of poisoning and unpoisoning calls.

In the previous email, you summarized what "patterns" can be optimized out:

1) - optimization to remove ASAN_MARK unpoisoning at the start of the function
2) - optimization to remove ASAN_MARK poisoning at the end of function (and
  remove epilogue unpoisoning)
3) - optimization to remove ASAN_MARK unpoisoning followed by ASAN_MARK poisoning
  or vice versa if there are no memory accesses in between

I'll come with more generalization:

Ia) ASAN_MARK poisonining followed by ASAN_MARK unpoisoning with no memory access
    in between => ASAN_MARK poisoning can be removed; 
Ib) ASAN_MARK unpoisonining followed by ASAN_MARK poisoning with no memory access
    in between => ASAN_MARK unpoisoning can be removed; 

Ia + Ib match exactly with 3). As function epilogue contains unpoisoning, Ia) == 2)

IIa) ASAN_MARK poisoning followed by ASAN_MARK poisoning => second ASAN_MARK can be removed
IIb) ASAN_MARK unpoisoning followed by ASAN_MARK unpoisoning => second ASAN_MARK can be removed

IIb matches 1)

III) ASAN_CHECK(&var) followed by ASAN_CHECK(&var) with no ASAN_MARK poisoning in between =>
     second ASAN_CHECK can be removed (this is more aggressive than what current
     can do maybe_optimize_asan_check_ifn).

Notes:
- Compared to current imm_dom_path_with_freeing_call_p, we would need 3 such predicates:
  x) imm_dom_path_with_asan_check () - needed by Ia, Ib
  y) imm_dom_path_with_asan_mark_poisoning () - this should return set of variables which
     can be potentially poisoned - needed by IIb and III
  z) imm_dom_path_with_asan_mark_unpoisoning () - this should return set of variables
     which can be potentially unpoisoned - needed by IIa

I would appreciate a feedback about my brainstorming I did about sanopt porting to use-after-scope,
maybe my cases are more generic that we would eventually need. And I'm not 100% convinced that all my
patterns described above would be doable in a similar way as current sanopt algorithm is done?

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-21 14:26                 ` Jakub Jelinek
  2016-10-25 13:18                   ` Martin Liška
@ 2016-10-27 14:40                   ` Martin Liška
  2016-10-27 17:24                     ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-10-27 14:40 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 5866 bytes --]

On 10/21/2016 04:26 PM, Jakub Jelinek wrote:
> On Wed, Oct 12, 2016 at 04:07:53PM +0200, Martin Liška wrote:
>>> Ok, first let me list some needed follow-ups that don't need to be handled
>>> right away:
>>> - r237814-like changes for ASAN_MARK

I've spent quite some on that and that's what I begin (use-after-scope-addressable.patch).
Problem is that as I ignore all ASAN_MARK internal fns, the code does not detect having address
taken in:

_2 = MEM[(char *)&my_char + 8B];

  char *ptr;
  {
    char my_char[9];
    ptr = &my_char[0];
  }

  return *(ptr+8);

and thus the code in tree-ssa.c (maybe_optimize_var) sets TREE_ADDRESSABLE (var) = 0.

Second question I have is whether we want to handle just TREE_ADDRESSABLE stuff during gimplification?
Basically in a way that the current patch is doing?

>>
>> Yes, that's missing. I'm wondering whether the same approach would be viable as
>> the {un}poisoning happens during gimplification. Thus it generates &var expressions
>> which verifier won't be happy about?
> 
> Sure, it uses &var.  The trick is that the addressable sub-pass then ignores
> the taking of the address just in ASAN_MARK, and if some var is determined
> to be addressable solely because of ASAN_MARK &var uses and no other reason,
> the ASAN_MARK would be dropped and variable rewritten into SSA form.
> 
>>> - optimization to remove ASAN_MARK unpoisoning at the start of the function
>>
>> As current implementation does not poison variables at the very beginning of
>> a functions (RTL stack frame emission), it won't be needed.
> 
> But you still ASAN_MARK unpoison the vars when they get into scope, right?
> And those can be removed if the optimizers could prove that the area has not
> been poisoned yet since the beginning of the function.
> 
>>> - optimization to remove ASAN_MARK poisoning at the end of function (and
>>>   remove epilogue unpoisoning)
>>> - optimization to remove ASAN_MARK unpoisoning followed by ASAN_MARK poisoning
>>>   or vice versa if there are no memory accesses in between
>>
>> Yep, both are not handled and are very similar from my perspective: pairing
>> poisoning and unpoisoning pair which are not interfered by a memory operation
>> in between (in dominator meaning of word).
>> I'm wondering whether can be done effectively as we would need to visit all BBs
>> in a dominance domain (get_all_dominated_blocks) and check for the memory operations.
>> One improvement can be set of BBs that do not have any memory operations (excluding
>> ASAN_CHECK, ASAN_MARK builtins), but it can be still expensive to simplify poisoning
>> for all local variables. Or am I wrong?
> 
> My memory is weak, but isn't this something the sanopt pass
> (sanopt_optimize) is already doing similarly for e.g. ASAN_CHECK, UBSAN_NULL
> and UBSAN_VPTR checks?  For ASAN_MARK, you actually don't care about any
> calls, those shouldn't unpoison or poison again the vars under compiler's
> back.
> 
>>> - try to improve the goto handling
>>
>> Works for me to be target for stage3.
> 
> Sure.
> 
>> 2016-09-27  Martin Liska  <mliska@suse.cz>
> 
> Likely newer date :)
> 
>> 	* c-common.c (warn_for_unused_label): Save all labels used
>> 	in goto or in &label;
> 
> &label.
> instead?
> 
>> +	      if (dump_file && (dump_flags & TDF_DETAILS))
>> +		{
>> +		  const char *n = DECL_NAME (decl)
>> +		    ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<unknown>";
> 
> Bad formatting.
> 
> 		  const char *n = (DECL_NAME (decl)
> 				   ? IDENTIFIER_POINTER (DECL_NAME (decl))
> 				   : "<unknown>");
> or
> 		  const char *n
> 		    = (DECL_NAME (decl)
> 		       ? IDENTIFIER_POINTER (DECL_NAME (decl)) : "<unknown>");
> 
>>  /* Return true if DECL should be guarded on the stack.  */
>>  
>>  static inline bool
>>  asan_protect_stack_decl (tree decl)
>>  {
>> -  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
>> +  return DECL_P (decl) && TREE_ADDRESSABLE (decl);
>>  }
> 
> Can you explain this change?  It won't affect just
> -fsanitize-use-after-scope, and goes in both directions, adds some
> decls that weren't previously protected and removes others that were
> previously protected.
> 
> Is the removal of !DECL_ARTIFICIAL so that you can use-after-scope
> verify the C++ TARGET_EXPR temporaries?  Do we need to stack protect
> those even for -fno-sanitize-use-after-scope?
> I'm not 100% sure if we keep TREE_ADDRESSABLE meaningful for variables that
> need to live in memory for other reasons until expansion.


Yes, it's connected to C++ temporaries that are DECL_ARTIFICIAL.

> 
> So, I wonder if we don't want && (TREE_ADDRESSABLE (decl) || !DECL_ARTIFICAL (decl))
> or just DECL_P (decl); or conditionalize something on
> -fsanitize-use-after-scope.  Perhaps the change is good as is, just want to
> point out that it affects also other -fsanitize=address modes in both
> ways (instruments something that hasn't been before, and stops instrumenting
> something that has been before).

Ok, I'm suggesting to use:

static inline bool
asan_protect_stack_decl (tree decl)
{
  return DECL_P (decl)
    && (!DECL_ARTIFICIAL (decl)
	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
}

Apart from that, all nits you pointed out are fixed and the patch should be
in an acceptable shape. I'll run regression tests and we can focus on the
sanopt side of the patch.

Martin

> 
>> @@ -1514,7 +1503,8 @@ defer_stack_allocation (tree var, bool toplevel)
>>    /* If stack protection is enabled, *all* stack variables must be deferred,
>>       so that we can re-order the strings to the top of the frame.
>>       Similarly for Address Sanitizer.  */
>> -  if (flag_stack_protect || asan_sanitize_stack_p ())
>> +  if (flag_stack_protect
>> +      || asan_sanitize_stack_p ())
>>      return true;
> 
> This hunk isn't needed, if all the conditions fit on one line,
> one line is better.
> 
> 	Jakub
> 


[-- Attachment #2: use-after-scope-addressable.patch --]
[-- Type: text/x-patch, Size: 2522 bytes --]

diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 135952b..2feb5a9 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -1550,6 +1550,23 @@ 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
+      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)
+    return true;
+
+  return false;
+}
+
 /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
 
 void
@@ -1575,17 +1592,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 +1864,16 @@ 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);
+			gsi_remove (&gsi, true);
+			continue;
+		      }
+		  }
 		for (i = 0; i < gimple_call_num_args (stmt); ++i)
 		  {
 		    tree *argp = gimple_call_arg_ptr (stmt, i);

[-- Attachment #3: 0001-Introduce-fsanitize-address-use-after-scope-v4.patch --]
[-- Type: text/x-patch, Size: 40838 bytes --]

From 40dbe847f3767b3c2e70620a4324838b85d0224a Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize-address-use-after-scope

gcc/c-family/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* c-warn.c (warn_for_unused_label): Save all labels used
	in goto or in &label.

gcc/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Move the enum to header file.
	(asan_init_shadow_ptr_types): Make type creation more generic.
	(shadow_mem_size): New function.
	(asan_emit_stack_protection): Use newly added ASAN_SHADOW_GRANULARITY.
	Rewritten stack unpoisoning code.
	(build_shadow_mem_access): Add new argument return_address.
	(instrument_derefs): Instrument local variables if use after scope
	sanitization is enabled.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	(asan_sanitize_stack_p): Moved from asan_sanitize_stack_p.
	* asan.h (enum asan_mark_flags): Moved here from asan.c
	(asan_protect_stack_decl): Protect all declaration that need
	to live in memory.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* cfgexpand.c (partition_stack_vars): Consider
	asan_sanitize_use_after_scope in condition.
	(expand_stack_vars): Likewise.
	* common.opt (-fsanitize-address-use-after-scope): New option.
	* doc/invoke.texi (use-after-scope-direct-emission-threshold):
	Explain the parameter.
	* flag-types.h (enum sanitize_code): Define SANITIZE_USE_AFTER_SCOPE.
	* gimplify.c (build_asan_poison_call_expr): New function.
	(asan_poison_variable): Likewise.
	(gimplify_bind_expr): Generate poisoning/unpoisoning for local
	variables that have address taken.
	(gimplify_decl_expr): Likewise.
	(gimplify_target_expr): Likewise for C++ temporaries.
	(sort_by_decl_uid): New function.
	(gimplify_expr): Unpoison all variables for a label we can jump
	from outside of a scope.
	(gimplify_function_tree): Clear asan_poisoned_variables.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def (ASAN_MARK): Declare.
	* opts.c (finish_options): Handle -fstack-reuse if
	-fsanitize-address-use-after-scope is enabled.
	(common_handle_option): Enable address sanitization if
	-fsanitize-address-use-after-scope is enabled.
	* params.def (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD):
	New parameter.
	* params.h: Likewise.
	* sancov.c (pass_sanopt::execute): Handle IFN_ASAN_MARK.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory functions.
---
 gcc/asan.c            | 287 +++++++++++++++++++++++++++++++++++++++++---------
 gcc/asan.h            |  66 ++++++++++--
 gcc/c-family/c-warn.c |   9 +-
 gcc/cfgexpand.c       |  18 +---
 gcc/common.opt        |   3 +
 gcc/doc/invoke.texi   |  15 ++-
 gcc/flag-types.h      |   2 +-
 gcc/gimplify.c        | 198 +++++++++++++++++++++++++++++++---
 gcc/internal-fn.c     |   9 ++
 gcc/internal-fn.def   |   1 +
 gcc/opts.c            |  27 ++++-
 gcc/params.def        |   6 ++
 gcc/params.h          |   2 +
 gcc/sanitizer.def     |   4 +
 gcc/sanopt.c          |   3 +
 15 files changed, 552 insertions(+), 98 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index c6d9240..95495d2 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -245,6 +245,13 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> *asan_handled_variables = NULL;
+
+hash_set <tree> *asan_used_labels = NULL;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -287,6 +294,14 @@ set_sanitized_sections (const char *sections)
     }
 }
 
+bool
+asan_sanitize_stack_p (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS)
+	  && ASAN_STACK
+	  && !asan_no_sanitize_address_p ());
+}
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -315,22 +330,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -933,12 +939,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1022,6 +1032,15 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+shadow_mem_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1047,7 +1066,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
-  HOST_WIDE_INT last_offset, last_size;
+  HOST_WIDE_INT last_offset;
   int l;
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
@@ -1205,10 +1224,10 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 				       (aoff - prev_offset)
 				       >> ASAN_SHADOW_SHIFT);
 	  prev_offset = aoff;
-	  for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT))
+	  for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
 	    if (aoff < offset)
 	      {
-		if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1)
+		if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
 		  shadow_bytes[i] = 0;
 		else
 		  shadow_bytes[i] = offset - aoff;
@@ -1282,35 +1301,66 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
 
-  prev_offset = base_offset;
+  /* Unpoison shadow memory of a stack at the very end of a function.
+     As we're poisoning stack variables at the end of their scope,
+     shadow memory must be properly unpoisoned here.  The easiest approach
+     would be to collect all variables that should not be unpoisoned and
+     we unpoison shadow memory of the whole stack except ranges
+     occupied by these variables.  */
   last_offset = base_offset;
-  last_size = 0;
-  for (l = length; l; l -= 2)
+  HOST_WIDE_INT current_offset = last_offset;
+  if (length)
     {
-      offset = base_offset + ((offsets[l - 1] - base_offset)
-			     & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-      if (last_offset + last_size != offset)
+      HOST_WIDE_INT var_end_offset = 0;
+      HOST_WIDE_INT stack_start = offsets[length - 1];
+      gcc_assert (last_offset == stack_start);
+
+      for (int l = length - 2; l > 0; l -= 2)
 	{
-	  shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				       (last_offset - prev_offset)
-				       >> ASAN_SHADOW_SHIFT);
-	  prev_offset = last_offset;
-	  asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
-	  last_offset = offset;
-	  last_size = 0;
+	  HOST_WIDE_INT var_offset = offsets[l];
+	  current_offset = var_offset;
+	  var_end_offset = offsets[l - 1];
+	  HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset,
+					     BITS_PER_UNIT);
+
+	  /* Should we unpoison the variable?  */
+	  if (asan_handled_variables != NULL
+	      && asan_handled_variables->contains (decl))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  const char *n = (DECL_NAME (decl)
+				   ? IDENTIFIER_POINTER (DECL_NAME (decl))
+				   : "<unknown>");
+		  fprintf (dump_file, "Unpoisoning shadow stack for variable: "
+			   "%s (%" PRId64 "B)\n", n,
+			   var_end_offset - var_offset);
+		}
+
+	      unsigned HOST_WIDE_INT s
+		= shadow_mem_size (current_offset - last_offset);
+	      asan_clear_shadow (shadow_mem, s);
+	      HOST_WIDE_INT shift
+		= shadow_mem_size (current_offset - last_offset + rounded_size);
+	      shadow_mem = adjust_address (shadow_mem, VOIDmode, shift);
+	      last_offset = var_offset + rounded_size;
+	      current_offset = last_offset;
+	    }
+
 	}
-      last_size += base_offset + ((offsets[l - 2] - base_offset)
-				  & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
-		   - offset;
-    }
-  if (last_size)
-    {
-      shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				   (last_offset - prev_offset)
-				   >> ASAN_SHADOW_SHIFT);
-      asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
+
+      /* Handle last redzone.  */
+      current_offset = offsets[0];
+      asan_clear_shadow (shadow_mem,
+			 shadow_mem_size (current_offset - last_offset));
     }
 
+  /* Clean-up set with instrumented stack variables.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1590,12 +1640,14 @@ insert_if_then_before_iter (gcond *cond,
   gsi_insert_after (&cond_insert_point, cond, GSI_NEW_STMT);
 }
 
-/* Build
-   (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
+/* Build (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().
+   If RETURN_ADDRESS is set to true, return memory location instread
+   of a value in the shadow memory.  */
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_address = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1618,11 +1670,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_address)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1826,7 +1882,9 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && (!asan_sanitize_use_after_scope ()
+		  || !TREE_ADDRESSABLE (inner)))
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2576,6 +2634,131 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Poison or unpoison (depending on IS_CLOBBER variable) shadow memory based
+   on SHADOW address.  Newly added statements will be added to ITER with
+   given location LOC.  We mark SIZE bytes in shadow memory, where
+   LAST_CHUNK_SIZE is greater than zero in situation where we are at the
+   end of a variable.  */
+
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 bool is_clobber, unsigned size,
+			 unsigned last_chunk_size)
+{
+  tree shadow_ptr_type;
+
+  switch (size)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < size; ++i)
+    {
+      unsigned char shadow_c = c;
+      if (i == size - 1 && last_chunk_size && !is_clobber)
+	shadow_c = last_chunk_size;
+      val |= (unsigned HOST_WIDE_INT) shadow_c << (BITS_PER_UNIT * i);
+    }
+
+  /* Handle last chunk in unpoisoning.  */
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (asan_handled_variables == NULL)
+    asan_handled_variables = new hash_set<tree> (16);
+  asan_handled_variables->add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  /* Generate direct emission if size_in_bytes is small.  */
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes);
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  unsigned HOST_WIDE_INT last_chunk_size = 0;
+	  unsigned HOST_WIDE_INT s = (offset + size) * ASAN_SHADOW_GRANULARITY;
+	  if (s > size_in_bytes)
+	    last_chunk_size = ASAN_SHADOW_GRANULARITY - (s - size_in_bytes);
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, is_clobber,
+				   size, last_chunk_size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..042af1f 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,9 +37,14 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of labels that are either used in a goto, or their address
+   has been taken.  */
+extern hash_set <tree> *asan_used_labels;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
+#define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT)
 
 /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
@@ -50,22 +56,32 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
-/* Return true if DECL should be guarded on the stack.  */
-
-static inline bool
-asan_protect_stack_decl (tree decl)
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
 {
-  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
-}
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
 
 /* Return the size of padding needed to insert after a protected
    decl of SIZE.  */
@@ -81,6 +97,8 @@ extern bool set_asan_shadow_offset (const char *);
 
 extern void set_sanitized_sections (const char *);
 
+extern bool asan_sanitize_stack_p (void);
+
 /* Return TRUE if builtin with given FCODE will be intercepted by
    libasan.  */
 
@@ -105,4 +123,30 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
+/* Return true if DECL should be guarded on the stack.  */
+
+static inline bool
+asan_protect_stack_decl (tree decl)
+{
+  return DECL_P (decl)
+    && (!DECL_ARTIFICIAL (decl)
+	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index 904f6d3..18ee247 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "diagnostic.h"
 #include "intl.h"
-
+#include "asan.h"
 
 /* Print a warning if a constant expression had overflow in folding.
    Invoke this function on every expression that the language
@@ -1627,6 +1627,13 @@ warn_for_unused_label (tree label)
       else
 	warning (OPT_Wunused_label, "label %q+D declared but not defined", label);
     }
+  else if (asan_sanitize_use_after_scope ())
+    {
+      if (asan_used_labels == NULL)
+	asan_used_labels = new hash_set<tree> (16);
+
+      asan_used_labels->add (label);
+    }
 }
 
 /* Warn for division by zero according to the value of DIVISOR.  LOC
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 430ad38..7ffb558 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -868,18 +868,6 @@ union_stack_vars (size_t a, size_t b)
     }
 }
 
-/* Return true if the current function should have its stack frame
-   protected by address sanitizer.  */
-
-static inline bool
-asan_sanitize_stack_p (void)
-{
-  return ((flag_sanitize & SANITIZE_ADDRESS)
-	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
-}
-
 /* A subroutine of expand_used_vars.  Binpack the variables into
    partitions constrained by the interference graph.  The overall
    algorithm used is as follows:
@@ -941,7 +929,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1128,7 +1117,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
 	  base = virtual_stack_vars_rtx;
-	  if (asan_sanitize_stack_p () && pred)
+	  if ((asan_sanitize_stack_p ())
+	      && pred)
 	    {
 	      HOST_WIDE_INT prev_offset
 		= align_base (frame_offset,
diff --git a/gcc/common.opt b/gcc/common.opt
index 1872d51..333d305 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -967,6 +967,9 @@ fsanitize-recover
 Common Report
 This switch is deprecated; use -fsanitize-recover= instead.
 
+fsanitize-address-use-after-scope
+Common Driver Report Var(flag_sanitize_address_use_after_scope) Init(0)
+
 fsanitize-undefined-trap-on-error
 Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
 Use trap instead of a library function for undefined behavior sanitization.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5ccd424..be8340b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10216,6 +10216,10 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item use-after-scope-direct-emission-threshold
+If size of a local variables in bytes is smaller of equal to this number,
+direct instruction emission is utilized to poison and unpoison local variables.
+
 @item chkp-max-ctor-size
 Static constructors generated by Pointer Bounds Checker may become very
 large and significantly increase compile time at optimization level
@@ -10426,6 +10430,7 @@ thread-safe code.
 Enable AddressSanitizer, a fast memory error detector.
 Memory access instructions are instrumented to detect
 out-of-bounds and use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/sanitizers/wiki/AddressSanitizer} for
 more details.  The run-time behavior can be influenced using the
 @env{ASAN_OPTIONS} environment variable.  When set to @code{help=1},
@@ -10437,6 +10442,7 @@ The option can't be combined with @option{-fsanitize=thread}.
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
 @item -fsanitize=thread
@@ -10636,8 +10642,8 @@ except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
 @option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
 @option{-fsanitize=bounds-strict},
 @option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
-For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
-for which this feature is experimental.
+For these sanitizers error recovery is turned on by default,
+except @option{-fsanitize=address}, for which this feature is experimental.
 @option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
 accepted, the former enables recovery for all sanitizers that support it,
 the latter disables recovery for all sanitizers that support it.
@@ -10659,6 +10665,11 @@ Similarly @option{-fno-sanitize-recover} is equivalent to
 -fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
 @end smallexample
 
+@item -fsanitize-address-use-after-scope
+@opindex fsanitize-address-use-after-scope
+Enable sanitization of local variables to detect use-after-scope bugs.
+The option sets @option{-fstack-reuse} to @samp{none}.
+
 @item -fsanitize-undefined-trap-on-error
 @opindex fsanitize-undefined-trap-on-error
 The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 816df6b..8cd6fc3 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -237,7 +237,7 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT,
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 5da1725..c8cfe67 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> *asan_poisoned_variables = NULL;
 
 enum gimplify_omp_var_data
 {
@@ -1088,6 +1092,79 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */
+
+static tree
+build_asan_poison_call_expr (tree decl)
+{
+  /* Do not poison variables that have size equal to zero.  */
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  if (zerop (unit_size))
+    return NULL_TREE;
+
+  tree base = build_fold_addr_expr (decl);
+
+  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
+				       void_type_node, 3,
+				       build_int_cst (integer_type_node,
+						      ASAN_MARK_CLOBBER),
+				       base, unit_size);
+}
+
+/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
+   on POISON flag, shadow memory of a DECL variable.  The call will be
+   put on location identified by IT iterator, where BEFORE flag drives
+   position where the stmt will be put.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
+		      bool before)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  /* Do not poison variables that have size equal to zero.  */
+  if (zerop (unit_size))
+    return;
+
+  /* It's necessary to have all stack variables aligned to ASAN granularity
+     bytes.  */
+  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+
+  if (before)
+    gsi_insert_before (it, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (it, g, GSI_NEW_STMT);
+}
+
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  gimple_stmt_iterator it = gsi_last (*seq_p);
+  bool before = false;
+
+  if (gsi_end_p (it))
+    before = true;
+
+  asan_poison_variable (decl, poison, &it, before);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1231,6 +1308,13 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 		}
 	    }
 	}
+
+      if (asan_poisoned_variables != NULL
+	  && asan_poisoned_variables->contains (t))
+	{
+	  asan_poisoned_variables->remove (t);
+	  asan_poison_variable (t, true, &cleanup);
+	}
     }
 
   if (ret_clauses)
@@ -1475,13 +1559,27 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
   if (VAR_P (decl) && !DECL_EXTERNAL (decl))
     {
       tree init = DECL_INITIAL (decl);
+      bool is_vla = false;
 
       if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
 	  || (!TREE_STATIC (decl)
 	      && flag_stack_check == GENERIC_STACK_CHECK
 	      && compare_tree_int (DECL_SIZE_UNIT (decl),
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
-	gimplify_vla_decl (decl, seq_p);
+	{
+	  gimplify_vla_decl (decl, seq_p);
+	  is_vla = true;
+	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && !is_vla
+	  && TREE_ADDRESSABLE (decl)
+	  && !TREE_STATIC (decl))
+	{
+	  asan_poisoned_variables->add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
 
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
@@ -6165,6 +6263,9 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   tree init = TARGET_EXPR_INITIAL (targ);
   enum gimplify_status ret;
 
+  bool unpoison_empty_seq = false;
+  gimple_stmt_iterator unpoison_it;
+
   if (init)
     {
       tree cleanup = NULL_TREE;
@@ -6178,7 +6279,14 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  gimplify_vla_decl (temp, pre_p);
 	}
       else
-	gimple_add_tmp_var (temp);
+	{
+	  /* Save location where we need to place unpoisoning.  It's possible
+	     that a variable will be converted to needs_to_live_in_memory.  */
+	  unpoison_it = gsi_last (*pre_p);
+	  unpoison_empty_seq = gsi_end_p (unpoison_it);
+
+	  gimple_add_tmp_var (temp);
+	}
 
       /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
 	 expression is supposed to initialize the slot.  */
@@ -6214,20 +6322,34 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       /* Add a clobber for the temporary going out of scope, like
 	 gimplify_bind_expr.  */
       if (gimplify_ctxp->in_cleanup_point_expr
-	  && needs_to_live_in_memory (temp)
-	  && flag_stack_reuse == SR_ALL)
-	{
-	  tree clobber = build_constructor (TREE_TYPE (temp),
-					    NULL);
-	  TREE_THIS_VOLATILE (clobber) = true;
-	  clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
-	  if (cleanup)
-	    cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
-			      clobber);
-	  else
-	    cleanup = clobber;
-	}
+	  && needs_to_live_in_memory (temp))
+	{
+	  if (flag_stack_reuse == SR_ALL)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (temp),
+						NULL);
+	      TREE_THIS_VOLATILE (clobber) = true;
+	      clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+	      if (cleanup)
+		cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+				  clobber);
+	      else
+		cleanup = clobber;
+	    }
+	  if (asan_sanitize_use_after_scope ())
+	    {
+	      tree asan_cleanup = build_asan_poison_call_expr (temp);
+	      if (asan_cleanup)
+		{
+		  if (unpoison_empty_seq)
+		    unpoison_it = gsi_start (*pre_p);
 
+		  asan_poison_variable (temp, false, &unpoison_it,
+					unpoison_empty_seq);
+		  gimple_push_cleanup (temp, asan_cleanup, false, pre_p);
+		}
+	    }
+	}
       if (cleanup)
 	gimple_push_cleanup (temp, cleanup, false, pre_p);
 
@@ -10734,6 +10856,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10825,6 +10966,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   location_t saved_location;
   enum gimplify_status ret;
   gimple_stmt_iterator pre_last_gsi, post_last_gsi;
+  tree label;
 
   save_expr = *expr_p;
   if (save_expr == NULL_TREE)
@@ -11240,6 +11382,29 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	case LABEL_EXPR:
 	  ret = gimplify_label_expr (expr_p, pre_p);
+	  label = LABEL_EXPR_LABEL (*expr_p);
+	  gcc_assert (decl_function_context (label) == current_function_decl);
+
+	  /* If the label is used in a goto statement, or address of the label
+	     is taken, we need to unpoison all variables that were seen so far.
+	     Doing so would prevent us from reporting a false positives.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && asan_used_labels != NULL
+	      && asan_used_labels->contains (label))
+	    {
+	      unsigned c = asan_poisoned_variables->elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set<tree>::iterator it
+		   = asan_poisoned_variables->begin ();
+		   it != asan_poisoned_variables->end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -12337,7 +12502,10 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
+  delete asan_poisoned_variables;
+  asan_poisoned_variables = NULL;
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 4477697..1fd988c 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -237,6 +237,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 28863df..92a4912 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts.c b/gcc/opts.c
index 6b34473..708a641 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -978,6 +978,25 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+     enabled.  */
+  if (opts->x_flag_sanitize
+      && !opts_set->x_flag_sanitize_address_use_after_scope)
+    opts->x_flag_sanitize_address_use_after_scope = true;
+
+  /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
+     is enabled.  */
+  if (opts->x_flag_sanitize_address_use_after_scope)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize-address-use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1451,8 +1470,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
-  SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
-  SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
+  SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
   SANITIZER_OPT (leak, SANITIZE_LEAK, false),
@@ -1780,6 +1799,10 @@ common_handle_option (struct gcc_options *opts,
       /* Deferred.  */
       break;
 
+    case OPT_fsanitize_address_use_after_scope:
+      opts->x_flag_sanitize_address_use_after_scope = value;
+      break;
+
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
diff --git a/gcc/params.def b/gcc/params.def
index 79b7dd4..f3c5c5c 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1156,6 +1156,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+	 256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 97c8d56..0a2905c 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -244,5 +244,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 8a6fbe9..320e14e 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -732,6 +732,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-27 14:40                   ` Martin Liška
@ 2016-10-27 17:24                     ` Jakub Jelinek
  2016-11-01 14:48                       ` Martin Liška
  2016-11-01 14:54                       ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-10-27 17:24 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Thu, Oct 27, 2016 at 04:40:30PM +0200, Martin Liška wrote:
> On 10/21/2016 04:26 PM, Jakub Jelinek wrote:
> > On Wed, Oct 12, 2016 at 04:07:53PM +0200, Martin Liška wrote:
> >>> Ok, first let me list some needed follow-ups that don't need to be handled
> >>> right away:
> >>> - r237814-like changes for ASAN_MARK
> 
> I've spent quite some on that and that's what I begin (use-after-scope-addressable.patch).
> Problem is that as I ignore all ASAN_MARK internal fns, the code does not detect having address
> taken in:
> 
> _2 = MEM[(char *)&my_char + 8B];
> 
>   char *ptr;
>   {
>     char my_char[9];
>     ptr = &my_char[0];
>   }
> 
>   return *(ptr+8);
> 
> and thus the code in tree-ssa.c (maybe_optimize_var) sets TREE_ADDRESSABLE (var) = 0.

Perhaps we should do that only if the var's type is_gimple_reg_type,
then we'd rewrite that into SSA at that time, right?  So, in theory if we
turned the ASAN_MARK poisoning call into another internal function
(var_5 = ASAN_POISON ()) and then after converting it into SSA looked at
all the uses of such an lhs and perhaps at sanopt part or when marked all
the use places with a library call that would complain at runtime?
Or turn those back at sanopt time into addressable memory loads which would
be poisoned or similar?  Or alternatively, immediately before turning
variables addressable just because of ASAN_MARK into non-addressable use
the same framework into-ssa uses to find out if there are any poisoned
accesses, and just not optimize it in that case.
Anyway, this can be done incrementally.

> Second question I have is whether we want to handle just TREE_ADDRESSABLE stuff during gimplification?
> Basically in a way that the current patch is doing?

How could variables that aren't TREE_ADDRESSABLE during gimplification be
accessed out of scope?

> +/* Return true if DECL should be guarded on the stack.  */
> +
> +static inline bool
> +asan_protect_stack_decl (tree decl)
> +{
> +  return DECL_P (decl)
> +    && (!DECL_ARTIFICIAL (decl)
> +	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));

Bad formatting.  Should be:

  return (DECL_P (decl)
	  && (!DECL_ARTIFICIAL (decl)
	      || (asan_sanitize_use_after_scope ()
		  && TREE_ADDRESSABLE (decl))));

Ok for trunk with that change.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-27 17:24                     ` Jakub Jelinek
@ 2016-11-01 14:48                       ` Martin Liška
  2016-11-01 14:54                         ` Jakub Jelinek
  2016-11-01 14:54                       ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-01 14:48 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 335 bytes --]

On 10/27/2016 07:23 PM, Jakub Jelinek wrote:
> Ok for trunk with that change.
> 
> 	Jakub

Hello.

I'll commit the patch as soon as following patch would be accepted. The patch
fixes false positives when running asan-bootstrap.

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

Ready to be installed?
Martin

[-- Attachment #2: 0001-Fix-ASAN-bootstrap-uninitialized-warning.patch --]
[-- Type: text/x-patch, Size: 3862 bytes --]

From b62e4d7ffe659ec338ef83e84ccb572a07264283 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 20 Sep 2016 10:31:25 +0200
Subject: [PATCH 1/4] Fix ASAN bootstrap uninitialized warning.

gcc/ChangeLog:

2016-09-26  Martin Liska  <mliska@suse.cz>

	* ipa-devirt.c (record_targets_from_bases): Initialize a
	variable.
	* omp-low.c (lower_omp_target): Remove a variable from
	scope defined by a switch statement.
	* tree-dump.c (dequeue_and_dump): Likewise.

gcc/java/ChangeLog:

2016-09-26  Martin Liska  <mliska@suse.cz>

	* mangle.c (mangle_type): Remove a variable from
	scope defined by a switch statement.
---
 gcc/ipa-devirt.c |  2 +-
 gcc/omp-low.c    | 11 ++++-------
 gcc/tree-dump.c  |  8 +++-----
 3 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 49e2195..5c0ae72 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -2862,7 +2862,7 @@ record_targets_from_bases (tree otr_type,
 {
   while (true)
     {
-      HOST_WIDE_INT pos, size;
+      HOST_WIDE_INT pos = 0, size;
       tree base_binfo;
       tree fld;
 
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index e5b9e4c..62c9e5c 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -15803,11 +15803,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   push_gimplify_context ();
   fplist = NULL;
 
+  tree var, x;
   for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
     switch (OMP_CLAUSE_CODE (c))
       {
-	tree var, x;
-
       default:
 	break;
       case OMP_CLAUSE_MAP:
@@ -16066,12 +16065,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
       vec_alloc (vkind, map_cnt);
       unsigned int map_idx = 0;
 
+      tree ovar, nc, s, purpose, var, x, type;
+      unsigned int talign;
       for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
 	switch (OMP_CLAUSE_CODE (c))
 	  {
-	    tree ovar, nc, s, purpose, var, x, type;
-	    unsigned int talign;
-
 	  default:
 	    break;
 
@@ -16442,10 +16440,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
   if (offloaded || data_region)
     {
       tree prev = NULL_TREE;
+      tree var, x;
       for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
 	switch (OMP_CLAUSE_CODE (c))
 	  {
-	    tree var, x;
 	  default:
 	    break;
 	  case OMP_CLAUSE_FIRSTPRIVATE:
@@ -16594,7 +16592,6 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
       for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
 	switch (OMP_CLAUSE_CODE (c))
 	  {
-	    tree var;
 	  default:
 	    break;
 	  case OMP_CLAUSE_MAP:
diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c
index df47181..89f72a0 100644
--- a/gcc/tree-dump.c
+++ b/gcc/tree-dump.c
@@ -420,8 +420,6 @@ dequeue_and_dump (dump_info_p di)
   /* Now handle the various kinds of nodes.  */
   switch (code)
     {
-      int i;
-
     case IDENTIFIER_NODE:
       dump_string_field (di, "strg", IDENTIFIER_POINTER (t));
       dump_int (di, "lngt", IDENTIFIER_LENGTH (t));
@@ -435,6 +433,7 @@ dequeue_and_dump (dump_info_p di)
 
     case STATEMENT_LIST:
       {
+	int i;
 	tree_stmt_iterator it;
 	for (i = 0, it = tsi_start (t); !tsi_end_p (it); tsi_next (&it), i++)
 	  {
@@ -447,7 +446,7 @@ dequeue_and_dump (dump_info_p di)
 
     case TREE_VEC:
       dump_int (di, "lngt", TREE_VEC_LENGTH (t));
-      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
+      for (int i = 0; i < TREE_VEC_LENGTH (t); ++i)
 	{
 	  char buffer[32];
 	  sprintf (buffer, "%u", i);
@@ -707,9 +706,8 @@ dequeue_and_dump (dump_info_p di)
       break;
     case OMP_CLAUSE:
       {
-	int i;
 	fprintf (di->stream, "%s\n", omp_clause_code_name[OMP_CLAUSE_CODE (t)]);
-	for (i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
+	for (int i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
 	  dump_child ("op: ", OMP_CLAUSE_OPERAND (t, i));
       }
       break;
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-10-27 17:24                     ` Jakub Jelinek
  2016-11-01 14:48                       ` Martin Liška
@ 2016-11-01 14:54                       ` Martin Liška
  2016-11-01 15:12                         ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-01 14:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2937 bytes --]

On 10/27/2016 07:23 PM, Jakub Jelinek wrote:
> On Thu, Oct 27, 2016 at 04:40:30PM +0200, Martin Liška wrote:
>> On 10/21/2016 04:26 PM, Jakub Jelinek wrote:
>>> On Wed, Oct 12, 2016 at 04:07:53PM +0200, Martin Liška wrote:
>>>>> Ok, first let me list some needed follow-ups that don't need to be handled
>>>>> right away:
>>>>> - r237814-like changes for ASAN_MARK
>>
>> I've spent quite some on that and that's what I begin (use-after-scope-addressable.patch).
>> Problem is that as I ignore all ASAN_MARK internal fns, the code does not detect having address
>> taken in:
>>
>> _2 = MEM[(char *)&my_char + 8B];
>>
>>   char *ptr;
>>   {
>>     char my_char[9];
>>     ptr = &my_char[0];
>>   }
>>
>>   return *(ptr+8);
>>
>> and thus the code in tree-ssa.c (maybe_optimize_var) sets TREE_ADDRESSABLE (var) = 0.
> 
> Perhaps we should do that only if the var's type is_gimple_reg_type,
> then we'd rewrite that into SSA at that time, right?  So, in theory if we
> turned the ASAN_MARK poisoning call into another internal function
> (var_5 = ASAN_POISON ()) and then after converting it into SSA looked at
> all the uses of such an lhs and perhaps at sanopt part or when marked all
> the use places with a library call that would complain at runtime?
> Or turn those back at sanopt time into addressable memory loads which would
> be poisoned or similar?  Or alternatively, immediately before turning
> variables addressable just because of ASAN_MARK into non-addressable use
> the same framework into-ssa uses to find out if there are any poisoned
> accesses, and just not optimize it in that case.
> Anyway, this can be done incrementally.

I've done a patch candidate (not tested yet) which is capable of ASAN_MARK removal
for local variables that can be rewritten into SSA. This is done by running execute_update_addresses_taken
after we create ASAN_CHECK internal fns and skipping all ASAN_MARK for having address taken considerations.
This removes significant number of ASAN_MARK fns in tramp3d (due to C++ temporaries).

> 
>> Second question I have is whether we want to handle just TREE_ADDRESSABLE stuff during gimplification?
>> Basically in a way that the current patch is doing?
> 
> How could variables that aren't TREE_ADDRESSABLE during gimplification be
> accessed out of scope?

Yep, TREE_ADDRESSABLE guard does what it should do.

I'm going to test the patch which can be installed incrementally.

Martin

> 
>> +/* Return true if DECL should be guarded on the stack.  */
>> +
>> +static inline bool
>> +asan_protect_stack_decl (tree decl)
>> +{
>> +  return DECL_P (decl)
>> +    && (!DECL_ARTIFICIAL (decl)
>> +	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
> 
> Bad formatting.  Should be:
> 
>   return (DECL_P (decl)
> 	  && (!DECL_ARTIFICIAL (decl)
> 	      || (asan_sanitize_use_after_scope ()
> 		  && TREE_ADDRESSABLE (decl))));
> 
> Ok for trunk with that change.
> 
> 	Jakub
> 


[-- Attachment #2: 0004-Use-after-scope-do-not-mark-variables-that-are-no-lo.patch --]
[-- Type: text/x-patch, Size: 8071 bytes --]

From cf860324da41244745f04a16b184fabe343ac5d9 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 1 Nov 2016 11:21:20 +0100
Subject: [PATCH 4/4] Use-after-scope: do not mark variables that are no longer
 addressable

gcc/ChangeLog:

2016-11-01  Martin Liska  <mliska@suse.cz>

	* asan.c (asan_instrument): Call
	execute_update_addresses_taken_asan_sanitize right after
	sanitization.
	* tree-ssa.c (maybe_optimize_var): Mark all variables set to
	non-addressable.
	(is_asan_mark_p): New function.
	(execute_update_addresses_taken): Likewise.
	(execute_update_addresses_taken_asan_sanitize): Likewise.
	* tree-ssa.h (execute_update_addresses_taken_asan_sanitize):
	Declare new function.
---
 gcc/asan.c     |  4 +++
 gcc/tree-ssa.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++-----------
 gcc/tree-ssa.h |  1 +
 3 files changed, 84 insertions(+), 17 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 95495d2..5cb37c8 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -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-ssa.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -2973,6 +2974,9 @@ asan_instrument (void)
   if (shadow_ptr_types[0] == NULL_TREE)
     asan_init_shadow_ptr_types ();
   transform_statements ();
+
+  if (optimize)
+    execute_update_addresses_taken_asan_sanitize ();
   return 0;
 }
 
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 135952b..0633b21 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;
@@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
 
 static void
 maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
-		    bitmap suitable_for_renaming)
+		    bitmap suitable_for_renaming, bitmap marked_nonaddressable)
 {
   /* Global Variables, result decls cannot be changed.  */
   if (is_global_var (var)
@@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
 	  || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
     {
       TREE_ADDRESSABLE (var) = 0;
+      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));
       if (is_gimple_reg (var))
 	bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
       if (dump_file)
@@ -1550,20 +1552,43 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
     }
 }
 
-/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
+/* Return true when STMT is ASAN mark where second argument is an address
+   of a local variable.  */
 
-void
-execute_update_addresses_taken (void)
+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
+      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)
+    return true;
+
+  return false;
+}
+
+/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.
+   If SANITIZE_ASAN_MARK is set to true, sanitize also ASAN_MARK built-ins.  */
+
+
+static void
+execute_update_addresses_taken (bool sanitize_asan_mark = false)
 {
   basic_block bb;
   bitmap addresses_taken = BITMAP_ALLOC (NULL);
   bitmap not_reg_needs = BITMAP_ALLOC (NULL);
   bitmap suitable_for_renaming = BITMAP_ALLOC (NULL);
+  bitmap marked_nonaddressable = BITMAP_ALLOC (NULL);
   tree var;
   unsigned i;
 
   timevar_push (TV_ADDRESS_TAKEN);
 
+  if (dump_file)
+    fprintf (dump_file, "call execute_update_addresses_taken\n");
+
   /* Collect into ADDRESSES_TAKEN all variables whose address is taken within
      the function body.  */
   FOR_EACH_BB_FN (bb, cfun)
@@ -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 (sanitize_asan_mark && is_asan_mark_p (stmt))
+		;
+	      else
+		gimple_ior_addresses_taken (addresses_taken, stmt);
 	    }
 	  else
 	    /* Note all addresses taken by the stmt.  */
@@ -1675,15 +1706,17 @@ execute_update_addresses_taken (void)
      for -g vs. -g0.  */
   for (var = DECL_ARGUMENTS (cfun->decl); var; var = DECL_CHAIN (var))
     maybe_optimize_var (var, addresses_taken, not_reg_needs,
-			suitable_for_renaming);
+			suitable_for_renaming, marked_nonaddressable);
 
   FOR_EACH_VEC_SAFE_ELT (cfun->local_decls, i, var)
     maybe_optimize_var (var, addresses_taken, not_reg_needs,
-			suitable_for_renaming);
+			suitable_for_renaming, marked_nonaddressable);
 
   /* Operand caches need to be recomputed for operands referencing the updated
      variables and operands need to be rewritten to expose bare symbols.  */
-  if (!bitmap_empty_p (suitable_for_renaming))
+  if (!bitmap_empty_p (suitable_for_renaming)
+      || (asan_sanitize_use_after_scope ()
+	  && !bitmap_empty_p (marked_nonaddressable)))
     {
       FOR_EACH_BB_FN (bb, cfun)
 	for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
@@ -1841,6 +1874,17 @@ execute_update_addresses_taken (void)
 			continue;
 		      }
 		  }
+		else if (sanitize_asan_mark && 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))
+			|| bitmap_bit_p (marked_nonaddressable, DECL_UID (var)))
+		      {
+			unlink_stmt_vdef (stmt);
+			gsi_remove (&gsi, true);
+			continue;
+		      }
+		  }
 		for (i = 0; i < gimple_call_num_args (stmt); ++i)
 		  {
 		    tree *argp = gimple_call_arg_ptr (stmt, i);
@@ -1896,9 +1940,27 @@ execute_update_addresses_taken (void)
   BITMAP_FREE (not_reg_needs);
   BITMAP_FREE (addresses_taken);
   BITMAP_FREE (suitable_for_renaming);
+  BITMAP_FREE (marked_nonaddressable);
   timevar_pop (TV_ADDRESS_TAKEN);
 }
 
+/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
+
+void
+execute_update_addresses_taken (void)
+{
+  execute_update_addresses_taken (false);
+}
+
+/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables
+   and sanitize ASAN_MARK built-ins.  */
+
+void
+execute_update_addresses_taken_asan_sanitize (void)
+{
+  execute_update_addresses_taken (true);
+}
+
 namespace {
 
 const pass_data pass_data_update_address_taken =
diff --git a/gcc/tree-ssa.h b/gcc/tree-ssa.h
index 37ad7ae..912d144 100644
--- a/gcc/tree-ssa.h
+++ b/gcc/tree-ssa.h
@@ -53,6 +53,7 @@ extern tree tree_ssa_strip_useless_type_conversions (tree);
 extern bool ssa_undefined_value_p (tree, bool = true);
 extern bool gimple_uses_undefined_value_p (gimple *);
 extern void execute_update_addresses_taken (void);
+extern void execute_update_addresses_taken_asan_sanitize (void);
 
 /* Given an edge_var_map V, return the PHI arg definition.  */
 
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 14:48                       ` Martin Liška
@ 2016-11-01 14:54                         ` Jakub Jelinek
  2016-11-01 15:01                           ` Martin Liška
  2016-11-02  9:36                           ` Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-01 14:54 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Nov 01, 2016 at 03:47:54PM +0100, Martin Liška wrote:
> On 10/27/2016 07:23 PM, Jakub Jelinek wrote:
> > Ok for trunk with that change.
> > 
> > 	Jakub
> 
> Hello.
> 
> I'll commit the patch as soon as following patch would be accepted. The patch
> fixes false positives when running asan-bootstrap.

What kind of false positives it is for each case?  Is it with normal
asan-bootstrap (without your -fsanitize-use-after-scope changes), or
only with those changes, or only with those changes and
-fsanitize-use-after-scope used during bootstrap?

> >From b62e4d7ffe659ec338ef83e84ccb572a07264283 Mon Sep 17 00:00:00 2001
> From: marxin <mliska@suse.cz>
> Date: Tue, 20 Sep 2016 10:31:25 +0200
> Subject: [PATCH 1/4] Fix ASAN bootstrap uninitialized warning.
> 
> gcc/ChangeLog:
> 
> 2016-09-26  Martin Liska  <mliska@suse.cz>
> 
> 	* ipa-devirt.c (record_targets_from_bases): Initialize a
> 	variable.
> 	* omp-low.c (lower_omp_target): Remove a variable from
> 	scope defined by a switch statement.
> 	* tree-dump.c (dequeue_and_dump): Likewise.
> 
> gcc/java/ChangeLog:
> 
> 2016-09-26  Martin Liska  <mliska@suse.cz>
> 
> 	* mangle.c (mangle_type): Remove a variable from
> 	scope defined by a switch statement.
> ---
>  gcc/ipa-devirt.c |  2 +-
>  gcc/omp-low.c    | 11 ++++-------
>  gcc/tree-dump.c  |  8 +++-----
>  3 files changed, 8 insertions(+), 13 deletions(-)
> 
> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
> index 49e2195..5c0ae72 100644
> --- a/gcc/ipa-devirt.c
> +++ b/gcc/ipa-devirt.c
> @@ -2862,7 +2862,7 @@ record_targets_from_bases (tree otr_type,
>  {
>    while (true)
>      {
> -      HOST_WIDE_INT pos, size;
> +      HOST_WIDE_INT pos = 0, size;
>        tree base_binfo;
>        tree fld;
>  
> diff --git a/gcc/omp-low.c b/gcc/omp-low.c
> index e5b9e4c..62c9e5c 100644
> --- a/gcc/omp-low.c
> +++ b/gcc/omp-low.c
> @@ -15803,11 +15803,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>    push_gimplify_context ();
>    fplist = NULL;
>  
> +  tree var, x;
>    for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>      switch (OMP_CLAUSE_CODE (c))
>        {
> -	tree var, x;
> -
>        default:
>  	break;
>        case OMP_CLAUSE_MAP:
> @@ -16066,12 +16065,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>        vec_alloc (vkind, map_cnt);
>        unsigned int map_idx = 0;
>  
> +      tree ovar, nc, s, purpose, var, x, type;
> +      unsigned int talign;
>        for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>  	switch (OMP_CLAUSE_CODE (c))
>  	  {
> -	    tree ovar, nc, s, purpose, var, x, type;
> -	    unsigned int talign;
> -
>  	  default:
>  	    break;
>  
> @@ -16442,10 +16440,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>    if (offloaded || data_region)
>      {
>        tree prev = NULL_TREE;
> +      tree var, x;
>        for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>  	switch (OMP_CLAUSE_CODE (c))
>  	  {
> -	    tree var, x;
>  	  default:
>  	    break;
>  	  case OMP_CLAUSE_FIRSTPRIVATE:
> @@ -16594,7 +16592,6 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>        for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
>  	switch (OMP_CLAUSE_CODE (c))
>  	  {
> -	    tree var;
>  	  default:
>  	    break;
>  	  case OMP_CLAUSE_MAP:
> diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c
> index df47181..89f72a0 100644
> --- a/gcc/tree-dump.c
> +++ b/gcc/tree-dump.c
> @@ -420,8 +420,6 @@ dequeue_and_dump (dump_info_p di)
>    /* Now handle the various kinds of nodes.  */
>    switch (code)
>      {
> -      int i;
> -
>      case IDENTIFIER_NODE:
>        dump_string_field (di, "strg", IDENTIFIER_POINTER (t));
>        dump_int (di, "lngt", IDENTIFIER_LENGTH (t));
> @@ -435,6 +433,7 @@ dequeue_and_dump (dump_info_p di)
>  
>      case STATEMENT_LIST:
>        {
> +	int i;
>  	tree_stmt_iterator it;
>  	for (i = 0, it = tsi_start (t); !tsi_end_p (it); tsi_next (&it), i++)
>  	  {
> @@ -447,7 +446,7 @@ dequeue_and_dump (dump_info_p di)
>  
>      case TREE_VEC:
>        dump_int (di, "lngt", TREE_VEC_LENGTH (t));
> -      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
> +      for (int i = 0; i < TREE_VEC_LENGTH (t); ++i)
>  	{
>  	  char buffer[32];
>  	  sprintf (buffer, "%u", i);
> @@ -707,9 +706,8 @@ dequeue_and_dump (dump_info_p di)
>        break;
>      case OMP_CLAUSE:
>        {
> -	int i;
>  	fprintf (di->stream, "%s\n", omp_clause_code_name[OMP_CLAUSE_CODE (t)]);
> -	for (i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
> +	for (int i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
>  	  dump_child ("op: ", OMP_CLAUSE_OPERAND (t, i));
>        }
>        break;
> -- 
> 2.10.1
> 


	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 14:54                         ` Jakub Jelinek
@ 2016-11-01 15:01                           ` Martin Liška
  2016-11-02  9:36                           ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-01 15:01 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On 11/01/2016 03:53 PM, Jakub Jelinek wrote:
> On Tue, Nov 01, 2016 at 03:47:54PM +0100, Martin Liška wrote:
>> On 10/27/2016 07:23 PM, Jakub Jelinek wrote:
>>> Ok for trunk with that change.
>>>
>>> 	Jakub
>>
>> Hello.
>>
>> I'll commit the patch as soon as following patch would be accepted. The patch
>> fixes false positives when running asan-bootstrap.
> 
> What kind of false positives it is for each case?  Is it with normal
> asan-bootstrap (without your -fsanitize-use-after-scope changes), or
> only with those changes, or only with those changes and
> -fsanitize-use-after-scope used during bootstrap?

It's only with those changes as -fsanitize-address-use-after-scope is enabled by
default with -fsanitize=address. Current bootstrap-asan works fine. I'll re-trigger
the bootstrap again, but IIRC the main culprit was ASAN_MARK poisoning done at switch labels
(which should be partially fixed with a newer version of the patch).

Martin

> 
>> >From b62e4d7ffe659ec338ef83e84ccb572a07264283 Mon Sep 17 00:00:00 2001
>> From: marxin <mliska@suse.cz>
>> Date: Tue, 20 Sep 2016 10:31:25 +0200
>> Subject: [PATCH 1/4] Fix ASAN bootstrap uninitialized warning.
>>
>> gcc/ChangeLog:
>>
>> 2016-09-26  Martin Liska  <mliska@suse.cz>
>>
>> 	* ipa-devirt.c (record_targets_from_bases): Initialize a
>> 	variable.
>> 	* omp-low.c (lower_omp_target): Remove a variable from
>> 	scope defined by a switch statement.
>> 	* tree-dump.c (dequeue_and_dump): Likewise.
>>
>> gcc/java/ChangeLog:
>>
>> 2016-09-26  Martin Liska  <mliska@suse.cz>
>>
>> 	* mangle.c (mangle_type): Remove a variable from
>> 	scope defined by a switch statement.
>> ---
>>  gcc/ipa-devirt.c |  2 +-
>>  gcc/omp-low.c    | 11 ++++-------
>>  gcc/tree-dump.c  |  8 +++-----
>>  3 files changed, 8 insertions(+), 13 deletions(-)
>>
>> diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
>> index 49e2195..5c0ae72 100644
>> --- a/gcc/ipa-devirt.c
>> +++ b/gcc/ipa-devirt.c
>> @@ -2862,7 +2862,7 @@ record_targets_from_bases (tree otr_type,
>>  {
>>    while (true)
>>      {
>> -      HOST_WIDE_INT pos, size;
>> +      HOST_WIDE_INT pos = 0, size;
>>        tree base_binfo;
>>        tree fld;
>>  
>> diff --git a/gcc/omp-low.c b/gcc/omp-low.c
>> index e5b9e4c..62c9e5c 100644
>> --- a/gcc/omp-low.c
>> +++ b/gcc/omp-low.c
>> @@ -15803,11 +15803,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>>    push_gimplify_context ();
>>    fplist = NULL;
>>  
>> +  tree var, x;
>>    for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>>      switch (OMP_CLAUSE_CODE (c))
>>        {
>> -	tree var, x;
>> -
>>        default:
>>  	break;
>>        case OMP_CLAUSE_MAP:
>> @@ -16066,12 +16065,11 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>>        vec_alloc (vkind, map_cnt);
>>        unsigned int map_idx = 0;
>>  
>> +      tree ovar, nc, s, purpose, var, x, type;
>> +      unsigned int talign;
>>        for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>>  	switch (OMP_CLAUSE_CODE (c))
>>  	  {
>> -	    tree ovar, nc, s, purpose, var, x, type;
>> -	    unsigned int talign;
>> -
>>  	  default:
>>  	    break;
>>  
>> @@ -16442,10 +16440,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>>    if (offloaded || data_region)
>>      {
>>        tree prev = NULL_TREE;
>> +      tree var, x;
>>        for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c))
>>  	switch (OMP_CLAUSE_CODE (c))
>>  	  {
>> -	    tree var, x;
>>  	  default:
>>  	    break;
>>  	  case OMP_CLAUSE_FIRSTPRIVATE:
>> @@ -16594,7 +16592,6 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>>        for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
>>  	switch (OMP_CLAUSE_CODE (c))
>>  	  {
>> -	    tree var;
>>  	  default:
>>  	    break;
>>  	  case OMP_CLAUSE_MAP:
>> diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c
>> index df47181..89f72a0 100644
>> --- a/gcc/tree-dump.c
>> +++ b/gcc/tree-dump.c
>> @@ -420,8 +420,6 @@ dequeue_and_dump (dump_info_p di)
>>    /* Now handle the various kinds of nodes.  */
>>    switch (code)
>>      {
>> -      int i;
>> -
>>      case IDENTIFIER_NODE:
>>        dump_string_field (di, "strg", IDENTIFIER_POINTER (t));
>>        dump_int (di, "lngt", IDENTIFIER_LENGTH (t));
>> @@ -435,6 +433,7 @@ dequeue_and_dump (dump_info_p di)
>>  
>>      case STATEMENT_LIST:
>>        {
>> +	int i;
>>  	tree_stmt_iterator it;
>>  	for (i = 0, it = tsi_start (t); !tsi_end_p (it); tsi_next (&it), i++)
>>  	  {
>> @@ -447,7 +446,7 @@ dequeue_and_dump (dump_info_p di)
>>  
>>      case TREE_VEC:
>>        dump_int (di, "lngt", TREE_VEC_LENGTH (t));
>> -      for (i = 0; i < TREE_VEC_LENGTH (t); ++i)
>> +      for (int i = 0; i < TREE_VEC_LENGTH (t); ++i)
>>  	{
>>  	  char buffer[32];
>>  	  sprintf (buffer, "%u", i);
>> @@ -707,9 +706,8 @@ dequeue_and_dump (dump_info_p di)
>>        break;
>>      case OMP_CLAUSE:
>>        {
>> -	int i;
>>  	fprintf (di->stream, "%s\n", omp_clause_code_name[OMP_CLAUSE_CODE (t)]);
>> -	for (i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
>> +	for (int i = 0; i < omp_clause_num_ops[OMP_CLAUSE_CODE (t)]; i++)
>>  	  dump_child ("op: ", OMP_CLAUSE_OPERAND (t, i));
>>        }
>>        break;
>> -- 
>> 2.10.1
>>
> 
> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 14:54                       ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
@ 2016-11-01 15:12                         ` Jakub Jelinek
  2016-11-02  9:40                           ` Richard Biener
  2016-11-02  9:52                           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-01 15:12 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Tue, Nov 01, 2016 at 03:53:46PM +0100, Martin Liška wrote:
> @@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
>  
>  static void
>  maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
> -		    bitmap suitable_for_renaming)
> +		    bitmap suitable_for_renaming, bitmap marked_nonaddressable)
>  {
>    /* Global Variables, result decls cannot be changed.  */
>    if (is_global_var (var)
> @@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>  	  || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
>      {
>        TREE_ADDRESSABLE (var) = 0;
> +      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));

Why do you need the marked_nonaddressable bitmap?

>        if (is_gimple_reg (var))
>  	bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
>        if (dump_file)
> @@ -1550,20 +1552,43 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>      }
>  }
>  
> -/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
> +/* Return true when STMT is ASAN mark where second argument is an address
> +   of a local variable.  */
>  
> -void
> -execute_update_addresses_taken (void)
> +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
> +      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)

Just check here if dropping TREE_ADDRESSABLE from the VAR (use VAR_P btw)
would turn it into is_gimple_reg), and don't return true if not.

> +    return true;
> +
> +  return false;
> +}
> +
> +/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.
> +   If SANITIZE_ASAN_MARK is set to true, sanitize also ASAN_MARK built-ins.  */
> +
> +
> +static void
> +execute_update_addresses_taken (bool sanitize_asan_mark = false)

I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
set during the asan pass and kept on until end of compilation of that
function.  That means even if a var only addressable because of ASAN_MARK is
discovered after this pass we'd still be able to rewrite it into SSA.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 14:54                         ` Jakub Jelinek
  2016-11-01 15:01                           ` Martin Liška
@ 2016-11-02  9:36                           ` Martin Liška
  2016-11-02  9:59                             ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-02  9:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On 11/01/2016 03:53 PM, Jakub Jelinek wrote:
> What kind of false positives it is for each case?  Is it with normal
> asan-bootstrap (without your -fsanitize-use-after-scope changes), or
> only with those changes, or only with those changes and
> -fsanitize-use-after-scope used during bootstrap?

Ok, the situation is simpler than I thought:

#include <stdio.h>

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

  switch (argc)
    {
      int a;

    case 1:
      break;

    default:
      ptr = &a;
      break;
    }

  fprintf (stderr, "v: %d\n", *ptr);
  return 0;
}

Which is gimplified as:

    int * ptr;

    switch (argc) <default: <D.2575>, case 1: <D.2573>>
    {
      int a;

      try
        {
          ASAN_MARK (2, &a, 4);
          <D.2573>:
          goto <D.2574>;
          <D.2575>:
          ptr = &a;
          goto <D.2574>;
        }
      finally
        {
          ASAN_MARK (1, &a, 4);
        }
    }
    <D.2574>:
    _1 = *ptr;
    stderr.0_2 = stderr;
    fprintf (stderr.0_2, "v: %d\n", _1);
    D.2577 = 0;
    return D.2577;
  }
  D.2577 = 0;
  return D.2577;

and thus we get:
/tmp/switch-case.c:9:11: warning: statement will never be executed [-Wswitch-unreachable]
       int a;

I'm wondering where properly fix that, we can either find all these ASAN_MARKs in gimplify_switch_expr
and distribute it to all labels (which are gimplified). Or we can put such variables to asan_poisoned_variables
if we have information that we're gimplifing statements before a first label. Do we know that from gimple context?
If so, these variables will be unpoisoned at the very beginning of each label and the ASAN_MARK call in between
switch statement and a first label can be removed.

Thoughts?
Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 15:12                         ` Jakub Jelinek
@ 2016-11-02  9:40                           ` Richard Biener
  2016-11-02  9:44                             ` Martin Liška
  2016-11-02  9:52                             ` Jakub Jelinek
  2016-11-02  9:52                           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
  1 sibling, 2 replies; 111+ messages in thread
From: Richard Biener @ 2016-11-02  9:40 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On Tue, Nov 1, 2016 at 4:12 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Tue, Nov 01, 2016 at 03:53:46PM +0100, Martin Liška wrote:
>> @@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
>>
>>  static void
>>  maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>> -                 bitmap suitable_for_renaming)
>> +                 bitmap suitable_for_renaming, bitmap marked_nonaddressable)
>>  {
>>    /* Global Variables, result decls cannot be changed.  */
>>    if (is_global_var (var)
>> @@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>         || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
>>      {
>>        TREE_ADDRESSABLE (var) = 0;
>> +      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));
>
> Why do you need the marked_nonaddressable bitmap?
>
>>        if (is_gimple_reg (var))
>>       bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
>>        if (dump_file)
>> @@ -1550,20 +1552,43 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>      }
>>  }
>>
>> -/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
>> +/* Return true when STMT is ASAN mark where second argument is an address
>> +   of a local variable.  */
>>
>> -void
>> -execute_update_addresses_taken (void)
>> +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
>> +      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)
>
> Just check here if dropping TREE_ADDRESSABLE from the VAR (use VAR_P btw)
> would turn it into is_gimple_reg), and don't return true if not.
>
>> +    return true;
>> +
>> +  return false;
>> +}
>> +
>> +/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.
>> +   If SANITIZE_ASAN_MARK is set to true, sanitize also ASAN_MARK built-ins.  */
>> +
>> +
>> +static void
>> +execute_update_addresses_taken (bool sanitize_asan_mark = false)
>
> I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
> set during the asan pass and kept on until end of compilation of that
> function.  That means even if a var only addressable because of ASAN_MARK is
> discovered after this pass we'd still be able to rewrite it into SSA.

Note that being TREE_ADDRESSABLE also has effects on alias analysis
(didn't follow the patches to see whether you handle ASAN_MARK specially
in points-to analysis and/or alias analysis).

Generally in update-address-taken you can handle ASAN_MARK similar to
MEM_REF (and drop it in the rewrite phase?).

As said, I didnt look at the patches and just came by here seeing
tree-ssa.c pieces...

Richard.

>         Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:40                           ` Richard Biener
@ 2016-11-02  9:44                             ` Martin Liška
  2016-11-02  9:52                             ` Jakub Jelinek
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-02  9:44 UTC (permalink / raw)
  To: Richard Biener, Jakub Jelinek; +Cc: GCC Patches

On 11/02/2016 10:40 AM, Richard Biener wrote:
> On Tue, Nov 1, 2016 at 4:12 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Tue, Nov 01, 2016 at 03:53:46PM +0100, Martin Liška wrote:
>>> @@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
>>>
>>>  static void
>>>  maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>> -                 bitmap suitable_for_renaming)
>>> +                 bitmap suitable_for_renaming, bitmap marked_nonaddressable)
>>>  {
>>>    /* Global Variables, result decls cannot be changed.  */
>>>    if (is_global_var (var)
>>> @@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>>         || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
>>>      {
>>>        TREE_ADDRESSABLE (var) = 0;
>>> +      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));
>>
>> Why do you need the marked_nonaddressable bitmap?
>>
>>>        if (is_gimple_reg (var))
>>>       bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
>>>        if (dump_file)
>>> @@ -1550,20 +1552,43 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>>      }
>>>  }
>>>
>>> -/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
>>> +/* Return true when STMT is ASAN mark where second argument is an address
>>> +   of a local variable.  */
>>>
>>> -void
>>> -execute_update_addresses_taken (void)
>>> +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
>>> +      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)
>>
>> Just check here if dropping TREE_ADDRESSABLE from the VAR (use VAR_P btw)
>> would turn it into is_gimple_reg), and don't return true if not.
>>
>>> +    return true;
>>> +
>>> +  return false;
>>> +}
>>> +
>>> +/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.
>>> +   If SANITIZE_ASAN_MARK is set to true, sanitize also ASAN_MARK built-ins.  */
>>> +
>>> +
>>> +static void
>>> +execute_update_addresses_taken (bool sanitize_asan_mark = false)
>>
>> I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
>> set during the asan pass and kept on until end of compilation of that
>> function.  That means even if a var only addressable because of ASAN_MARK is
>> discovered after this pass we'd still be able to rewrite it into SSA.
> 
> Note that being TREE_ADDRESSABLE also has effects on alias analysis
> (didn't follow the patches to see whether you handle ASAN_MARK specially
> in points-to analysis and/or alias analysis).

Currently all manipulation with shadow memory is done via a pointer type
which has created a separate aliasing set:

static void
asan_init_shadow_ptr_types (void)
{
  asan_shadow_set = new_alias_set ();
  tree types[3] = { signed_char_type_node, short_integer_type_node,
		    integer_type_node };

  for (unsigned i = 0; i < 3; i++)
    {
      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
    }
...

Martin

> 
> Generally in update-address-taken you can handle ASAN_MARK similar to
> MEM_REF (and drop it in the rewrite phase?).
> 
> As said, I didnt look at the patches and just came by here seeing
> tree-ssa.c pieces...
> 
> Richard.
> 
>>         Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-01 15:12                         ` Jakub Jelinek
  2016-11-02  9:40                           ` Richard Biener
@ 2016-11-02  9:52                           ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-02  9:52 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2854 bytes --]

On 11/01/2016 04:12 PM, Jakub Jelinek wrote:
> On Tue, Nov 01, 2016 at 03:53:46PM +0100, Martin Liška wrote:
>> @@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
>>  
>>  static void
>>  maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>> -		    bitmap suitable_for_renaming)
>> +		    bitmap suitable_for_renaming, bitmap marked_nonaddressable)
>>  {
>>    /* Global Variables, result decls cannot be changed.  */
>>    if (is_global_var (var)
>> @@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>  	  || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
>>      {
>>        TREE_ADDRESSABLE (var) = 0;
>> +      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));
> 
> Why do you need the marked_nonaddressable bitmap?

Because the later loop (which visits every gimple statement) iterates only
if there's an entry in suitable_for_renaming.

> 
>>        if (is_gimple_reg (var))
>>  	bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
>>        if (dump_file)
>> @@ -1550,20 +1552,43 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
>>      }
>>  }
>>  
>> -/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
>> +/* Return true when STMT is ASAN mark where second argument is an address
>> +   of a local variable.  */
>>  
>> -void
>> -execute_update_addresses_taken (void)
>> +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
>> +      && TREE_CODE (TREE_OPERAND (addr, 0)) == VAR_DECL)
> 
> Just check here if dropping TREE_ADDRESSABLE from the VAR (use VAR_P btw)
> would turn it into is_gimple_reg), and don't return true if not.

Well, the predicate is called once before maybe_optimize_var, thus I need to have
it conservative and not consider TREE_ADDRESSABLE flag. Having argument would work
for that?

> 
>> +    return true;
>> +
>> +  return false;
>> +}
>> +
>> +/* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.
>> +   If SANITIZE_ASAN_MARK is set to true, sanitize also ASAN_MARK built-ins.  */
>> +
>> +
>> +static void
>> +execute_update_addresses_taken (bool sanitize_asan_mark = false)
> 
> I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
> set during the asan pass and kept on until end of compilation of that
> function.  That means even if a var only addressable because of ASAN_MARK is
> discovered after this pass we'd still be able to rewrite it into SSA.

It's doable (please see attached patch) and also nicer. However, one would need to
extend curr_properties to long type as we already use 16 existing values.

Martin

> 
> 	Jakub
> 


[-- Attachment #2: 0001-Use-after-scope-do-not-mark-variables-that-are-no-lo.patch --]
[-- Type: text/x-patch, Size: 7849 bytes --]

From ad5f68a010674118fac7ca8b6953f7b99fd3c2a8 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 1 Nov 2016 11:21:20 +0100
Subject: [PATCH] Use-after-scope: do not mark variables that are no longer
 addressable

gcc/ChangeLog:

2016-11-02  Martin Liska  <mliska@suse.cz>

	* asan.c: Update properties_provided and todo_flags_finish.
	* function.h (struct GTY): Change int to long as there's not
	enough space for a new value.
	* tree-pass.h: Define PROP_asan_check_done.
	* tree-ssa.c (maybe_optimize_var): Add new argument.
	(is_asan_mark_p): New function.
	(execute_update_addresses_taken): Handle ASAN_MARK internal fns.
---
 gcc/asan.c      |  5 +++--
 gcc/function.h  |  2 +-
 gcc/tree-pass.h |  1 +
 gcc/tree-ssa.c  | 69 +++++++++++++++++++++++++++++++++++++++++++++------------
 4 files changed, 60 insertions(+), 17 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 95495d2..94ee877 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -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-ssa.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -2993,10 +2994,10 @@ const pass_data pass_data_asan =
   OPTGROUP_NONE, /* optinfo_flags */
   TV_NONE, /* tv_id */
   ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
-  0, /* properties_provided */
+  PROP_asan_check_done, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
-  TODO_update_ssa, /* todo_flags_finish */
+  TODO_update_ssa | TODO_update_address_taken, /* todo_flags_finish */
 };
 
 class pass_asan : public gimple_opt_pass
diff --git a/gcc/function.h b/gcc/function.h
index e854c7f..5600488 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -289,7 +289,7 @@ struct GTY(()) function {
   location_t function_end_locus;
 
   /* Properties used by the pass manager.  */
-  unsigned int curr_properties;
+  unsigned long curr_properties;
   unsigned int last_verified;
 
   /* Non-null if the function does something that would prevent it from
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index da9ba13..ea43593 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -222,6 +222,7 @@ protected:
 						   of math functions; the
 						   current choices have
 						   been optimized.  */
+#define PROP_asan_check_done	(1 << 16)	/* ASAN check is emitted */
 
 #define PROP_trees \
   (PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_gimple_lomp)
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 135952b..1492f47 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;
@@ -1504,7 +1505,7 @@ non_rewritable_lvalue_p (tree lhs)
 
 static void
 maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
-		    bitmap suitable_for_renaming)
+		    bitmap suitable_for_renaming, bitmap marked_nonaddressable)
 {
   /* Global Variables, result decls cannot be changed.  */
   if (is_global_var (var)
@@ -1522,6 +1523,7 @@ maybe_optimize_var (tree var, bitmap addresses_taken, bitmap not_reg_needs,
 	  || !bitmap_bit_p (not_reg_needs, DECL_UID (var))))
     {
       TREE_ADDRESSABLE (var) = 0;
+      bitmap_set_bit (marked_nonaddressable, DECL_UID (var));
       if (is_gimple_reg (var))
 	bitmap_set_bit (suitable_for_renaming, DECL_UID (var));
       if (dump_file)
@@ -1550,6 +1552,22 @@ 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)))
+    return true;
+
+  return false;
+}
+
 /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
 
 void
@@ -1559,9 +1577,12 @@ execute_update_addresses_taken (void)
   bitmap addresses_taken = BITMAP_ALLOC (NULL);
   bitmap not_reg_needs = BITMAP_ALLOC (NULL);
   bitmap suitable_for_renaming = BITMAP_ALLOC (NULL);
+  bitmap marked_nonaddressable = BITMAP_ALLOC (NULL);
   tree var;
   unsigned i;
 
+  bool sanitize_asan_mark = cfun->curr_properties & PROP_asan_check_done;
+
   timevar_push (TV_ADDRESS_TAKEN);
 
   /* Collect into ADDRESSES_TAKEN all variables whose address is taken within
@@ -1575,17 +1596,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 (sanitize_asan_mark && is_asan_mark_p (stmt))
+		;
+	      else
+		gimple_ior_addresses_taken (addresses_taken, stmt);
 	    }
 	  else
 	    /* Note all addresses taken by the stmt.  */
@@ -1675,15 +1702,17 @@ execute_update_addresses_taken (void)
      for -g vs. -g0.  */
   for (var = DECL_ARGUMENTS (cfun->decl); var; var = DECL_CHAIN (var))
     maybe_optimize_var (var, addresses_taken, not_reg_needs,
-			suitable_for_renaming);
+			suitable_for_renaming, marked_nonaddressable);
 
   FOR_EACH_VEC_SAFE_ELT (cfun->local_decls, i, var)
     maybe_optimize_var (var, addresses_taken, not_reg_needs,
-			suitable_for_renaming);
+			suitable_for_renaming, marked_nonaddressable);
 
   /* Operand caches need to be recomputed for operands referencing the updated
      variables and operands need to be rewritten to expose bare symbols.  */
-  if (!bitmap_empty_p (suitable_for_renaming))
+  if (!bitmap_empty_p (suitable_for_renaming)
+      || (asan_sanitize_use_after_scope ()
+	  && !bitmap_empty_p (marked_nonaddressable)))
     {
       FOR_EACH_BB_FN (bb, cfun)
 	for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
@@ -1841,6 +1870,17 @@ execute_update_addresses_taken (void)
 			continue;
 		      }
 		  }
+		else if (sanitize_asan_mark && 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))
+			|| bitmap_bit_p (marked_nonaddressable, DECL_UID (var)))
+		      {
+			unlink_stmt_vdef (stmt);
+			gsi_remove (&gsi, true);
+			continue;
+		      }
+		  }
 		for (i = 0; i < gimple_call_num_args (stmt); ++i)
 		  {
 		    tree *argp = gimple_call_arg_ptr (stmt, i);
@@ -1896,6 +1936,7 @@ execute_update_addresses_taken (void)
   BITMAP_FREE (not_reg_needs);
   BITMAP_FREE (addresses_taken);
   BITMAP_FREE (suitable_for_renaming);
+  BITMAP_FREE (marked_nonaddressable);
   timevar_pop (TV_ADDRESS_TAKEN);
 }
 
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:40                           ` Richard Biener
  2016-11-02  9:44                             ` Martin Liška
@ 2016-11-02  9:52                             ` Jakub Jelinek
  2016-11-02 12:36                               ` Richard Biener
  1 sibling, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02  9:52 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Liška, GCC Patches

On Wed, Nov 02, 2016 at 10:40:35AM +0100, Richard Biener wrote:
> > I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
> > set during the asan pass and kept on until end of compilation of that
> > function.  That means even if a var only addressable because of ASAN_MARK is
> > discovered after this pass we'd still be able to rewrite it into SSA.
> 
> Note that being TREE_ADDRESSABLE also has effects on alias analysis
> (didn't follow the patches to see whether you handle ASAN_MARK specially
> in points-to analysis and/or alias analysis).
> 
> Generally in update-address-taken you can handle ASAN_MARK similar to
> MEM_REF (and drop it in the rewrite phase?).

That is the intent, but we can't do that before the asan pass, because
otherwise as Martin explained we don't diagnose at runtime bugs where
a variable is used outside of its scope only through a MEM_REF.
So we need to wait for asan pass to actually add a real builtin call that
takes the address in that case.  Except now I really don't see how that
can work for the case where the var is used only properly when it is in the
scope, because the asan pass will still see those being addressable.

Unless I'm missing something we either need to perform further analysis
during the addressable subpass - this variable could be made
non-addressable, but is used in ASAN_MARK, would if we rewrote it into SSA
form there be any SSA uses of the poisoning ASAN_MARK?  If yes, keep it
addressable, otherwise rewrite into SSA.
Or, just rewrite it into SSA always, but turn the poisoning ASAN_MARK into
some new internal ECF_CONST call var_5 = ASAN_POISON (); and if we have any
uses of those, rewrite it back into addressable immediately or later or
something.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:36                           ` Martin Liška
@ 2016-11-02  9:59                             ` Jakub Jelinek
  2016-11-02 10:09                               ` Martin Liška
  2016-11-02 10:11                               ` Jakub Jelinek
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02  9:59 UTC (permalink / raw)
  To: Martin Liška, Marek Polacek; +Cc: GCC Patches

On Wed, Nov 02, 2016 at 10:36:44AM +0100, Martin Liška wrote:
> On 11/01/2016 03:53 PM, Jakub Jelinek wrote:
> > What kind of false positives it is for each case?  Is it with normal
> > asan-bootstrap (without your -fsanitize-use-after-scope changes), or
> > only with those changes, or only with those changes and
> > -fsanitize-use-after-scope used during bootstrap?
> 
> Ok, the situation is simpler than I thought:

CCing also Marek.
> 
> #include <stdio.h>
> 
> int main(int argc, char **argv)
> {
>   int *ptr;
> 
>   switch (argc)
>     {
>       int a;
> 
>     case 1:
>       break;
> 
>     default:
>       ptr = &a;
>       break;
>     }
> 
>   fprintf (stderr, "v: %d\n", *ptr);
>   return 0;
> }
> 
> Which is gimplified as:
> 
>     int * ptr;
> 
>     switch (argc) <default: <D.2575>, case 1: <D.2573>>
>     {
>       int a;
> 
>       try
>         {
>           ASAN_MARK (2, &a, 4);
>           <D.2573>:
>           goto <D.2574>;
>           <D.2575>:
>           ptr = &a;
>           goto <D.2574>;
>         }
>       finally
>         {
>           ASAN_MARK (1, &a, 4);
>         }
>     }
>     <D.2574>:
>     _1 = *ptr;
>     stderr.0_2 = stderr;
>     fprintf (stderr.0_2, "v: %d\n", _1);
>     D.2577 = 0;
>     return D.2577;
>   }
>   D.2577 = 0;
>   return D.2577;
> 
> and thus we get:
> /tmp/switch-case.c:9:11: warning: statement will never be executed [-Wswitch-unreachable]
>        int a;
> 
> I'm wondering where properly fix that, we can either find all these ASAN_MARKs in gimplify_switch_expr
> and distribute it to all labels (which are gimplified). Or we can put such variables to asan_poisoned_variables
> if we have information that we're gimplifing statements before a first label. Do we know that from gimple context?
> If so, these variables will be unpoisoned at the very beginning of each label and the ASAN_MARK call in between
> switch statement and a first label can be removed.

Wouldn't it be easiest if -Wswitch-unreachable warning just ignored
the ASAN_MARK internal calls altogether?
Do you emit there any other statements, or just ASAN_MARK and nothing else?

Shouldn't there be also ASAN_MARK on the implicit gotos from the switch
statement?  Otherwise, consider this being done in a loop, after the first
iteration you ASAN_MARK (1, &a, 4) (i.e. poison), then you iterate say with
args 1 and have in case 1: a = 0;, won't that trigger runtime error?

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:59                             ` Jakub Jelinek
@ 2016-11-02 10:09                               ` Martin Liška
  2016-11-02 10:11                               ` Jakub Jelinek
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-02 10:09 UTC (permalink / raw)
  To: Jakub Jelinek, Marek Polacek; +Cc: GCC Patches

On 11/02/2016 10:59 AM, Jakub Jelinek wrote:
> On Wed, Nov 02, 2016 at 10:36:44AM +0100, Martin Liška wrote:
>> On 11/01/2016 03:53 PM, Jakub Jelinek wrote:
>>> What kind of false positives it is for each case?  Is it with normal
>>> asan-bootstrap (without your -fsanitize-use-after-scope changes), or
>>> only with those changes, or only with those changes and
>>> -fsanitize-use-after-scope used during bootstrap?
>>
>> Ok, the situation is simpler than I thought:
> 
> CCing also Marek.
>>
>> #include <stdio.h>
>>
>> int main(int argc, char **argv)
>> {
>>   int *ptr;
>>
>>   switch (argc)
>>     {
>>       int a;
>>
>>     case 1:
>>       break;
>>
>>     default:
>>       ptr = &a;
>>       break;
>>     }
>>
>>   fprintf (stderr, "v: %d\n", *ptr);
>>   return 0;
>> }
>>
>> Which is gimplified as:
>>
>>     int * ptr;
>>
>>     switch (argc) <default: <D.2575>, case 1: <D.2573>>
>>     {
>>       int a;
>>
>>       try
>>         {
>>           ASAN_MARK (2, &a, 4);
>>           <D.2573>:
>>           goto <D.2574>;
>>           <D.2575>:
>>           ptr = &a;
>>           goto <D.2574>;
>>         }
>>       finally
>>         {
>>           ASAN_MARK (1, &a, 4);
>>         }
>>     }
>>     <D.2574>:
>>     _1 = *ptr;
>>     stderr.0_2 = stderr;
>>     fprintf (stderr.0_2, "v: %d\n", _1);
>>     D.2577 = 0;
>>     return D.2577;
>>   }
>>   D.2577 = 0;
>>   return D.2577;
>>
>> and thus we get:
>> /tmp/switch-case.c:9:11: warning: statement will never be executed [-Wswitch-unreachable]
>>        int a;
>>
>> I'm wondering where properly fix that, we can either find all these ASAN_MARKs in gimplify_switch_expr
>> and distribute it to all labels (which are gimplified). Or we can put such variables to asan_poisoned_variables
>> if we have information that we're gimplifing statements before a first label. Do we know that from gimple context?
>> If so, these variables will be unpoisoned at the very beginning of each label and the ASAN_MARK call in between
>> switch statement and a first label can be removed.
> 
> Wouldn't it be easiest if -Wswitch-unreachable warning just ignored
> the ASAN_MARK internal calls altogether?
> Do you emit there any other statements, or just ASAN_MARK and nothing else?

Yep, skipping warning can be done easily, however gimplified code is wrong as
un-poisoning is not done for variable (even for a valid program).

> 
> Shouldn't there be also ASAN_MARK on the implicit gotos from the switch
> statement?  Otherwise, consider this being done in a loop, after the first
> iteration you ASAN_MARK (1, &a, 4) (i.e. poison), then you iterate say with
> args 1 and have in case 1: a = 0;, won't that trigger runtime error?

Hopefully having the un-poisoning call be encapsulated in finally block would properly
clean up the variable. Or am I wrong?

Martin

> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:59                             ` Jakub Jelinek
  2016-11-02 10:09                               ` Martin Liška
@ 2016-11-02 10:11                               ` Jakub Jelinek
  2016-11-02 14:20                                 ` Marek Polacek
  1 sibling, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02 10:11 UTC (permalink / raw)
  To: Martin Liška, Marek Polacek; +Cc: GCC Patches

On Wed, Nov 02, 2016 at 10:59:26AM +0100, Jakub Jelinek wrote:
> > Which is gimplified as:
> > 
> >     int * ptr;
> > 
> >     switch (argc) <default: <D.2575>, case 1: <D.2573>>
> >     {
> >       int a;
> > 
> >       try
> >         {
> >           ASAN_MARK (2, &a, 4);
> >           <D.2573>:
> >           goto <D.2574>;
> >           <D.2575>:
> >           ptr = &a;
> >           goto <D.2574>;
> >         }
> >       finally
> >         {
> >           ASAN_MARK (1, &a, 4);
> >         }

> Shouldn't there be also ASAN_MARK on the implicit gotos from the switch
> statement?  Otherwise, consider this being done in a loop, after the first
> iteration you ASAN_MARK (1, &a, 4) (i.e. poison), then you iterate say with
> args 1 and have in case 1: a = 0;, won't that trigger runtime error?

Wonder if for the variables declared inside of switch body, because we don't
care about uses before scope, it wouldn't be more efficient to arrange for
int *p, *q, *r;
switch (x)
  {
    int a;
  case 1:
    p = &a;
    a = 5;
    break;
    int b;
  case 2:
    int c;
    q = &b;
    r = &c;
    b = 3;
    c = 4;
    break;
  }
to effectively ASAN_MARK (2, &a, 4); ASAN_MARK (2, &b, 4); ASAN_MARK (2, &c, 4);
before the GIMPLE_SWITCH, instead of unpoisoning them on every case label
where they might be in scope.  Though, of course, at least until lower pass
that is quite ugly, because it would refer to "not yet declared" variables.
Perhaps we'd need to move the ASAN_MARK and GIMPLE_SWITCH (but of course not
the expression evaluation of the switch control expression) inside of the
switches' GIMPLE_BIND.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02  9:52                             ` Jakub Jelinek
@ 2016-11-02 12:36                               ` Richard Biener
  2016-11-02 12:56                                 ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Richard Biener @ 2016-11-02 12:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On Wed, Nov 2, 2016 at 10:52 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Nov 02, 2016 at 10:40:35AM +0100, Richard Biener wrote:
>> > I wonder if the sanitize_asan_mark wouldn't better be some PROP_* property
>> > set during the asan pass and kept on until end of compilation of that
>> > function.  That means even if a var only addressable because of ASAN_MARK is
>> > discovered after this pass we'd still be able to rewrite it into SSA.
>>
>> Note that being TREE_ADDRESSABLE also has effects on alias analysis
>> (didn't follow the patches to see whether you handle ASAN_MARK specially
>> in points-to analysis and/or alias analysis).
>>
>> Generally in update-address-taken you can handle ASAN_MARK similar to
>> MEM_REF (and drop it in the rewrite phase?).
>
> That is the intent, but we can't do that before the asan pass, because
> otherwise as Martin explained we don't diagnose at runtime bugs where
> a variable is used outside of its scope only through a MEM_REF.
> So we need to wait for asan pass to actually add a real builtin call that
> takes the address in that case.  Except now I really don't see how that
> can work for the case where the var is used only properly when it is in the
> scope, because the asan pass will still see those being addressable.
>
> Unless I'm missing something we either need to perform further analysis
> during the addressable subpass - this variable could be made
> non-addressable, but is used in ASAN_MARK, would if we rewrote it into SSA
> form there be any SSA uses of the poisoning ASAN_MARK?  If yes, keep it
> addressable, otherwise rewrite into SSA.
> Or, just rewrite it into SSA always, but turn the poisoning ASAN_MARK into
> some new internal ECF_CONST call var_5 = ASAN_POISON (); and if we have any
> uses of those, rewrite it back into addressable immediately or later or
> something.

Or just give up optimizing (asan has a penalty anyway)?  Or
re-structure ASAN_POISON ()
similar to clobbers:

  var = ASAN_POISION ();

that avoids the address-taking and thus should be less heavy-weight on
optimizations.

Richard.

>
>         Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 12:36                               ` Richard Biener
@ 2016-11-02 12:56                                 ` Jakub Jelinek
  2016-11-02 12:59                                   ` Richard Biener
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02 12:56 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Liška, GCC Patches

On Wed, Nov 02, 2016 at 01:36:31PM +0100, Richard Biener wrote:
> > Unless I'm missing something we either need to perform further analysis
> > during the addressable subpass - this variable could be made
> > non-addressable, but is used in ASAN_MARK, would if we rewrote it into SSA
> > form there be any SSA uses of the poisoning ASAN_MARK?  If yes, keep it
> > addressable, otherwise rewrite into SSA.
> > Or, just rewrite it into SSA always, but turn the poisoning ASAN_MARK into
> > some new internal ECF_CONST call var_5 = ASAN_POISON (); and if we have any
> > uses of those, rewrite it back into addressable immediately or later or
> > something.
> 
> Or just give up optimizing (asan has a penalty anyway)?  Or

Well, asan has a penalty and -fsanitize-use-after-scope even bigger penalty,
but the point is to make that penalty bearable.

> re-structure ASAN_POISON ()
> similar to clobbers:
> 
>   var = ASAN_POISION ();
> 
> that avoids the address-taking and thus should be less heavy-weight on
> optimizations.

Yeah, that is what I meant.  The issue is how to report uses of such
SSA_NAME when there is no memory.  So, either we'd need a special runtime
library entrypoint that would report uses after scope even when there is no
underlying memory, or we'd need to force it at asan pass time into memory again.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 12:56                                 ` Jakub Jelinek
@ 2016-11-02 12:59                                   ` Richard Biener
  2016-11-02 13:06                                     ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Richard Biener @ 2016-11-02 12:59 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On Wed, Nov 2, 2016 at 1:56 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Nov 02, 2016 at 01:36:31PM +0100, Richard Biener wrote:
>> > Unless I'm missing something we either need to perform further analysis
>> > during the addressable subpass - this variable could be made
>> > non-addressable, but is used in ASAN_MARK, would if we rewrote it into SSA
>> > form there be any SSA uses of the poisoning ASAN_MARK?  If yes, keep it
>> > addressable, otherwise rewrite into SSA.
>> > Or, just rewrite it into SSA always, but turn the poisoning ASAN_MARK into
>> > some new internal ECF_CONST call var_5 = ASAN_POISON (); and if we have any
>> > uses of those, rewrite it back into addressable immediately or later or
>> > something.
>>
>> Or just give up optimizing (asan has a penalty anyway)?  Or
>
> Well, asan has a penalty and -fsanitize-use-after-scope even bigger penalty,
> but the point is to make that penalty bearable.
>
>> re-structure ASAN_POISON ()
>> similar to clobbers:
>>
>>   var = ASAN_POISION ();
>>
>> that avoids the address-taking and thus should be less heavy-weight on
>> optimizations.
>
> Yeah, that is what I meant.  The issue is how to report uses of such
> SSA_NAME when there is no memory.  So, either we'd need a special runtime
> library entrypoint that would report uses after scope even when there is no
> underlying memory, or we'd need to force it at asan pass time into memory again.

Well, there can't be any uses outside the scope -- there are no (memory) uses
left if we rewrite the thing into SSA.  That is, the address can no
longer "escape".

Of course there could have been invalid uses before the rewrite into SSA.  But
those can be diagnosed either immediately before or after re-writing into SSA
at compile-time (may be in dead code regions of course).

Richard.

>         Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 12:59                                   ` Richard Biener
@ 2016-11-02 13:06                                     ` Jakub Jelinek
  2016-11-02 13:16                                       ` Richard Biener
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02 13:06 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Liška, GCC Patches

On Wed, Nov 02, 2016 at 01:59:00PM +0100, Richard Biener wrote:
> > Yeah, that is what I meant.  The issue is how to report uses of such
> > SSA_NAME when there is no memory.  So, either we'd need a special runtime
> > library entrypoint that would report uses after scope even when there is no
> > underlying memory, or we'd need to force it at asan pass time into memory again.
> 
> Well, there can't be any uses outside the scope -- there are no (memory) uses
> left if we rewrite the thing into SSA.  That is, the address can no
> longer "escape".
> 
> Of course there could have been invalid uses before the rewrite into SSA.  But
> those can be diagnosed either immediately before or after re-writing into SSA
> at compile-time (may be in dead code regions of course).

Sure, we can warn on those at compile time, but we really should arrange to
error on those at runtime if they are ever executed, the UB happens only at
runtime, so in dead code isn't fatal.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 13:06                                     ` Jakub Jelinek
@ 2016-11-02 13:16                                       ` Richard Biener
  2016-11-02 14:38                                         ` Martin Liška
  2016-11-16 12:25                                         ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Richard Biener @ 2016-11-02 13:16 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On Wed, Nov 2, 2016 at 2:06 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Nov 02, 2016 at 01:59:00PM +0100, Richard Biener wrote:
>> > Yeah, that is what I meant.  The issue is how to report uses of such
>> > SSA_NAME when there is no memory.  So, either we'd need a special runtime
>> > library entrypoint that would report uses after scope even when there is no
>> > underlying memory, or we'd need to force it at asan pass time into memory again.
>>
>> Well, there can't be any uses outside the scope -- there are no (memory) uses
>> left if we rewrite the thing into SSA.  That is, the address can no
>> longer "escape".
>>
>> Of course there could have been invalid uses before the rewrite into SSA.  But
>> those can be diagnosed either immediately before or after re-writing into SSA
>> at compile-time (may be in dead code regions of course).
>
> Sure, we can warn on those at compile time, but we really should arrange to
> error on those at runtime if they are ever executed, the UB happens only at
> runtime, so in dead code isn't fatal.

Then we can replace those uses with a call into the asan runtime diagnosing the
issue instead?

Richard.

>         Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 10:11                               ` Jakub Jelinek
@ 2016-11-02 14:20                                 ` Marek Polacek
  2016-11-02 14:27                                   ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Marek Polacek @ 2016-11-02 14:20 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, GCC Patches

On Wed, Nov 02, 2016 at 11:10:53AM +0100, Jakub Jelinek wrote:
> On Wed, Nov 02, 2016 at 10:59:26AM +0100, Jakub Jelinek wrote:
> > > Which is gimplified as:
> > > 
> > >     int * ptr;
> > > 
> > >     switch (argc) <default: <D.2575>, case 1: <D.2573>>
> > >     {
> > >       int a;
> > > 
> > >       try
> > >         {
> > >           ASAN_MARK (2, &a, 4);
> > >           <D.2573>:
> > >           goto <D.2574>;
> > >           <D.2575>:
> > >           ptr = &a;
> > >           goto <D.2574>;
> > >         }
> > >       finally
> > >         {
> > >           ASAN_MARK (1, &a, 4);
> > >         }
> 
> > Shouldn't there be also ASAN_MARK on the implicit gotos from the switch
> > statement?  Otherwise, consider this being done in a loop, after the first
> > iteration you ASAN_MARK (1, &a, 4) (i.e. poison), then you iterate say with
> > args 1 and have in case 1: a = 0;, won't that trigger runtime error?
> 
> Wonder if for the variables declared inside of switch body, because we don't
> care about uses before scope, it wouldn't be more efficient to arrange for
> int *p, *q, *r;
> switch (x)
>   {
>     int a;
>   case 1:
>     p = &a;
>     a = 5;
>     break;
>     int b;
>   case 2:
>     int c;
>     q = &b;
>     r = &c;
>     b = 3;
>     c = 4;
>     break;
>   }
> to effectively ASAN_MARK (2, &a, 4); ASAN_MARK (2, &b, 4); ASAN_MARK (2, &c, 4);
> before the GIMPLE_SWITCH, instead of unpoisoning them on every case label
> where they might be in scope.  Though, of course, at least until lower pass
> that is quite ugly, because it would refer to "not yet declared" variables.
> Perhaps we'd need to move the ASAN_MARK and GIMPLE_SWITCH (but of course not
> the expression evaluation of the switch control expression) inside of the
> switches' GIMPLE_BIND.

So is there anything I should do wrt -Wswitch-unreachable?

	Marek

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:20                                 ` Marek Polacek
@ 2016-11-02 14:27                                   ` Martin Liška
  2016-11-02 14:35                                     ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-02 14:27 UTC (permalink / raw)
  To: Marek Polacek, Jakub Jelinek; +Cc: GCC Patches

On 11/02/2016 03:20 PM, Marek Polacek wrote:
> On Wed, Nov 02, 2016 at 11:10:53AM +0100, Jakub Jelinek wrote:
>> On Wed, Nov 02, 2016 at 10:59:26AM +0100, Jakub Jelinek wrote:
>>>> Which is gimplified as:
>>>>
>>>>     int * ptr;
>>>>
>>>>     switch (argc) <default: <D.2575>, case 1: <D.2573>>
>>>>     {
>>>>       int a;
>>>>
>>>>       try
>>>>         {
>>>>           ASAN_MARK (2, &a, 4);
>>>>           <D.2573>:
>>>>           goto <D.2574>;
>>>>           <D.2575>:
>>>>           ptr = &a;
>>>>           goto <D.2574>;
>>>>         }
>>>>       finally
>>>>         {
>>>>           ASAN_MARK (1, &a, 4);
>>>>         }
>>
>>> Shouldn't there be also ASAN_MARK on the implicit gotos from the switch
>>> statement?  Otherwise, consider this being done in a loop, after the first
>>> iteration you ASAN_MARK (1, &a, 4) (i.e. poison), then you iterate say with
>>> args 1 and have in case 1: a = 0;, won't that trigger runtime error?
>>
>> Wonder if for the variables declared inside of switch body, because we don't
>> care about uses before scope, it wouldn't be more efficient to arrange for
>> int *p, *q, *r;
>> switch (x)
>>   {
>>     int a;
>>   case 1:
>>     p = &a;
>>     a = 5;
>>     break;
>>     int b;
>>   case 2:
>>     int c;
>>     q = &b;
>>     r = &c;
>>     b = 3;
>>     c = 4;
>>     break;
>>   }
>> to effectively ASAN_MARK (2, &a, 4); ASAN_MARK (2, &b, 4); ASAN_MARK (2, &c, 4);
>> before the GIMPLE_SWITCH, instead of unpoisoning them on every case label
>> where they might be in scope.  Though, of course, at least until lower pass
>> that is quite ugly, because it would refer to "not yet declared" variables.
>> Perhaps we'd need to move the ASAN_MARK and GIMPLE_SWITCH (but of course not
>> the expression evaluation of the switch control expression) inside of the
>> switches' GIMPLE_BIND.
> 
> So is there anything I should do wrt -Wswitch-unreachable?
> 
> 	Marek
> 

Probably not. I'm having a patch puts GIMPLE_SWITCH statement to a proper place
in GIMPLE_BIND. Let's see whether such patch can bootstrap and survive regression
tests.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:27                                   ` Martin Liška
@ 2016-11-02 14:35                                     ` Jakub Jelinek
  2016-11-04  9:17                                       ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02 14:35 UTC (permalink / raw)
  To: Martin Liška; +Cc: Marek Polacek, GCC Patches

On Wed, Nov 02, 2016 at 03:27:42PM +0100, Martin Liška wrote:
> > So is there anything I should do wrt -Wswitch-unreachable?
> > 
> > 	Marek
> > 
> 
> Probably not. I'm having a patch puts GIMPLE_SWITCH statement to a proper place
> in GIMPLE_BIND. Let's see whether such patch can bootstrap and survive regression
> tests.

Please do that only for -fsanitize-use-after-scope, it will likely affect at
least for -O0 the debugging experience.  For -O0 -fsanitize=address -fsanitize-use-after-scope
perhaps we could arrange for some extra stmt to have the locus of the
switch (where we still don't want the vars to appear in scope) and then
have no locus on the ASAN_MARK and actual GIMPLE_SWITCH or something
similar.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 13:16                                       ` Richard Biener
@ 2016-11-02 14:38                                         ` Martin Liška
  2016-11-02 14:51                                           ` Jakub Jelinek
  2016-11-16 12:25                                         ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-02 14:38 UTC (permalink / raw)
  To: Richard Biener, Jakub Jelinek; +Cc: GCC Patches

On 11/02/2016 02:16 PM, Richard Biener wrote:
> On Wed, Nov 2, 2016 at 2:06 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>> On Wed, Nov 02, 2016 at 01:59:00PM +0100, Richard Biener wrote:
>>>> Yeah, that is what I meant.  The issue is how to report uses of such
>>>> SSA_NAME when there is no memory.  So, either we'd need a special runtime
>>>> library entrypoint that would report uses after scope even when there is no
>>>> underlying memory, or we'd need to force it at asan pass time into memory again.
>>>
>>> Well, there can't be any uses outside the scope -- there are no (memory) uses
>>> left if we rewrite the thing into SSA.  That is, the address can no
>>> longer "escape".
>>>
>>> Of course there could have been invalid uses before the rewrite into SSA.  But
>>> those can be diagnosed either immediately before or after re-writing into SSA
>>> at compile-time (may be in dead code regions of course).
>>
>> Sure, we can warn on those at compile time, but we really should arrange to
>> error on those at runtime if they are ever executed, the UB happens only at
>> runtime, so in dead code isn't fatal.
> 
> Then we can replace those uses with a call into the asan runtime diagnosing the
> issue instead?
> 
> Richard.
> 
>>         Jakub

OK, thanks for the clarification, it's more clear to me. So we want to consider for
SSA transformation of ASAN_MARK only is_gimple_reg_types. I'm having a test-case where
it converts:
foo ()
{
  char a;
  char * p;
  char _1;
  int _2;
  int _8;
  int _9;

  <bb 2>:
  ASAN_MARK (2, &a, 1);
  a = 0;
  p_6 = &a;
  ASAN_MARK (1, &a, 1);
  _1 = *p_6;
  if (_1 != 0)
    goto <bb 3>;
  else
    goto <bb 4>;

  <bb 3>:
  _9 = 1;
  goto <bb 5>;

  <bb 4>:
  _8 = 0;

  <bb 5>:
  # _2 = PHI <_9(3), _8(4)>
  return _2;

}

to:

foo ()
{
  char a;
  char * p;
  char _1;
  int _2;

  <bb 2>:
  a_10 = 0;
  a_12 = ASAN_POISON ();
  _1 = a_12;
  if (_1 != 0)
    goto <bb 4>;
  else
    goto <bb 3>;

  <bb 3>:

  <bb 4>:
  # _2 = PHI <1(2), 0(3)>
  return _2;

}

and probably the last goal is to convert the newly added internal fn to a runtime call.
Hope sanopt pass is the right place where to it?

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:38                                         ` Martin Liška
@ 2016-11-02 14:51                                           ` Jakub Jelinek
  2016-11-02 15:25                                             ` Martin Liška
  2016-11-03 13:34                                             ` Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-02 14:51 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

On Wed, Nov 02, 2016 at 03:38:25PM +0100, Martin Liška wrote:
> it converts:
> foo ()
> {
>   char a;
>   char * p;
>   char _1;
>   int _2;
>   int _8;
>   int _9;
> 
>   <bb 2>:
>   ASAN_MARK (2, &a, 1);
>   a = 0;
>   p_6 = &a;
>   ASAN_MARK (1, &a, 1);
>   _1 = *p_6;

You shouldn't convert if a is addressable (when ignoring &a in ASAN_MARK
calls).  Only if there is &a just in ASAN_MARK and MEM_REF, you can convert.

> to:
> 
> foo ()
> {
>   char a;
>   char * p;
>   char _1;
>   int _2;
> 
>   <bb 2>:
>   a_10 = 0;
>   a_12 = ASAN_POISON ();
>   _1 = a_12;
>   if (_1 != 0)
>     goto <bb 4>;
>   else
>     goto <bb 3>;
> 
>   <bb 3>:
> 
>   <bb 4>:
>   # _2 = PHI <1(2), 0(3)>
>   return _2;
> 
> }
> 
> and probably the last goal is to convert the newly added internal fn to a runtime call.
> Hope sanopt pass is the right place where to it?

If ASAN_POISON is ECF_CONST and has any uses during sanopt, perhaps best
would be to add an artificial variable you give the same name as the
underlying var of the SSA_NAME (and alignment, locus etc.) and poison it
right away (keep unpoisoning only to the function epilogue) and then
ASAN_CHECK replace all uses of that SSA_NAME with ASAN_CHECK + use of
(D) SSA_NAME.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:51                                           ` Jakub Jelinek
@ 2016-11-02 15:25                                             ` Martin Liška
  2016-11-03 13:34                                             ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-02 15:25 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On 11/02/2016 03:51 PM, Jakub Jelinek wrote:
> On Wed, Nov 02, 2016 at 03:38:25PM +0100, Martin Liška wrote:
>> it converts:
>> foo ()
>> {
>>   char a;
>>   char * p;
>>   char _1;
>>   int _2;
>>   int _8;
>>   int _9;
>>
>>   <bb 2>:
>>   ASAN_MARK (2, &a, 1);
>>   a = 0;
>>   p_6 = &a;
>>   ASAN_MARK (1, &a, 1);
>>   _1 = *p_6;
> 
> You shouldn't convert if a is addressable (when ignoring &a in ASAN_MARK
> calls).  Only if there is &a just in ASAN_MARK and MEM_REF, you can convert.

Sure, which should be done in execute_update_addresses_taken via gimple_ior_addresses_taken.

> 
>> to:
>>
>> foo ()
>> {
>>   char a;
>>   char * p;
>>   char _1;
>>   int _2;
>>
>>   <bb 2>:
>>   a_10 = 0;
>>   a_12 = ASAN_POISON ();
>>   _1 = a_12;
>>   if (_1 != 0)
>>     goto <bb 4>;
>>   else
>>     goto <bb 3>;
>>
>>   <bb 3>:
>>
>>   <bb 4>:
>>   # _2 = PHI <1(2), 0(3)>
>>   return _2;
>>
>> }
>>
>> and probably the last goal is to convert the newly added internal fn to a runtime call.
>> Hope sanopt pass is the right place where to it?
> 
> If ASAN_POISON is ECF_CONST and has any uses during sanopt, perhaps best
> would be to add an artificial variable you give the same name as the
> underlying var of the SSA_NAME (and alignment, locus etc.) and poison it
> right away (keep unpoisoning only to the function epilogue) and then
> ASAN_CHECK replace all uses of that SSA_NAME with ASAN_CHECK + use of
> (D) SSA_NAME.

When I create an ASAN_POISON call in execute_update_addresses_taken, there would not
be any ASAN_CHECK generated as it's going to be rewritten to SSA form (like the previous
sample I sent).

I like the idea of having a parallel variable, which can be poisoned at the very beginning of
a function. Whenever we have a use of the SSA_NAME (like a_12 = ASAN_POISON ()), we can simply
insert BUILT_IN_ASAN_REPORT_LOADx(&parallel_variable) statement. No change would be necessary
for ASAN runtime in such case.

Will it work?
Thanks,
Martin


> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:51                                           ` Jakub Jelinek
  2016-11-02 15:25                                             ` Martin Liška
@ 2016-11-03 13:34                                             ` Martin Liška
  2016-11-03 13:44                                               ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-03 13:34 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On 11/02/2016 03:51 PM, Jakub Jelinek wrote:
> On Wed, Nov 02, 2016 at 03:38:25PM +0100, Martin Liška wrote:
>> it converts:
>> foo ()
>> {
>>   char a;
>>   char * p;
>>   char _1;
>>   int _2;
>>   int _8;
>>   int _9;
>>
>>   <bb 2>:
>>   ASAN_MARK (2, &a, 1);
>>   a = 0;
>>   p_6 = &a;
>>   ASAN_MARK (1, &a, 1);
>>   _1 = *p_6;
> 
> You shouldn't convert if a is addressable (when ignoring &a in ASAN_MARK
> calls).  Only if there is &a just in ASAN_MARK and MEM_REF, you can convert.
> 
>> to:
>>
>> foo ()
>> {
>>   char a;
>>   char * p;
>>   char _1;
>>   int _2;
>>
>>   <bb 2>:
>>   a_10 = 0;
>>   a_12 = ASAN_POISON ();
>>   _1 = a_12;
>>   if (_1 != 0)
>>     goto <bb 4>;
>>   else
>>     goto <bb 3>;
>>
>>   <bb 3>:
>>
>>   <bb 4>:
>>   # _2 = PHI <1(2), 0(3)>
>>   return _2;
>>
>> }
>>
>> and probably the last goal is to convert the newly added internal fn to a runtime call.
>> Hope sanopt pass is the right place where to it?
> 
> If ASAN_POISON is ECF_CONST and has any uses during sanopt, perhaps best
> would be to add an artificial variable you give the same name as the
> underlying var of the SSA_NAME (and alignment, locus etc.) and poison it
> right away (keep unpoisoning only to the function epilogue) and then
> ASAN_CHECK replace all uses of that SSA_NAME with ASAN_CHECK + use of
> (D) SSA_NAME.
> 
> 	Jakub
> 

Hi.

I'm having a semi-working patch that comes up with the ASAN_POISON built-in. Well, to be honest,
I still have a feeling that doing the magic with the parallel variable is bit overkill. Maybe
a new runtime call would make it easier for us.

However, I still don't fully understand why we want to support just is_gimple_reg variables.
Let's consider following test-case:

void foo()
{
char *ptr;
  {
    char my_char[9];
    ptr = &my_char[0];
  }
}

Where I would expect to optimize out:
  <bb 2>:
  _5 = (unsigned long) 9;
  _4 = (unsigned long) &my_char;
  __builtin___asan_unpoison_stack_memory (_4, _5);
  _7 = (unsigned long) 9;
  _6 = (unsigned long) &my_char;
  __builtin___asan_poison_stack_memory (_6, _7);
  return;

where address of my_char is taken in the original source code, while not during tree-ssa
optimization, where the address is used only by ASAN_MARK calls.

Doing such transformation can rapidly decrease number of __builtin___asan_{un}poison_stack_memory
in tramp3d: from ~36K to ~22K.

Thanks for clarification.
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-03 13:34                                             ` Martin Liška
@ 2016-11-03 13:44                                               ` Jakub Jelinek
  2016-11-03 14:02                                                 ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-03 13:44 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

Hi!

FYI, I think it is much more important to get the initial patch in, so
resolve the switch + declarations inside of its body stuff, add testcases
for that and post for re-review and check in.
These optimizations can be done even in stage3.

On Thu, Nov 03, 2016 at 02:34:47PM +0100, Martin Liška wrote:
> I'm having a semi-working patch that comes up with the ASAN_POISON built-in. Well, to be honest,
> I still have a feeling that doing the magic with the parallel variable is bit overkill. Maybe
> a new runtime call would make it easier for us.
> 
> However, I still don't fully understand why we want to support just is_gimple_reg variables.
> Let's consider following test-case:
> 
> void foo()
> {
> char *ptr;
>   {
>     char my_char[9];
>     ptr = &my_char[0];
>   }
> }
> 
> Where I would expect to optimize out:
>   <bb 2>:
>   _5 = (unsigned long) 9;
>   _4 = (unsigned long) &my_char;
>   __builtin___asan_unpoison_stack_memory (_4, _5);
>   _7 = (unsigned long) 9;
>   _6 = (unsigned long) &my_char;
>   __builtin___asan_poison_stack_memory (_6, _7);
>   return;
> 
> where address of my_char is taken in the original source code, while not during tree-ssa
> optimization, where the address is used only by ASAN_MARK calls.

But how would you be able to find out if there isn't any return *ptr; after
the scope or similar (as MEM_REF)?  With is_gimple_reg, they will be turned
into SSA form and you can easily verify (uses of ASAN_POISON are a problem
if they are encountered at runtime).  What would you do for the
must_live_in_memory vars?  Add some pass that detects it, handle it somehow
in addressable pass, handle it in SRA, ... ?

> 
> Doing such transformation can rapidly decrease number of __builtin___asan_{un}poison_stack_memory
> in tramp3d: from ~36K to ~22K.
> 
> Thanks for clarification.
> Martin

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-03 13:44                                               ` Jakub Jelinek
@ 2016-11-03 14:02                                                 ` Martin Liška
  2016-11-03 14:04                                                   ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-03 14:02 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On 11/03/2016 02:44 PM, Jakub Jelinek wrote:
> Hi!
> 
> FYI, I think it is much more important to get the initial patch in, so
> resolve the switch + declarations inside of its body stuff, add testcases
> for that and post for re-review and check in.
> These optimizations can be done even in stage3.

I know that it's much urgent to have it done first. I'm currently testing patch
for the switch + declaration. Hopefully I'll send it today.

> 
> On Thu, Nov 03, 2016 at 02:34:47PM +0100, Martin Liška wrote:
>> I'm having a semi-working patch that comes up with the ASAN_POISON built-in. Well, to be honest,
>> I still have a feeling that doing the magic with the parallel variable is bit overkill. Maybe
>> a new runtime call would make it easier for us.
>>
>> However, I still don't fully understand why we want to support just is_gimple_reg variables.
>> Let's consider following test-case:
>>
>> void foo()
>> {
>> char *ptr;
>>   {
>>     char my_char[9];
>>     ptr = &my_char[0];
>>   }
>> }
>>
>> Where I would expect to optimize out:
>>   <bb 2>:
>>   _5 = (unsigned long) 9;
>>   _4 = (unsigned long) &my_char;
>>   __builtin___asan_unpoison_stack_memory (_4, _5);
>>   _7 = (unsigned long) 9;
>>   _6 = (unsigned long) &my_char;
>>   __builtin___asan_poison_stack_memory (_6, _7);
>>   return;
>>
>> where address of my_char is taken in the original source code, while not during tree-ssa
>> optimization, where the address is used only by ASAN_MARK calls.
> 
> But how would you be able to find out if there isn't any return *ptr; after
> the scope or similar (as MEM_REF)?  With is_gimple_reg, they will be turned
> into SSA form and you can easily verify (uses of ASAN_POISON are a problem
> if they are encountered at runtime).  What would you do for the
> must_live_in_memory vars?  Add some pass that detects it, handle it somehow
> in addressable pass, handle it in SRA, ... ?

If there's return of *ptr, there must be a &my_char, and it looks
  _4 = MEM[(char *)&my_char];

properly identifies that my_char has address taken.

M.

> 
>>
>> Doing such transformation can rapidly decrease number of __builtin___asan_{un}poison_stack_memory
>> in tramp3d: from ~36K to ~22K.
>>
>> Thanks for clarification.
>> Martin
> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-03 14:02                                                 ` Martin Liška
@ 2016-11-03 14:04                                                   ` Jakub Jelinek
  2016-11-03 14:18                                                     ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-03 14:04 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

On Thu, Nov 03, 2016 at 03:02:21PM +0100, Martin Liška wrote:
> > But how would you be able to find out if there isn't any return *ptr; after
> > the scope or similar (as MEM_REF)?  With is_gimple_reg, they will be turned
> > into SSA form and you can easily verify (uses of ASAN_POISON are a problem
> > if they are encountered at runtime).  What would you do for the
> > must_live_in_memory vars?  Add some pass that detects it, handle it somehow
> > in addressable pass, handle it in SRA, ... ?
> 
> If there's return of *ptr, there must be a &my_char, and it looks
>   _4 = MEM[(char *)&my_char];
> 
> properly identifies that my_char has address taken.

It doesn't.  MEM_REF's ADDR_EXPR isn't considered to be address taking.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-03 14:04                                                   ` Jakub Jelinek
@ 2016-11-03 14:18                                                     ` Martin Liška
  0 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-03 14:18 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On 11/03/2016 03:03 PM, Jakub Jelinek wrote:
> On Thu, Nov 03, 2016 at 03:02:21PM +0100, Martin Liška wrote:
>>> But how would you be able to find out if there isn't any return *ptr; after
>>> the scope or similar (as MEM_REF)?  With is_gimple_reg, they will be turned
>>> into SSA form and you can easily verify (uses of ASAN_POISON are a problem
>>> if they are encountered at runtime).  What would you do for the
>>> must_live_in_memory vars?  Add some pass that detects it, handle it somehow
>>> in addressable pass, handle it in SRA, ... ?
>>
>> If there's return of *ptr, there must be a &my_char, and it looks
>>   _4 = MEM[(char *)&my_char];
>>
>> properly identifies that my_char has address taken.
> 
> It doesn't.  MEM_REF's ADDR_EXPR isn't considered to be address taking.
> 
> 	Jakub
> 

You are of course right, my mistake in the patch draft.

Thanks for clarification,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-02 14:35                                     ` Jakub Jelinek
@ 2016-11-04  9:17                                       ` Martin Liška
  2016-11-04  9:33                                         ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-04  9:17 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1132 bytes --]

On 11/02/2016 03:35 PM, Jakub Jelinek wrote:
> On Wed, Nov 02, 2016 at 03:27:42PM +0100, Martin Liška wrote:
>>> So is there anything I should do wrt -Wswitch-unreachable?
>>>
>>> 	Marek
>>>
>>
>> Probably not. I'm having a patch puts GIMPLE_SWITCH statement to a proper place
>> in GIMPLE_BIND. Let's see whether such patch can bootstrap and survive regression
>> tests.
> 
> Please do that only for -fsanitize-use-after-scope, it will likely affect at
> least for -O0 the debugging experience.  For -O0 -fsanitize=address -fsanitize-use-after-scope
> perhaps we could arrange for some extra stmt to have the locus of the
> switch (where we still don't want the vars to appear in scope) and then
> have no locus on the ASAN_MARK and actual GIMPLE_SWITCH or something
> similar.
> 
> 	Jakub
> 

I'm sending patch where I put gimple switch statement to a place where all BIND_EXPR vars
are unpoisoned. I'm sending diff to a previous version and new version of the patch.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests. Apart from that,
asan bootstrap successfully finished on x86_64-linux-gnu.

Martin

[-- Attachment #2: 0001-Introduce-fsanitize-address-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 44040 bytes --]

From 742ba5e3f5eddb069a13bc51347d21a4ec6a45c6 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize-address-use-after-scope

gcc/c-family/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* c-warn.c (warn_for_unused_label): Save all labels used
	in goto or in &label.

gcc/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Move the enum to header file.
	(asan_init_shadow_ptr_types): Make type creation more generic.
	(shadow_mem_size): New function.
	(asan_emit_stack_protection): Use newly added ASAN_SHADOW_GRANULARITY.
	Rewritten stack unpoisoning code.
	(build_shadow_mem_access): Add new argument return_address.
	(instrument_derefs): Instrument local variables if use after scope
	sanitization is enabled.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	(asan_sanitize_stack_p): Moved from asan_sanitize_stack_p.
	* asan.h (enum asan_mark_flags): Moved here from asan.c
	(asan_protect_stack_decl): Protect all declaration that need
	to live in memory.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* cfgexpand.c (partition_stack_vars): Consider
	asan_sanitize_use_after_scope in condition.
	(expand_stack_vars): Likewise.
	* common.opt (-fsanitize-address-use-after-scope): New option.
	* doc/invoke.texi (use-after-scope-direct-emission-threshold):
	Explain the parameter.
	* flag-types.h (enum sanitize_code): Define SANITIZE_USE_AFTER_SCOPE.
	* gimplify.c (build_asan_poison_call_expr): New function.
	(asan_poison_variable): Likewise.
	(gimplify_bind_expr): Generate poisoning/unpoisoning for local
	variables that have address taken.
	(gimplify_decl_expr): Likewise.
	(gimplify_target_expr): Likewise for C++ temporaries.
	(sort_by_decl_uid): New function.
	(gimplify_expr): Unpoison all variables for a label we can jump
	from outside of a scope.
	(gimplify_function_tree): Clear asan_poisoned_variables.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def (ASAN_MARK): Declare.
	* opts.c (finish_options): Handle -fstack-reuse if
	-fsanitize-address-use-after-scope is enabled.
	(common_handle_option): Enable address sanitization if
	-fsanitize-address-use-after-scope is enabled.
	(warn_switch_unreachable_r): Return iterator
	instead of gimple *.
	(maybe_warn_switch_unreachable): Release the iterator.
	(gimplify_switch_expr): In case of enabled sanitization, put switch
	gimple statement to a place where all variables in BIND_EXPR
	are sanitized.
	* params.def (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD):
	New parameter.
	* params.h: Likewise.
	* sancov.c (pass_sanopt::execute): Handle IFN_ASAN_MARK.
	sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory functions.
	* tree-eh.c (verify_norecord_switch_expr): Verify switch expr
	only when not doing sanitization.
---
 gcc/asan.c            | 287 +++++++++++++++++++++++++++++++++++++++++---------
 gcc/asan.h            |  66 ++++++++++--
 gcc/c-family/c-warn.c |   9 +-
 gcc/cfgexpand.c       |  18 +---
 gcc/common.opt        |   3 +
 gcc/doc/invoke.texi   |  15 ++-
 gcc/gimplify.c        | 239 +++++++++++++++++++++++++++++++++++++----
 gcc/internal-fn.c     |   9 ++
 gcc/internal-fn.def   |   1 +
 gcc/opts.c            |  27 ++++-
 gcc/params.def        |   6 ++
 gcc/params.h          |   2 +
 gcc/sanitizer.def     |   4 +
 gcc/sanopt.c          |   3 +
 gcc/tree-eh.c         |   4 +
 15 files changed, 592 insertions(+), 101 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index c6d9240..95495d2 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -245,6 +245,13 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> *asan_handled_variables = NULL;
+
+hash_set <tree> *asan_used_labels = NULL;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -287,6 +294,14 @@ set_sanitized_sections (const char *sections)
     }
 }
 
+bool
+asan_sanitize_stack_p (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS)
+	  && ASAN_STACK
+	  && !asan_no_sanitize_address_p ());
+}
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -315,22 +330,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -933,12 +939,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1022,6 +1032,15 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+shadow_mem_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1047,7 +1066,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
-  HOST_WIDE_INT last_offset, last_size;
+  HOST_WIDE_INT last_offset;
   int l;
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
@@ -1205,10 +1224,10 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 				       (aoff - prev_offset)
 				       >> ASAN_SHADOW_SHIFT);
 	  prev_offset = aoff;
-	  for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT))
+	  for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
 	    if (aoff < offset)
 	      {
-		if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1)
+		if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
 		  shadow_bytes[i] = 0;
 		else
 		  shadow_bytes[i] = offset - aoff;
@@ -1282,35 +1301,66 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
 
-  prev_offset = base_offset;
+  /* Unpoison shadow memory of a stack at the very end of a function.
+     As we're poisoning stack variables at the end of their scope,
+     shadow memory must be properly unpoisoned here.  The easiest approach
+     would be to collect all variables that should not be unpoisoned and
+     we unpoison shadow memory of the whole stack except ranges
+     occupied by these variables.  */
   last_offset = base_offset;
-  last_size = 0;
-  for (l = length; l; l -= 2)
+  HOST_WIDE_INT current_offset = last_offset;
+  if (length)
     {
-      offset = base_offset + ((offsets[l - 1] - base_offset)
-			     & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-      if (last_offset + last_size != offset)
+      HOST_WIDE_INT var_end_offset = 0;
+      HOST_WIDE_INT stack_start = offsets[length - 1];
+      gcc_assert (last_offset == stack_start);
+
+      for (int l = length - 2; l > 0; l -= 2)
 	{
-	  shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				       (last_offset - prev_offset)
-				       >> ASAN_SHADOW_SHIFT);
-	  prev_offset = last_offset;
-	  asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
-	  last_offset = offset;
-	  last_size = 0;
+	  HOST_WIDE_INT var_offset = offsets[l];
+	  current_offset = var_offset;
+	  var_end_offset = offsets[l - 1];
+	  HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset,
+					     BITS_PER_UNIT);
+
+	  /* Should we unpoison the variable?  */
+	  if (asan_handled_variables != NULL
+	      && asan_handled_variables->contains (decl))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  const char *n = (DECL_NAME (decl)
+				   ? IDENTIFIER_POINTER (DECL_NAME (decl))
+				   : "<unknown>");
+		  fprintf (dump_file, "Unpoisoning shadow stack for variable: "
+			   "%s (%" PRId64 "B)\n", n,
+			   var_end_offset - var_offset);
+		}
+
+	      unsigned HOST_WIDE_INT s
+		= shadow_mem_size (current_offset - last_offset);
+	      asan_clear_shadow (shadow_mem, s);
+	      HOST_WIDE_INT shift
+		= shadow_mem_size (current_offset - last_offset + rounded_size);
+	      shadow_mem = adjust_address (shadow_mem, VOIDmode, shift);
+	      last_offset = var_offset + rounded_size;
+	      current_offset = last_offset;
+	    }
+
 	}
-      last_size += base_offset + ((offsets[l - 2] - base_offset)
-				  & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
-		   - offset;
-    }
-  if (last_size)
-    {
-      shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				   (last_offset - prev_offset)
-				   >> ASAN_SHADOW_SHIFT);
-      asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
+
+      /* Handle last redzone.  */
+      current_offset = offsets[0];
+      asan_clear_shadow (shadow_mem,
+			 shadow_mem_size (current_offset - last_offset));
     }
 
+  /* Clean-up set with instrumented stack variables.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1590,12 +1640,14 @@ insert_if_then_before_iter (gcond *cond,
   gsi_insert_after (&cond_insert_point, cond, GSI_NEW_STMT);
 }
 
-/* Build
-   (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
+/* Build (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().
+   If RETURN_ADDRESS is set to true, return memory location instread
+   of a value in the shadow memory.  */
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_address = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1618,11 +1670,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_address)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1826,7 +1882,9 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && (!asan_sanitize_use_after_scope ()
+		  || !TREE_ADDRESSABLE (inner)))
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2576,6 +2634,131 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Poison or unpoison (depending on IS_CLOBBER variable) shadow memory based
+   on SHADOW address.  Newly added statements will be added to ITER with
+   given location LOC.  We mark SIZE bytes in shadow memory, where
+   LAST_CHUNK_SIZE is greater than zero in situation where we are at the
+   end of a variable.  */
+
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 bool is_clobber, unsigned size,
+			 unsigned last_chunk_size)
+{
+  tree shadow_ptr_type;
+
+  switch (size)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < size; ++i)
+    {
+      unsigned char shadow_c = c;
+      if (i == size - 1 && last_chunk_size && !is_clobber)
+	shadow_c = last_chunk_size;
+      val |= (unsigned HOST_WIDE_INT) shadow_c << (BITS_PER_UNIT * i);
+    }
+
+  /* Handle last chunk in unpoisoning.  */
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (asan_handled_variables == NULL)
+    asan_handled_variables = new hash_set<tree> (16);
+  asan_handled_variables->add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  /* Generate direct emission if size_in_bytes is small.  */
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes);
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  unsigned HOST_WIDE_INT last_chunk_size = 0;
+	  unsigned HOST_WIDE_INT s = (offset + size) * ASAN_SHADOW_GRANULARITY;
+	  if (s > size_in_bytes)
+	    last_chunk_size = ASAN_SHADOW_GRANULARITY - (s - size_in_bytes);
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, is_clobber,
+				   size, last_chunk_size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..042af1f 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,9 +37,14 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of labels that are either used in a goto, or their address
+   has been taken.  */
+extern hash_set <tree> *asan_used_labels;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
+#define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT)
 
 /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
@@ -50,22 +56,32 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
-/* Return true if DECL should be guarded on the stack.  */
-
-static inline bool
-asan_protect_stack_decl (tree decl)
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
 {
-  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
-}
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
 
 /* Return the size of padding needed to insert after a protected
    decl of SIZE.  */
@@ -81,6 +97,8 @@ extern bool set_asan_shadow_offset (const char *);
 
 extern void set_sanitized_sections (const char *);
 
+extern bool asan_sanitize_stack_p (void);
+
 /* Return TRUE if builtin with given FCODE will be intercepted by
    libasan.  */
 
@@ -105,4 +123,30 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
+/* Return true if DECL should be guarded on the stack.  */
+
+static inline bool
+asan_protect_stack_decl (tree decl)
+{
+  return DECL_P (decl)
+    && (!DECL_ARTIFICIAL (decl)
+	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index 904f6d3..18ee247 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "diagnostic.h"
 #include "intl.h"
-
+#include "asan.h"
 
 /* Print a warning if a constant expression had overflow in folding.
    Invoke this function on every expression that the language
@@ -1627,6 +1627,13 @@ warn_for_unused_label (tree label)
       else
 	warning (OPT_Wunused_label, "label %q+D declared but not defined", label);
     }
+  else if (asan_sanitize_use_after_scope ())
+    {
+      if (asan_used_labels == NULL)
+	asan_used_labels = new hash_set<tree> (16);
+
+      asan_used_labels->add (label);
+    }
 }
 
 /* Warn for division by zero according to the value of DIVISOR.  LOC
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 430ad38..7ffb558 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -868,18 +868,6 @@ union_stack_vars (size_t a, size_t b)
     }
 }
 
-/* Return true if the current function should have its stack frame
-   protected by address sanitizer.  */
-
-static inline bool
-asan_sanitize_stack_p (void)
-{
-  return ((flag_sanitize & SANITIZE_ADDRESS)
-	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
-}
-
 /* A subroutine of expand_used_vars.  Binpack the variables into
    partitions constrained by the interference graph.  The overall
    algorithm used is as follows:
@@ -941,7 +929,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1128,7 +1117,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
 	  base = virtual_stack_vars_rtx;
-	  if (asan_sanitize_stack_p () && pred)
+	  if ((asan_sanitize_stack_p ())
+	      && pred)
 	    {
 	      HOST_WIDE_INT prev_offset
 		= align_base (frame_offset,
diff --git a/gcc/common.opt b/gcc/common.opt
index 097cae6..a60bebf 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -986,6 +986,9 @@ fsanitize-recover
 Common Report
 This switch is deprecated; use -fsanitize-recover= instead.
 
+fsanitize-address-use-after-scope
+Common Driver Report Var(flag_sanitize_address_use_after_scope) Init(0)
+
 fsanitize-undefined-trap-on-error
 Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
 Use trap instead of a library function for undefined behavior sanitization.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ad9304f..6e0dbf7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10288,6 +10288,10 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item use-after-scope-direct-emission-threshold
+If size of a local variables in bytes is smaller of equal to this number,
+direct instruction emission is utilized to poison and unpoison local variables.
+
 @item chkp-max-ctor-size
 Static constructors generated by Pointer Bounds Checker may become very
 large and significantly increase compile time at optimization level
@@ -10498,6 +10502,7 @@ thread-safe code.
 Enable AddressSanitizer, a fast memory error detector.
 Memory access instructions are instrumented to detect
 out-of-bounds and use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/sanitizers/wiki/AddressSanitizer} for
 more details.  The run-time behavior can be influenced using the
 @env{ASAN_OPTIONS} environment variable.  When set to @code{help=1},
@@ -10509,6 +10514,7 @@ The option can't be combined with @option{-fsanitize=thread}.
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
 @item -fsanitize=thread
@@ -10708,8 +10714,8 @@ except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
 @option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
 @option{-fsanitize=bounds-strict},
 @option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
-For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
-for which this feature is experimental.
+For these sanitizers error recovery is turned on by default,
+except @option{-fsanitize=address}, for which this feature is experimental.
 @option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
 accepted, the former enables recovery for all sanitizers that support it,
 the latter disables recovery for all sanitizers that support it.
@@ -10731,6 +10737,11 @@ Similarly @option{-fno-sanitize-recover} is equivalent to
 -fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
 @end smallexample
 
+@item -fsanitize-address-use-after-scope
+@opindex fsanitize-address-use-after-scope
+Enable sanitization of local variables to detect use-after-scope bugs.
+The option sets @option{-fstack-reuse} to @samp{none}.
+
 @item -fsanitize-undefined-trap-on-error
 @opindex fsanitize-undefined-trap-on-error
 The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 1531582..ab87999 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> *asan_poisoned_variables = NULL;
 
 enum gimplify_omp_var_data
 {
@@ -1088,6 +1092,79 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */
+
+static tree
+build_asan_poison_call_expr (tree decl)
+{
+  /* Do not poison variables that have size equal to zero.  */
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  if (zerop (unit_size))
+    return NULL_TREE;
+
+  tree base = build_fold_addr_expr (decl);
+
+  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
+				       void_type_node, 3,
+				       build_int_cst (integer_type_node,
+						      ASAN_MARK_CLOBBER),
+				       base, unit_size);
+}
+
+/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
+   on POISON flag, shadow memory of a DECL variable.  The call will be
+   put on location identified by IT iterator, where BEFORE flag drives
+   position where the stmt will be put.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
+		      bool before)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  /* Do not poison variables that have size equal to zero.  */
+  if (zerop (unit_size))
+    return;
+
+  /* It's necessary to have all stack variables aligned to ASAN granularity
+     bytes.  */
+  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+
+  if (before)
+    gsi_insert_before (it, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (it, g, GSI_NEW_STMT);
+}
+
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  gimple_stmt_iterator it = gsi_last (*seq_p);
+  bool before = false;
+
+  if (gsi_end_p (it))
+    before = true;
+
+  asan_poison_variable (decl, poison, &it, before);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1231,6 +1308,13 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 		}
 	    }
 	}
+
+      if (asan_poisoned_variables != NULL
+	  && asan_poisoned_variables->contains (t))
+	{
+	  asan_poisoned_variables->remove (t);
+	  asan_poison_variable (t, true, &cleanup);
+	}
     }
 
   if (ret_clauses)
@@ -1475,13 +1559,27 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
   if (VAR_P (decl) && !DECL_EXTERNAL (decl))
     {
       tree init = DECL_INITIAL (decl);
+      bool is_vla = false;
 
       if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
 	  || (!TREE_STATIC (decl)
 	      && flag_stack_check == GENERIC_STACK_CHECK
 	      && compare_tree_int (DECL_SIZE_UNIT (decl),
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
-	gimplify_vla_decl (decl, seq_p);
+	{
+	  gimplify_vla_decl (decl, seq_p);
+	  is_vla = true;
+	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && !is_vla
+	  && TREE_ADDRESSABLE (decl)
+	  && !TREE_STATIC (decl))
+	{
+	  asan_poisoned_variables->add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	}
 
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
@@ -1580,7 +1678,9 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
 	 worse location info.  */
       if (gimple_try_eval (stmt) == NULL)
 	{
-	  wi->info = stmt;
+	  gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
+	  memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));
+	  wi->info = it;
 	  return integer_zero_node;
 	}
       /* Fall through.  */
@@ -1591,9 +1691,18 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
       /* Walk the sub-statements.  */
       *handled_ops_p = false;
       break;
+    case GIMPLE_CALL:
+      if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
+	{
+	  *handled_ops_p = false;
+	  break;
+	}
+      /* Fall through.  */
     default:
       /* Save the first "real" statement (not a decl/lexical scope/...).  */
-      wi->info = stmt;
+      gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
+      memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));
+      wi->info = it;
       return integer_zero_node;
     }
   return NULL_TREE;
@@ -1615,7 +1724,11 @@ maybe_warn_switch_unreachable (gimple_seq seq)
   struct walk_stmt_info wi;
   memset (&wi, 0, sizeof (wi));
   walk_gimple_seq (seq, warn_switch_unreachable_r, NULL, &wi);
-  gimple *stmt = (gimple *) wi.info;
+  gimple *stmt = NULL;
+  gimple_stmt_iterator *gsi = (gimple_stmt_iterator *) wi.info;
+  if (gsi)
+    stmt = gsi_stmt (*gsi);
+  free (wi.info);
 
   if (stmt && gimple_code (stmt) != GIMPLE_LABEL)
     {
@@ -1802,6 +1915,8 @@ collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
 	  if (find_label_entry (labels, label))
 	    prev = gsi_stmt (*gsi_p);
 	}
+      else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
+	;
       else
 	prev = gsi_stmt (*gsi_p);
       gsi_next (gsi_p);
@@ -2124,7 +2239,23 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
 
       switch_stmt = gimple_build_switch (SWITCH_COND (switch_expr),
 					   default_case, labels);
-      gimplify_seq_add_stmt (pre_p, switch_stmt);
+
+      if (asan_sanitize_use_after_scope ())
+	{
+	  struct walk_stmt_info wi;
+	  memset (&wi, 0, sizeof (wi));
+	  walk_gimple_seq (switch_body_seq, warn_switch_unreachable_r, NULL,
+			   &wi);
+	  gimple_stmt_iterator *it = (gimple_stmt_iterator *)wi.info;
+	  if (gsi_stmt (*it) == switch_body_seq)
+	    gimplify_seq_add_stmt (pre_p, switch_stmt);
+	  else
+	    gsi_insert_before_without_update (it, switch_stmt, GSI_SAME_STMT);
+	  free (it);
+	}
+      else
+	gimplify_seq_add_stmt (pre_p, switch_stmt);
+
       gimplify_seq_add_seq (pre_p, switch_body_seq);
       labels.release ();
     }
@@ -6164,6 +6295,9 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   tree init = TARGET_EXPR_INITIAL (targ);
   enum gimplify_status ret;
 
+  bool unpoison_empty_seq = false;
+  gimple_stmt_iterator unpoison_it;
+
   if (init)
     {
       tree cleanup = NULL_TREE;
@@ -6177,7 +6311,14 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  gimplify_vla_decl (temp, pre_p);
 	}
       else
-	gimple_add_tmp_var (temp);
+	{
+	  /* Save location where we need to place unpoisoning.  It's possible
+	     that a variable will be converted to needs_to_live_in_memory.  */
+	  unpoison_it = gsi_last (*pre_p);
+	  unpoison_empty_seq = gsi_end_p (unpoison_it);
+
+	  gimple_add_tmp_var (temp);
+	}
 
       /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
 	 expression is supposed to initialize the slot.  */
@@ -6213,20 +6354,34 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       /* Add a clobber for the temporary going out of scope, like
 	 gimplify_bind_expr.  */
       if (gimplify_ctxp->in_cleanup_point_expr
-	  && needs_to_live_in_memory (temp)
-	  && flag_stack_reuse == SR_ALL)
-	{
-	  tree clobber = build_constructor (TREE_TYPE (temp),
-					    NULL);
-	  TREE_THIS_VOLATILE (clobber) = true;
-	  clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
-	  if (cleanup)
-	    cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
-			      clobber);
-	  else
-	    cleanup = clobber;
-	}
+	  && needs_to_live_in_memory (temp))
+	{
+	  if (flag_stack_reuse == SR_ALL)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (temp),
+						NULL);
+	      TREE_THIS_VOLATILE (clobber) = true;
+	      clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+	      if (cleanup)
+		cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+				  clobber);
+	      else
+		cleanup = clobber;
+	    }
+	  if (asan_sanitize_use_after_scope ())
+	    {
+	      tree asan_cleanup = build_asan_poison_call_expr (temp);
+	      if (asan_cleanup)
+		{
+		  if (unpoison_empty_seq)
+		    unpoison_it = gsi_start (*pre_p);
 
+		  asan_poison_variable (temp, false, &unpoison_it,
+					unpoison_empty_seq);
+		  gimple_push_cleanup (temp, asan_cleanup, false, pre_p);
+		}
+	    }
+	}
       if (cleanup)
 	gimple_push_cleanup (temp, cleanup, false, pre_p);
 
@@ -10733,6 +10888,25 @@ gimplify_omp_ordered (tree expr, gimple_seq body)
   return gimple_build_omp_ordered (body, OMP_ORDERED_CLAUSES (expr));
 }
 
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
 /* Convert the GENERIC expression tree *EXPR_P to GIMPLE.  If the
    expression produces a value to be used as an operand inside a GIMPLE
    statement, the value will be stored back in *EXPR_P.  This value will
@@ -10824,6 +10998,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   location_t saved_location;
   enum gimplify_status ret;
   gimple_stmt_iterator pre_last_gsi, post_last_gsi;
+  tree label;
 
   save_expr = *expr_p;
   if (save_expr == NULL_TREE)
@@ -11239,6 +11414,29 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	case LABEL_EXPR:
 	  ret = gimplify_label_expr (expr_p, pre_p);
+	  label = LABEL_EXPR_LABEL (*expr_p);
+	  gcc_assert (decl_function_context (label) == current_function_decl);
+
+	  /* If the label is used in a goto statement, or address of the label
+	     is taken, we need to unpoison all variables that were seen so far.
+	     Doing so would prevent us from reporting a false positives.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && asan_used_labels != NULL
+	      && asan_used_labels->contains (label))
+	    {
+	      unsigned c = asan_poisoned_variables->elements ();
+	      auto_vec<tree> sorted_variables (c);
+
+	      for (hash_set<tree>::iterator it
+		   = asan_poisoned_variables->begin ();
+		   it != asan_poisoned_variables->end (); ++it)
+		sorted_variables.safe_push (*it);
+
+	      sorted_variables.qsort (sort_by_decl_uid);
+
+	      for (unsigned i = 0; i < sorted_variables.length (); ++i)
+		asan_poison_variable (sorted_variables[i], false, pre_p);
+	    }
 	  break;
 
 	case CASE_LABEL_EXPR:
@@ -12336,7 +12534,10 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
+  delete asan_poisoned_variables;
+  asan_poisoned_variables = NULL;
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 168adc6..cbee97e 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -237,6 +237,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 cf2c402..6a0a7f6 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts.c b/gcc/opts.c
index d381cb5..2f230ce 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -979,6 +979,25 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+     enabled.  */
+  if (opts->x_flag_sanitize
+      && !opts_set->x_flag_sanitize_address_use_after_scope)
+    opts->x_flag_sanitize_address_use_after_scope = true;
+
+  /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
+     is enabled.  */
+  if (opts->x_flag_sanitize_address_use_after_scope)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize-address-use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1452,8 +1471,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
-  SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
-  SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
+  SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
   SANITIZER_OPT (leak, SANITIZE_LEAK, false),
@@ -1781,6 +1800,10 @@ common_handle_option (struct gcc_options *opts,
       /* Deferred.  */
       break;
 
+    case OPT_fsanitize_address_use_after_scope:
+      opts->x_flag_sanitize_address_use_after_scope = value;
+      break;
+
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
diff --git a/gcc/params.def b/gcc/params.def
index ab3eb3d..89f7093 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1168,6 +1168,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+	 256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 97c8d56..0a2905c 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -244,5 +244,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 8a6fbe9..320e14e 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -732,6 +732,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c
index db72156..150b2ab 100644
--- a/gcc/tree-eh.c
+++ b/gcc/tree-eh.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "cfgloop.h"
 #include "gimple-low.h"
+#include "asan.h"
 
 /* In some instances a tree and a gimple need to be stored in a same table,
    i.e. in hash tables. This is a structure to do this. */
@@ -706,6 +707,9 @@ verify_norecord_switch_expr (struct leh_state *state,
   if (!tf)
     return;
 
+  if (asan_sanitize_use_after_scope ())
+    return;
+
   n = gimple_switch_num_labels (switch_expr);
 
   for (i = 0; i < n; ++i)
-- 
2.10.1


[-- Attachment #3: use-after-scope.patch --]
[-- Type: text/x-patch, Size: 3397 bytes --]

diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 813777d..86ce793 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1678,7 +1678,9 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
 	 worse location info.  */
       if (gimple_try_eval (stmt) == NULL)
 	{
-	  wi->info = stmt;
+	  gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
+	  memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));
+	  wi->info = it;
 	  return integer_zero_node;
 	}
       /* Fall through.  */
@@ -1689,9 +1691,18 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
       /* Walk the sub-statements.  */
       *handled_ops_p = false;
       break;
+    case GIMPLE_CALL:
+      if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
+	{
+	  *handled_ops_p = false;
+	  break;
+	}
+      /* Fall through.  */
     default:
       /* Save the first "real" statement (not a decl/lexical scope/...).  */
-      wi->info = stmt;
+      gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
+      memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));
+      wi->info = it;
       return integer_zero_node;
     }
   return NULL_TREE;
@@ -1713,7 +1724,11 @@ maybe_warn_switch_unreachable (gimple_seq seq)
   struct walk_stmt_info wi;
   memset (&wi, 0, sizeof (wi));
   walk_gimple_seq (seq, warn_switch_unreachable_r, NULL, &wi);
-  gimple *stmt = (gimple *) wi.info;
+  gimple *stmt = NULL;
+  gimple_stmt_iterator *gsi = (gimple_stmt_iterator *) wi.info;
+  if (gsi)
+    stmt = gsi_stmt (*gsi);
+  free (wi.info);
 
   if (stmt && gimple_code (stmt) != GIMPLE_LABEL)
     {
@@ -1802,6 +1900,8 @@ collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
 	  if (find_label_entry (labels, label))
 	    prev = gsi_stmt (*gsi_p);
 	}
+      else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
+	;
       else
 	prev = gsi_stmt (*gsi_p);
       gsi_next (gsi_p);

@@ -2224,7 +2239,22 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
 
       switch_stmt = gimple_build_switch (SWITCH_COND (switch_expr),
 					   default_case, labels);
-      gimplify_seq_add_stmt (pre_p, switch_stmt);
+
+      if (asan_sanitize_use_after_scope ())
+	{
+	  struct walk_stmt_info wi;
+	  memset (&wi, 0, sizeof (wi));
+	  walk_gimple_seq (switch_body_seq, warn_switch_unreachable_r, NULL, &wi);
+	  gimple_stmt_iterator *it = (gimple_stmt_iterator *)wi.info;
+	  if (gsi_stmt (*it) == switch_body_seq)
+	    gimplify_seq_add_stmt (pre_p, switch_stmt);
+	  else
+	    gsi_insert_before_without_update (it, switch_stmt, GSI_SAME_STMT);
+	  free (it);
+	}
+      else
+	gimplify_seq_add_stmt (pre_p, switch_stmt);
+
       gimplify_seq_add_seq (pre_p, switch_body_seq);
       labels.release ();
     }
diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c
index db72156..150b2ab 100644
--- a/gcc/tree-eh.c
+++ b/gcc/tree-eh.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "cfgloop.h"
 #include "gimple-low.h"
+#include "asan.h"
 
 /* In some instances a tree and a gimple need to be stored in a same table,
    i.e. in hash tables. This is a structure to do this. */
@@ -706,6 +707,9 @@ verify_norecord_switch_expr (struct leh_state *state,
   if (!tf)
     return;
 
+  if (asan_sanitize_use_after_scope ())
+    return;
+
   n = gimple_switch_num_labels (switch_expr);
 
   for (i = 0; i < n; ++i)
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-04  9:17                                       ` Martin Liška
@ 2016-11-04  9:33                                         ` Jakub Jelinek
  2016-11-04 10:59                                           ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-04  9:33 UTC (permalink / raw)
  To: Martin Liška; +Cc: Marek Polacek, GCC Patches

Hi!

On Fri, Nov 04, 2016 at 10:17:31AM +0100, Martin Liška wrote:
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index 813777d..86ce793 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -1678,7 +1678,9 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
>  	 worse location info.  */
>        if (gimple_try_eval (stmt) == NULL)
>  	{
> -	  wi->info = stmt;
> +	  gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
> +	  memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));

That would be cleaner as *it = *gsi_p;
That set, I fail to see
1) the need to use a gsi pointer in wi->info compared to stmt itself,
   you can gsi_for_stmt cheaply at any time
2) why is anything done about this in warn_switch_unreachable_r
   - the problem isn't related to this warning IMHO.  Even
switch (x)
  {
  case 1:
    int x;
    x = 6;
    ptr = &x;
    break;
  case 2:
    ptr = &x;
    *ptr = 7;
    break;
  }
   has the same issue and there is no switch unreachable code there, but you
   still want for -fsanitize-use-after-scope pretend it is actually:
x_tmp = x;
{
  int x;
  switch (x_tmp)
    {
    case 1:
      x = 6;
      ptr = &x;
      break;
    case 2:
      ptr = &x;
      *ptr = 7;
      break;
    }
}
    and put ASAN_MARK unpoisoning before GIMPLE_SWITCH.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2)
  2016-11-04  9:33                                         ` Jakub Jelinek
@ 2016-11-04 10:59                                           ` Martin Liška
  2016-11-07 10:03                                             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3) Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-04 10:59 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On 11/04/2016 10:32 AM, Jakub Jelinek wrote:
> Hi!
> 
> On Fri, Nov 04, 2016 at 10:17:31AM +0100, Martin Liška wrote:
>> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
>> index 813777d..86ce793 100644
>> --- a/gcc/gimplify.c
>> +++ b/gcc/gimplify.c
>> @@ -1678,7 +1678,9 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
>>  	 worse location info.  */
>>        if (gimple_try_eval (stmt) == NULL)
>>  	{
>> -	  wi->info = stmt;
>> +	  gimple_stmt_iterator *it = XNEW (gimple_stmt_iterator);
>> +	  memcpy (it, gsi_p, sizeof (gimple_stmt_iterator));
> 
> That would be cleaner as *it = *gsi_p;

I know that it's kind of ugly, but as the gimple stmt is not yet added to a BB, using
gsi_for_stmt ICEs:

/tmp/use-after-scope-switch.c:12:5: internal compiler error: Segmentation fault
     switch (argc)
     ^~~~~~
0xe16a14 crash_signal
	../../gcc/toplev.c:338
0xadf890 bb_seq_addr
	../../gcc/gimple.h:1658
0xae01dd gsi_start_bb
	../../gcc/gimple-iterator.h:129
0xae111f gsi_for_stmt(gimple*)
	../../gcc/gimple-iterator.c:617

> That set, I fail to see
> 1) the need to use a gsi pointer in wi->info compared to stmt itself,
>    you can gsi_for_stmt cheaply at any time
> 2) why is anything done about this in warn_switch_unreachable_r
>    - the problem isn't related to this warning IMHO.  Even
> switch (x)
>   {
>   case 1:
>     int x;
>     x = 6;
>     ptr = &x;
>     break;
>   case 2:
>     ptr = &x;
>     *ptr = 7;
>     break;
>   }
>    has the same issue and there is no switch unreachable code there, but you
>    still want for -fsanitize-use-after-scope pretend it is actually:

You're right, that's not handled. I'm wondering whether it's always profitable
because when you do not reach the case 1, you're not doing a poisoning operation?

Martin

> x_tmp = x;
> {
>   int x;
>   switch (x_tmp)
>     {
>     case 1:
>       x = 6;
>       ptr = &x;
>       break;
>     case 2:
>       ptr = &x;
>       *ptr = 7;
>       break;
>     }
> }
>     and put ASAN_MARK unpoisoning before GIMPLE_SWITCH.
> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)
  2016-11-04 10:59                                           ` Martin Liška
@ 2016-11-07 10:03                                             ` Martin Liška
  2016-11-07 10:08                                               ` Jakub Jelinek
  2016-11-07 16:07                                               ` Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)) David Malcolm
  0 siblings, 2 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-07 10:03 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 801 bytes --]

Hello.

After discussion with Jakub, I'm resending new version of the patch, where I changed following:
1) gimplify_ctxp->live_switch_vars is used to track variables introduced in switch_expr. Every time
   a case_label_expr is seen, these are unpoisoned. It's quite conservative, however it covers all
   corner cases on can come up with. Compared to clang, we are much more precise in switch statements
   where a variable liveness crosses label boundary.
2) I found a bug where ASAN_CHECK was optimized out due to missing check of IFN_ASAN_MARK internal fn.
   Test was added for that.
3) Multiple switch tests have been added, which is going to be sent in upcoming email.

Patch can bootstrap on ppc64le-redhat-linux and survives regression tests (+ asan bootstrap finishes
successfully).

Martin

[-- Attachment #2: 0001-Introduce-fsanitize-address-use-after-scope.patch --]
[-- Type: text/x-patch, Size: 44796 bytes --]

From 2b37a59dd639ad740fdbd49d57b9f1975fc35046 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 3 May 2016 15:35:22 +0200
Subject: [PATCH 1/2] Introduce -fsanitize-address-use-after-scope

gcc/c-family/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* c-warn.c (warn_for_unused_label): Save all labels used
	in goto or in &label.

gcc/ChangeLog:

2016-10-27  Martin Liska  <mliska@suse.cz>

	* asan.c (enum asan_check_flags): Move the enum to header file.
	(asan_init_shadow_ptr_types): Make type creation more generic.
	(shadow_mem_size): New function.
	(asan_emit_stack_protection): Use newly added ASAN_SHADOW_GRANULARITY.
	Rewritten stack unpoisoning code.
	(build_shadow_mem_access): Add new argument return_address.
	(instrument_derefs): Instrument local variables if use after scope
	sanitization is enabled.
	(asan_store_shadow_bytes): New function.
	(asan_expand_mark_ifn): Likewise.
	(asan_sanitize_stack_p): Moved from asan_sanitize_stack_p.
	* asan.h (enum asan_mark_flags): Moved here from asan.c
	(asan_protect_stack_decl): Protect all declaration that need
	to live in memory.
	(asan_sanitize_use_after_scope): New function.
	(asan_no_sanitize_address_p): Likewise.
	* cfgexpand.c (partition_stack_vars): Consider
	asan_sanitize_use_after_scope in condition.
	(expand_stack_vars): Likewise.
	* common.opt (-fsanitize-address-use-after-scope): New option.
	* doc/invoke.texi (use-after-scope-direct-emission-threshold):
	Explain the parameter.
	* flag-types.h (enum sanitize_code): Define SANITIZE_USE_AFTER_SCOPE.
	* gimplify.c (build_asan_poison_call_expr): New function.
	(asan_poison_variable): Likewise.
	(gimplify_bind_expr): Generate poisoning/unpoisoning for local
	variables that have address taken.
	(gimplify_decl_expr): Likewise.
	(gimplify_target_expr): Likewise for C++ temporaries.
	(sort_by_decl_uid): New function.
	(gimplify_expr): Unpoison all variables for a label we can jump
	from outside of a scope.
	(gimplify_switch_expr): Unpoison variables defined in the switch
	context.
	(gimplify_function_tree): Clear asan_poisoned_variables.
	(asan_poison_variables): New function.
	(warn_switch_unreachable_r): Handle IFN_ASAN_MARK.
	* internal-fn.c (expand_ASAN_MARK): New function.
	* internal-fn.def (ASAN_MARK): Declare.
	* opts.c (finish_options): Handle -fstack-reuse if
	-fsanitize-address-use-after-scope is enabled.
	(common_handle_option): Enable address sanitization if
	-fsanitize-address-use-after-scope is enabled.
	* params.def (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD):
	New parameter.
	* params.h: Likewise.
	* sancov.c (pass_sanopt::execute): Handle IFN_ASAN_MARK.
	* sanitizer.def: Define __asan_poison_stack_memory and
	__asan_unpoison_stack_memory functions.
	* asan.c (asan_mark_poison_p): New function.
	(transform_statements): Handle asan_mark_poison_p calls.
	* gimple.c (nonfreeing_call_p): Handle IFN_ASAN_MARK.
---
 gcc/asan.c            | 302 +++++++++++++++++++++++++++++++++++++++++---------
 gcc/asan.h            |  66 +++++++++--
 gcc/c-family/c-warn.c |   9 +-
 gcc/cfgexpand.c       |  18 +--
 gcc/common.opt        |   3 +
 gcc/doc/invoke.texi   |  15 ++-
 gcc/gimple.c          |   3 +
 gcc/gimplify.c        | 234 +++++++++++++++++++++++++++++++++++---
 gcc/internal-fn.c     |   9 ++
 gcc/internal-fn.def   |   1 +
 gcc/opts.c            |  27 ++++-
 gcc/params.def        |   6 +
 gcc/params.h          |   2 +
 gcc/sanitizer.def     |   4 +
 gcc/sanopt.c          |   3 +
 15 files changed, 603 insertions(+), 99 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index c6d9240..1e0ce8d 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -245,6 +245,22 @@ static unsigned HOST_WIDE_INT asan_shadow_offset_value;
 static bool asan_shadow_offset_computed;
 static vec<char *> sanitized_sections;
 
+/* Return true if STMT is ASAN_MARK poisoning internal function call.  */
+static inline bool
+asan_mark_poison_p (gimple *stmt)
+{
+  return (gimple_call_internal_p (stmt, IFN_ASAN_MARK)
+	  && tree_to_uhwi (gimple_call_arg (stmt, 0)) == ASAN_MARK_CLOBBER);
+
+}
+
+/* Set of variable declarations that are going to be guarded by
+   use-after-scope sanitizer.  */
+
+static hash_set<tree> *asan_handled_variables = NULL;
+
+hash_set <tree> *asan_used_labels = NULL;
+
 /* Sets shadow offset to value in string VAL.  */
 
 bool
@@ -287,6 +303,14 @@ set_sanitized_sections (const char *sections)
     }
 }
 
+bool
+asan_sanitize_stack_p (void)
+{
+  return ((flag_sanitize & SANITIZE_ADDRESS)
+	  && ASAN_STACK
+	  && !asan_no_sanitize_address_p ());
+}
+
 /* Checks whether section SEC should be sanitized.  */
 
 static bool
@@ -315,22 +339,13 @@ asan_shadow_offset ()
 
 alias_set_type asan_shadow_set = -1;
 
-/* Pointer types to 1 resp. 2 byte integers in shadow memory.  A separate
+/* Pointer types to 1, 2 or 4 byte integers in shadow memory.  A separate
    alias set is used for all shadow memory accesses.  */
-static GTY(()) tree shadow_ptr_types[2];
+static GTY(()) tree shadow_ptr_types[3];
 
 /* Decl for __asan_option_detect_stack_use_after_return.  */
 static GTY(()) tree asan_detect_stack_use_after_return;
 
-/* Various flags for Asan builtins.  */
-enum asan_check_flags
-{
-  ASAN_CHECK_STORE = 1 << 0,
-  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
-  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_LAST = 1 << 3
-};
-
 /* Hashtable support for memory references used by gimple
    statements.  */
 
@@ -933,12 +948,16 @@ static void
 asan_init_shadow_ptr_types (void)
 {
   asan_shadow_set = new_alias_set ();
-  shadow_ptr_types[0] = build_distinct_type_copy (signed_char_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[0]) = asan_shadow_set;
-  shadow_ptr_types[0] = build_pointer_type (shadow_ptr_types[0]);
-  shadow_ptr_types[1] = build_distinct_type_copy (short_integer_type_node);
-  TYPE_ALIAS_SET (shadow_ptr_types[1]) = asan_shadow_set;
-  shadow_ptr_types[1] = build_pointer_type (shadow_ptr_types[1]);
+  tree types[3] = { signed_char_type_node, short_integer_type_node,
+		    integer_type_node };
+
+  for (unsigned i = 0; i < 3; i++)
+    {
+      shadow_ptr_types[i] = build_distinct_type_copy (types[i]);
+      TYPE_ALIAS_SET (shadow_ptr_types[i]) = asan_shadow_set;
+      shadow_ptr_types[i] = build_pointer_type (shadow_ptr_types[i]);
+    }
+
   initialize_sanitizer_builtins ();
 }
 
@@ -1022,6 +1041,15 @@ asan_function_start (void)
 			 current_function_funcdef_no);
 }
 
+/* Return number of shadow bytes that are occupied by a local variable
+   of SIZE bytes.  */
+
+static unsigned HOST_WIDE_INT
+shadow_mem_size (unsigned HOST_WIDE_INT size)
+{
+  return ROUND_UP (size, ASAN_SHADOW_GRANULARITY) / ASAN_SHADOW_GRANULARITY;
+}
+
 /* Insert code to protect stack vars.  The prologue sequence should be emitted
    directly, epilogue sequence returned.  BASE is the register holding the
    stack base, against which OFFSETS array offsets are relative to, OFFSETS
@@ -1047,7 +1075,7 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   HOST_WIDE_INT base_offset = offsets[length - 1];
   HOST_WIDE_INT base_align_bias = 0, offset, prev_offset;
   HOST_WIDE_INT asan_frame_size = offsets[0] - base_offset;
-  HOST_WIDE_INT last_offset, last_size;
+  HOST_WIDE_INT last_offset;
   int l;
   unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT;
   tree str_cst, decl, id;
@@ -1205,10 +1233,10 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
 				       (aoff - prev_offset)
 				       >> ASAN_SHADOW_SHIFT);
 	  prev_offset = aoff;
-	  for (i = 0; i < 4; i++, aoff += (1 << ASAN_SHADOW_SHIFT))
+	  for (i = 0; i < 4; i++, aoff += ASAN_SHADOW_GRANULARITY)
 	    if (aoff < offset)
 	      {
-		if (aoff < offset - (1 << ASAN_SHADOW_SHIFT) + 1)
+		if (aoff < offset - (HOST_WIDE_INT)ASAN_SHADOW_GRANULARITY + 1)
 		  shadow_bytes[i] = 0;
 		else
 		  shadow_bytes[i] = offset - aoff;
@@ -1282,35 +1310,66 @@ asan_emit_stack_protection (rtx base, rtx pbase, unsigned int alignb,
   if (STRICT_ALIGNMENT)
     set_mem_align (shadow_mem, (GET_MODE_ALIGNMENT (SImode)));
 
-  prev_offset = base_offset;
+  /* Unpoison shadow memory of a stack at the very end of a function.
+     As we're poisoning stack variables at the end of their scope,
+     shadow memory must be properly unpoisoned here.  The easiest approach
+     would be to collect all variables that should not be unpoisoned and
+     we unpoison shadow memory of the whole stack except ranges
+     occupied by these variables.  */
   last_offset = base_offset;
-  last_size = 0;
-  for (l = length; l; l -= 2)
+  HOST_WIDE_INT current_offset = last_offset;
+  if (length)
     {
-      offset = base_offset + ((offsets[l - 1] - base_offset)
-			     & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1));
-      if (last_offset + last_size != offset)
+      HOST_WIDE_INT var_end_offset = 0;
+      HOST_WIDE_INT stack_start = offsets[length - 1];
+      gcc_assert (last_offset == stack_start);
+
+      for (int l = length - 2; l > 0; l -= 2)
 	{
-	  shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				       (last_offset - prev_offset)
-				       >> ASAN_SHADOW_SHIFT);
-	  prev_offset = last_offset;
-	  asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
-	  last_offset = offset;
-	  last_size = 0;
+	  HOST_WIDE_INT var_offset = offsets[l];
+	  current_offset = var_offset;
+	  var_end_offset = offsets[l - 1];
+	  HOST_WIDE_INT rounded_size = ROUND_UP (var_end_offset - var_offset,
+					     BITS_PER_UNIT);
+
+	  /* Should we unpoison the variable?  */
+	  if (asan_handled_variables != NULL
+	      && asan_handled_variables->contains (decl))
+	    {
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  const char *n = (DECL_NAME (decl)
+				   ? IDENTIFIER_POINTER (DECL_NAME (decl))
+				   : "<unknown>");
+		  fprintf (dump_file, "Unpoisoning shadow stack for variable: "
+			   "%s (%" PRId64 "B)\n", n,
+			   var_end_offset - var_offset);
+		}
+
+	      unsigned HOST_WIDE_INT s
+		= shadow_mem_size (current_offset - last_offset);
+	      asan_clear_shadow (shadow_mem, s);
+	      HOST_WIDE_INT shift
+		= shadow_mem_size (current_offset - last_offset + rounded_size);
+	      shadow_mem = adjust_address (shadow_mem, VOIDmode, shift);
+	      last_offset = var_offset + rounded_size;
+	      current_offset = last_offset;
+	    }
+
 	}
-      last_size += base_offset + ((offsets[l - 2] - base_offset)
-				  & ~(ASAN_RED_ZONE_SIZE - HOST_WIDE_INT_1))
-		   - offset;
-    }
-  if (last_size)
-    {
-      shadow_mem = adjust_address (shadow_mem, VOIDmode,
-				   (last_offset - prev_offset)
-				   >> ASAN_SHADOW_SHIFT);
-      asan_clear_shadow (shadow_mem, last_size >> ASAN_SHADOW_SHIFT);
+
+      /* Handle last redzone.  */
+      current_offset = offsets[0];
+      asan_clear_shadow (shadow_mem,
+			 shadow_mem_size (current_offset - last_offset));
     }
 
+  /* Clean-up set with instrumented stack variables.  */
+  delete asan_handled_variables;
+  asan_handled_variables = NULL;
+  delete asan_used_labels;
+  asan_used_labels = NULL;
+
   do_pending_stack_adjust ();
   if (lab)
     emit_label (lab);
@@ -1590,12 +1649,14 @@ insert_if_then_before_iter (gcond *cond,
   gsi_insert_after (&cond_insert_point, cond, GSI_NEW_STMT);
 }
 
-/* Build
-   (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
+/* Build (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().
+   If RETURN_ADDRESS is set to true, return memory location instread
+   of a value in the shadow memory.  */
 
 static tree
 build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
-			 tree base_addr, tree shadow_ptr_type)
+			 tree base_addr, tree shadow_ptr_type,
+			 bool return_address = false)
 {
   tree t, uintptr_type = TREE_TYPE (base_addr);
   tree shadow_type = TREE_TYPE (shadow_ptr_type);
@@ -1618,11 +1679,15 @@ build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
   gimple_set_location (g, location);
   gsi_insert_after (gsi, g, GSI_NEW_STMT);
 
-  t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
-	      build_int_cst (shadow_ptr_type, 0));
-  g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
-  gimple_set_location (g, location);
-  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+  if (!return_address)
+    {
+      t = build2 (MEM_REF, shadow_type, gimple_assign_lhs (g),
+		  build_int_cst (shadow_ptr_type, 0));
+      g = gimple_build_assign (make_ssa_name (shadow_type), MEM_REF, t);
+      gimple_set_location (g, location);
+      gsi_insert_after (gsi, g, GSI_NEW_STMT);
+    }
+
   return gimple_assign_lhs (g);
 }
 
@@ -1826,7 +1891,9 @@ instrument_derefs (gimple_stmt_iterator *iter, tree t,
 	{
 	  /* Automatic vars in the current function will be always
 	     accessible.  */
-	  if (decl_function_context (inner) == current_function_decl)
+	  if (decl_function_context (inner) == current_function_decl
+	      && (!asan_sanitize_use_after_scope ()
+		  || !TREE_ADDRESSABLE (inner)))
 	    return;
 	}
       /* Always instrument external vars, they might be dynamically
@@ -2141,8 +2208,10 @@ transform_statements (void)
 		 If the current instruction is a function call that
 		 might free something, let's forget about the memory
 		 references that got instrumented.  Otherwise we might
-		 miss some instrumentation opportunities.  */
-	      if (is_gimple_call (s) && !nonfreeing_call_p (s))
+		 miss some instrumentation opportunities.  Do the same
+		 for a ASAN_MARK poisoning internal function.  */
+	      if (is_gimple_call (s)
+		  && (!nonfreeing_call_p (s) || asan_mark_poison_p (s)))
 		empty_mem_ref_hash_table ();
 
 	      gsi_next (&i);
@@ -2576,6 +2645,131 @@ asan_finish_file (void)
   flag_sanitize |= SANITIZE_ADDRESS;
 }
 
+/* Poison or unpoison (depending on IS_CLOBBER variable) shadow memory based
+   on SHADOW address.  Newly added statements will be added to ITER with
+   given location LOC.  We mark SIZE bytes in shadow memory, where
+   LAST_CHUNK_SIZE is greater than zero in situation where we are at the
+   end of a variable.  */
+
+static void
+asan_store_shadow_bytes (gimple_stmt_iterator *iter, location_t loc,
+			 tree shadow,
+			 unsigned HOST_WIDE_INT base_addr_offset,
+			 bool is_clobber, unsigned size,
+			 unsigned last_chunk_size)
+{
+  tree shadow_ptr_type;
+
+  switch (size)
+    {
+    case 1:
+      shadow_ptr_type = shadow_ptr_types[0];
+      break;
+    case 2:
+      shadow_ptr_type = shadow_ptr_types[1];
+      break;
+    case 4:
+      shadow_ptr_type = shadow_ptr_types[2];
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  unsigned char c = (char) is_clobber ? ASAN_STACK_MAGIC_USE_AFTER_SCOPE : 0;
+  unsigned HOST_WIDE_INT val = 0;
+  for (unsigned i = 0; i < size; ++i)
+    {
+      unsigned char shadow_c = c;
+      if (i == size - 1 && last_chunk_size && !is_clobber)
+	shadow_c = last_chunk_size;
+      val |= (unsigned HOST_WIDE_INT) shadow_c << (BITS_PER_UNIT * i);
+    }
+
+  /* Handle last chunk in unpoisoning.  */
+  tree magic = build_int_cst (TREE_TYPE (shadow_ptr_type), val);
+
+  tree dest = build2 (MEM_REF, TREE_TYPE (shadow_ptr_type), shadow,
+		      build_int_cst (shadow_ptr_type, base_addr_offset));
+
+  gimple *g = gimple_build_assign (dest, magic);
+  gimple_set_location (g, loc);
+  gsi_insert_after (iter, g, GSI_NEW_STMT);
+}
+
+/* Expand the ASAN_MARK builtins.  */
+
+bool
+asan_expand_mark_ifn (gimple_stmt_iterator *iter)
+{
+  gimple *g = gsi_stmt (*iter);
+  location_t loc = gimple_location (g);
+  HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
+  gcc_assert (flags < ASAN_MARK_LAST);
+  bool is_clobber = (flags & ASAN_MARK_CLOBBER) != 0;
+
+  tree base = gimple_call_arg (g, 1);
+  gcc_checking_assert (TREE_CODE (base) == ADDR_EXPR);
+  tree decl = TREE_OPERAND (base, 0);
+  gcc_checking_assert (TREE_CODE (decl) == VAR_DECL);
+  if (asan_handled_variables == NULL)
+    asan_handled_variables = new hash_set<tree> (16);
+  asan_handled_variables->add (decl);
+  tree len = gimple_call_arg (g, 2);
+
+  gcc_assert (tree_fits_shwi_p (len));
+  unsigned HOST_WIDE_INT size_in_bytes = tree_to_shwi (len);
+  gcc_assert (size_in_bytes);
+
+  g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			   NOP_EXPR, base);
+  gimple_set_location (g, loc);
+  gsi_replace (iter, g, false);
+  tree base_addr = gimple_assign_lhs (g);
+
+  /* Generate direct emission if size_in_bytes is small.  */
+  if (size_in_bytes <= ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD)
+    {
+      unsigned HOST_WIDE_INT shadow_size = shadow_mem_size (size_in_bytes);
+
+      tree shadow = build_shadow_mem_access (iter, loc, base_addr,
+					     shadow_ptr_types[0], true);
+
+      for (unsigned HOST_WIDE_INT offset = 0; offset < shadow_size;)
+	{
+	  unsigned size = 1;
+	  if (shadow_size - offset >= 4)
+	    size = 4;
+	  else if (shadow_size - offset >= 2)
+	    size = 2;
+
+	  unsigned HOST_WIDE_INT last_chunk_size = 0;
+	  unsigned HOST_WIDE_INT s = (offset + size) * ASAN_SHADOW_GRANULARITY;
+	  if (s > size_in_bytes)
+	    last_chunk_size = ASAN_SHADOW_GRANULARITY - (s - size_in_bytes);
+
+	  asan_store_shadow_bytes (iter, loc, shadow, offset, is_clobber,
+				   size, last_chunk_size);
+	  offset += size;
+	}
+    }
+  else
+    {
+      g = gimple_build_assign (make_ssa_name (pointer_sized_int_node),
+			       NOP_EXPR, len);
+      gimple_set_location (g, loc);
+      gsi_insert_before (iter, g, GSI_SAME_STMT);
+      tree sz_arg = gimple_assign_lhs (g);
+
+      tree fun = builtin_decl_implicit (is_clobber ? BUILT_IN_ASAN_CLOBBER_N
+					: BUILT_IN_ASAN_UNCLOBBER_N);
+      g = gimple_build_call (fun, 2, base_addr, sz_arg);
+      gimple_set_location (g, loc);
+      gsi_insert_after (iter, g, GSI_NEW_STMT);
+    }
+
+  return false;
+}
+
 /* Expand the ASAN_{LOAD,STORE} builtins.  */
 
 bool
diff --git a/gcc/asan.h b/gcc/asan.h
index 7ec693f..042af1f 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -29,6 +29,7 @@ extern bool asan_protect_global (tree);
 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 gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
@@ -36,9 +37,14 @@ extern gimple_stmt_iterator create_cond_insert_point
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
+/* Hash set of labels that are either used in a goto, or their address
+   has been taken.  */
+extern hash_set <tree> *asan_used_labels;
+
 /* Shadow memory is found at
    (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset ().  */
 #define ASAN_SHADOW_SHIFT	3
+#define ASAN_SHADOW_GRANULARITY (1UL << ASAN_SHADOW_SHIFT)
 
 /* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
    up to 2 * ASAN_RED_ZONE_SIZE - 1 bytes.  */
@@ -50,22 +56,32 @@ extern alias_set_type asan_shadow_set;
    the frame.  Middle is for padding in between variables, right is
    above the last protected variable and partial immediately after variables
    up to ASAN_RED_ZONE_SIZE alignment.  */
-#define ASAN_STACK_MAGIC_LEFT		0xf1
-#define ASAN_STACK_MAGIC_MIDDLE		0xf2
-#define ASAN_STACK_MAGIC_RIGHT		0xf3
-#define ASAN_STACK_MAGIC_PARTIAL	0xf4
-#define ASAN_STACK_MAGIC_USE_AFTER_RET	0xf5
+#define ASAN_STACK_MAGIC_LEFT		  0xf1
+#define ASAN_STACK_MAGIC_MIDDLE		  0xf2
+#define ASAN_STACK_MAGIC_RIGHT		  0xf3
+#define ASAN_STACK_MAGIC_PARTIAL	  0xf4
+#define ASAN_STACK_MAGIC_USE_AFTER_RET	  0xf5
+#define ASAN_STACK_MAGIC_USE_AFTER_SCOPE  0xf8
 
 #define ASAN_STACK_FRAME_MAGIC		0x41b58ab3
 #define ASAN_STACK_RETIRED_MAGIC	0x45e0360e
 
-/* Return true if DECL should be guarded on the stack.  */
-
-static inline bool
-asan_protect_stack_decl (tree decl)
+/* Various flags for Asan builtins.  */
+enum asan_check_flags
 {
-  return DECL_P (decl) && !DECL_ARTIFICIAL (decl);
-}
+  ASAN_CHECK_STORE = 1 << 0,
+  ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
+  ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
+  ASAN_CHECK_LAST = 1 << 3
+};
+
+/* Flags for Asan check builtins.  */
+enum asan_mark_flags
+{
+  ASAN_MARK_CLOBBER = 1 << 0,
+  ASAN_MARK_UNCLOBBER = 1 << 1,
+  ASAN_MARK_LAST = 1 << 2
+};
 
 /* Return the size of padding needed to insert after a protected
    decl of SIZE.  */
@@ -81,6 +97,8 @@ extern bool set_asan_shadow_offset (const char *);
 
 extern void set_sanitized_sections (const char *);
 
+extern bool asan_sanitize_stack_p (void);
+
 /* Return TRUE if builtin with given FCODE will be intercepted by
    libasan.  */
 
@@ -105,4 +123,30 @@ asan_intercepted_p (enum built_in_function fcode)
 	 || fcode == BUILT_IN_STRNCMP
 	 || fcode == BUILT_IN_STRNCPY;
 }
+
+/* Return TRUE if we should instrument for use-after-scope sanity checking.  */
+
+static inline bool
+asan_sanitize_use_after_scope (void)
+{
+  return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
+}
+
+static inline bool
+asan_no_sanitize_address_p (void)
+{
+  return lookup_attribute ("no_sanitize_address",
+			   DECL_ATTRIBUTES (current_function_decl));
+}
+
+/* Return true if DECL should be guarded on the stack.  */
+
+static inline bool
+asan_protect_stack_decl (tree decl)
+{
+  return DECL_P (decl)
+    && (!DECL_ARTIFICIAL (decl)
+	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/c-family/c-warn.c b/gcc/c-family/c-warn.c
index 904f6d3..18ee247 100644
--- a/gcc/c-family/c-warn.c
+++ b/gcc/c-family/c-warn.c
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "diagnostic.h"
 #include "intl.h"
-
+#include "asan.h"
 
 /* Print a warning if a constant expression had overflow in folding.
    Invoke this function on every expression that the language
@@ -1627,6 +1627,13 @@ warn_for_unused_label (tree label)
       else
 	warning (OPT_Wunused_label, "label %q+D declared but not defined", label);
     }
+  else if (asan_sanitize_use_after_scope ())
+    {
+      if (asan_used_labels == NULL)
+	asan_used_labels = new hash_set<tree> (16);
+
+      asan_used_labels->add (label);
+    }
 }
 
 /* Warn for division by zero according to the value of DIVISOR.  LOC
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 430ad38..7ffb558 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -868,18 +868,6 @@ union_stack_vars (size_t a, size_t b)
     }
 }
 
-/* Return true if the current function should have its stack frame
-   protected by address sanitizer.  */
-
-static inline bool
-asan_sanitize_stack_p (void)
-{
-  return ((flag_sanitize & SANITIZE_ADDRESS)
-	  && ASAN_STACK
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl)));
-}
-
 /* A subroutine of expand_used_vars.  Binpack the variables into
    partitions constrained by the interference graph.  The overall
    algorithm used is as follows:
@@ -941,7 +929,8 @@ partition_stack_vars (void)
 	     sizes, as the shorter vars wouldn't be adequately protected.
 	     Don't do that for "large" (unsupported) alignment objects,
 	     those aren't protected anyway.  */
-	  if (asan_sanitize_stack_p () && isize != jsize
+	  if ((asan_sanitize_stack_p ())
+	      && isize != jsize
 	      && ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	    break;
 
@@ -1128,7 +1117,8 @@ expand_stack_vars (bool (*pred) (size_t), struct stack_vars_data *data)
       if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
 	{
 	  base = virtual_stack_vars_rtx;
-	  if (asan_sanitize_stack_p () && pred)
+	  if ((asan_sanitize_stack_p ())
+	      && pred)
 	    {
 	      HOST_WIDE_INT prev_offset
 		= align_base (frame_offset,
diff --git a/gcc/common.opt b/gcc/common.opt
index 097cae6..a60bebf 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -986,6 +986,9 @@ fsanitize-recover
 Common Report
 This switch is deprecated; use -fsanitize-recover= instead.
 
+fsanitize-address-use-after-scope
+Common Driver Report Var(flag_sanitize_address_use_after_scope) Init(0)
+
 fsanitize-undefined-trap-on-error
 Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
 Use trap instead of a library function for undefined behavior sanitization.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 089661b..e0afc26 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10290,6 +10290,10 @@ is greater or equal to this number, use callbacks instead of inline checks.
 E.g. to disable inline code use
 @option{--param asan-instrumentation-with-call-threshold=0}.
 
+@item use-after-scope-direct-emission-threshold
+If size of a local variables in bytes is smaller of equal to this number,
+direct instruction emission is utilized to poison and unpoison local variables.
+
 @item chkp-max-ctor-size
 Static constructors generated by Pointer Bounds Checker may become very
 large and significantly increase compile time at optimization level
@@ -10500,6 +10504,7 @@ thread-safe code.
 Enable AddressSanitizer, a fast memory error detector.
 Memory access instructions are instrumented to detect
 out-of-bounds and use-after-free bugs.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/sanitizers/wiki/AddressSanitizer} for
 more details.  The run-time behavior can be influenced using the
 @env{ASAN_OPTIONS} environment variable.  When set to @code{help=1},
@@ -10511,6 +10516,7 @@ The option can't be combined with @option{-fsanitize=thread}.
 @item -fsanitize=kernel-address
 @opindex fsanitize=kernel-address
 Enable AddressSanitizer for Linux kernel.
+The option enables @option{-fsanitize-address-use-after-scope}.
 See @uref{https://github.com/google/kasan/wiki} for more details.
 
 @item -fsanitize=thread
@@ -10710,8 +10716,8 @@ except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
 @option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
 @option{-fsanitize=bounds-strict},
 @option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
-For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
-for which this feature is experimental.
+For these sanitizers error recovery is turned on by default,
+except @option{-fsanitize=address}, for which this feature is experimental.
 @option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
 accepted, the former enables recovery for all sanitizers that support it,
 the latter disables recovery for all sanitizers that support it.
@@ -10733,6 +10739,11 @@ Similarly @option{-fno-sanitize-recover} is equivalent to
 -fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
 @end smallexample
 
+@item -fsanitize-address-use-after-scope
+@opindex fsanitize-address-use-after-scope
+Enable sanitization of local variables to detect use-after-scope bugs.
+The option sets @option{-fstack-reuse} to @samp{none}.
+
 @item -fsanitize-undefined-trap-on-error
 @opindex fsanitize-undefined-trap-on-error
 The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 26e9322..0a3dc72 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "selftest.h"
 #include "gimple-pretty-print.h"
+#include "asan.h"
 
 
 /* All the tuples have their operand vector (if present) at the very bottom
@@ -2629,6 +2630,8 @@ nonfreeing_call_p (gimple *call)
       {
       case IFN_ABNORMAL_DISPATCHER:
         return true;
+      case IFN_ASAN_MARK:
+	return tree_to_uhwi (gimple_call_arg (call, 0)) == ASAN_MARK_UNCLOBBER;
       default:
 	if (gimple_call_flags (call) & ECF_LEAF)
 	  return true;
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 1531582..e5930e6 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -59,6 +59,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
+#include "asan.h"
+
+/* Hash set of poisoned variables in a bind expr.  */
+static hash_set<tree> *asan_poisoned_variables = NULL;
 
 enum gimplify_omp_var_data
 {
@@ -151,6 +155,7 @@ struct gimplify_ctx
   tree return_temp;
 
   vec<tree> case_labels;
+  hash_set<tree> *live_switch_vars;
   /* The formal temporary table.  Should this be persistent?  */
   hash_table<gimplify_hasher> *temp_htab;
 
@@ -1088,6 +1093,121 @@ build_stack_save_restore (gcall **save, gcall **restore)
 			 1, tmp_var);
 }
 
+/* Generate IFN_ASAN_MARK call that poisons shadow of a for DECL variable.  */
+
+static tree
+build_asan_poison_call_expr (tree decl)
+{
+  /* Do not poison variables that have size equal to zero.  */
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  if (zerop (unit_size))
+    return NULL_TREE;
+
+  tree base = build_fold_addr_expr (decl);
+
+  return build_call_expr_internal_loc (UNKNOWN_LOCATION, IFN_ASAN_MARK,
+				       void_type_node, 3,
+				       build_int_cst (integer_type_node,
+						      ASAN_MARK_CLOBBER),
+				       base, unit_size);
+}
+
+/* Generate IFN_ASAN_MARK call that would poison or unpoison, depending
+   on POISON flag, shadow memory of a DECL variable.  The call will be
+   put on location identified by IT iterator, where BEFORE flag drives
+   position where the stmt will be put.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_stmt_iterator *it,
+		      bool before)
+{
+  /* When within an OMP context, do not emit ASAN_MARK internal fns.  */
+  if (gimplify_omp_ctxp)
+    return;
+
+  tree unit_size = DECL_SIZE_UNIT (decl);
+  tree base = build_fold_addr_expr (decl);
+
+  /* Do not poison variables that have size equal to zero.  */
+  if (zerop (unit_size))
+    return;
+
+  /* It's necessary to have all stack variables aligned to ASAN granularity
+     bytes.  */
+  if (DECL_ALIGN_UNIT (decl) <= ASAN_SHADOW_GRANULARITY)
+    SET_DECL_ALIGN (decl, BITS_PER_UNIT * ASAN_SHADOW_GRANULARITY);
+
+  HOST_WIDE_INT flags = poison ? ASAN_MARK_CLOBBER : ASAN_MARK_UNCLOBBER;
+
+  gimple *g
+    = gimple_build_call_internal (IFN_ASAN_MARK, 3,
+				  build_int_cst (integer_type_node, flags),
+				  base, unit_size);
+
+  if (before)
+    gsi_insert_before (it, g, GSI_NEW_STMT);
+  else
+    gsi_insert_after (it, g, GSI_NEW_STMT);
+}
+
+/* Generate IFN_ASAN_MARK internal call that depending on POISON flag
+   either poisons or unpoisons a DECL.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variable (tree decl, bool poison, gimple_seq *seq_p)
+{
+  gimple_stmt_iterator it = gsi_last (*seq_p);
+  bool before = false;
+
+  if (gsi_end_p (it))
+    before = true;
+
+  asan_poison_variable (decl, poison, &it, before);
+}
+
+/* Sort pair of VAR_DECLs A and B by DECL_UID.  */
+
+static int
+sort_by_decl_uid (const void *a, const void *b)
+{
+  const tree *t1 = (const tree *)a;
+  const tree *t2 = (const tree *)b;
+
+  int uid1 = DECL_UID (*t1);
+  int uid2 = DECL_UID (*t2);
+
+  if (uid1 < uid2)
+    return -1;
+  else if (uid1 > uid2)
+    return 1;
+  else
+    return 0;
+}
+
+/* Generate IFN_ASAN_MARK internal call for all VARIABLES
+   depending on POISON flag.  Created statement is appended
+   to SEQ_P gimple sequence.  */
+
+static void
+asan_poison_variables (hash_set<tree> *variables, bool poison, gimple_seq *seq_p)
+{
+  unsigned c = variables->elements ();
+  if (c == 0)
+    return;
+
+  auto_vec<tree> sorted_variables (c);
+
+  for (hash_set<tree>::iterator it = variables->begin ();
+       it != variables->end (); ++it)
+    sorted_variables.safe_push (*it);
+
+  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);
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -1231,6 +1351,17 @@ gimplify_bind_expr (tree *expr_p, gimple_seq *pre_p)
 		}
 	    }
 	}
+
+      if (asan_poisoned_variables != NULL
+	  && asan_poisoned_variables->contains (t))
+	{
+	  asan_poisoned_variables->remove (t);
+	  asan_poison_variable (t, true, &cleanup);
+	}
+
+      if (gimplify_ctxp->live_switch_vars != NULL
+	  && gimplify_ctxp->live_switch_vars->contains (t))
+	gimplify_ctxp->live_switch_vars->remove (t);
     }
 
   if (ret_clauses)
@@ -1475,13 +1606,29 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
   if (VAR_P (decl) && !DECL_EXTERNAL (decl))
     {
       tree init = DECL_INITIAL (decl);
+      bool is_vla = false;
 
       if (TREE_CODE (DECL_SIZE_UNIT (decl)) != INTEGER_CST
 	  || (!TREE_STATIC (decl)
 	      && flag_stack_check == GENERIC_STACK_CHECK
 	      && compare_tree_int (DECL_SIZE_UNIT (decl),
 				   STACK_CHECK_MAX_VAR_SIZE) > 0))
-	gimplify_vla_decl (decl, seq_p);
+	{
+	  gimplify_vla_decl (decl, seq_p);
+	  is_vla = true;
+	}
+
+      if (asan_sanitize_use_after_scope ()
+	  && !asan_no_sanitize_address_p ()
+	  && !is_vla
+	  && TREE_ADDRESSABLE (decl)
+	  && !TREE_STATIC (decl))
+	{
+	  asan_poisoned_variables->add (decl);
+	  asan_poison_variable (decl, false, seq_p);
+	  if (gimplify_ctxp->live_switch_vars)
+	    gimplify_ctxp->live_switch_vars->add (decl);
+	}
 
       /* Some front ends do not explicitly declare all anonymous
 	 artificial variables.  We compensate here by declaring the
@@ -1591,6 +1738,13 @@ warn_switch_unreachable_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
       /* Walk the sub-statements.  */
       *handled_ops_p = false;
       break;
+    case GIMPLE_CALL:
+      if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
+	{
+	  *handled_ops_p = false;
+	  break;
+	}
+      /* Fall through.  */
     default:
       /* Save the first "real" statement (not a decl/lexical scope/...).  */
       wi->info = stmt;
@@ -1802,6 +1956,8 @@ collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
 	  if (find_label_entry (labels, label))
 	    prev = gsi_stmt (*gsi_p);
 	}
+      else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
+	;
       else
 	prev = gsi_stmt (*gsi_p);
       gsi_next (gsi_p);
@@ -2082,6 +2238,7 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
     {
       vec<tree> labels;
       vec<tree> saved_labels;
+      hash_set<tree> *saved_live_switch_vars;
       tree default_case = NULL_TREE;
       gswitch *switch_stmt;
 
@@ -2093,6 +2250,8 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
          labels.  Save all the things from the switch body to append after.  */
       saved_labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels.create (8);
+      saved_live_switch_vars = gimplify_ctxp->live_switch_vars;
+      gimplify_ctxp->live_switch_vars = new hash_set<tree> (4);
       bool old_in_switch_expr = gimplify_ctxp->in_switch_expr;
       gimplify_ctxp->in_switch_expr = true;
 
@@ -2107,6 +2266,9 @@ gimplify_switch_expr (tree *expr_p, gimple_seq *pre_p)
 
       labels = gimplify_ctxp->case_labels;
       gimplify_ctxp->case_labels = saved_labels;
+      gcc_assert (gimplify_ctxp->live_switch_vars->elements () == 0);
+      delete gimplify_ctxp->live_switch_vars;
+      gimplify_ctxp->live_switch_vars = saved_live_switch_vars;
 
       preprocess_case_label_vec_for_gimple (labels, index_type,
 					    &default_case);
@@ -6164,6 +6326,9 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   tree init = TARGET_EXPR_INITIAL (targ);
   enum gimplify_status ret;
 
+  bool unpoison_empty_seq = false;
+  gimple_stmt_iterator unpoison_it;
+
   if (init)
     {
       tree cleanup = NULL_TREE;
@@ -6177,7 +6342,14 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	  gimplify_vla_decl (temp, pre_p);
 	}
       else
-	gimple_add_tmp_var (temp);
+	{
+	  /* Save location where we need to place unpoisoning.  It's possible
+	     that a variable will be converted to needs_to_live_in_memory.  */
+	  unpoison_it = gsi_last (*pre_p);
+	  unpoison_empty_seq = gsi_end_p (unpoison_it);
+
+	  gimple_add_tmp_var (temp);
+	}
 
       /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
 	 expression is supposed to initialize the slot.  */
@@ -6213,20 +6385,34 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       /* Add a clobber for the temporary going out of scope, like
 	 gimplify_bind_expr.  */
       if (gimplify_ctxp->in_cleanup_point_expr
-	  && needs_to_live_in_memory (temp)
-	  && flag_stack_reuse == SR_ALL)
-	{
-	  tree clobber = build_constructor (TREE_TYPE (temp),
-					    NULL);
-	  TREE_THIS_VOLATILE (clobber) = true;
-	  clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
-	  if (cleanup)
-	    cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
-			      clobber);
-	  else
-	    cleanup = clobber;
-	}
+	  && needs_to_live_in_memory (temp))
+	{
+	  if (flag_stack_reuse == SR_ALL)
+	    {
+	      tree clobber = build_constructor (TREE_TYPE (temp),
+						NULL);
+	      TREE_THIS_VOLATILE (clobber) = true;
+	      clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+	      if (cleanup)
+		cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+				  clobber);
+	      else
+		cleanup = clobber;
+	    }
+	  if (asan_sanitize_use_after_scope ())
+	    {
+	      tree asan_cleanup = build_asan_poison_call_expr (temp);
+	      if (asan_cleanup)
+		{
+		  if (unpoison_empty_seq)
+		    unpoison_it = gsi_start (*pre_p);
 
+		  asan_poison_variable (temp, false, &unpoison_it,
+					unpoison_empty_seq);
+		  gimple_push_cleanup (temp, asan_cleanup, false, pre_p);
+		}
+	    }
+	}
       if (cleanup)
 	gimple_push_cleanup (temp, cleanup, false, pre_p);
 
@@ -10824,6 +11010,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   location_t saved_location;
   enum gimplify_status ret;
   gimple_stmt_iterator pre_last_gsi, post_last_gsi;
+  tree label;
 
   save_expr = *expr_p;
   if (save_expr == NULL_TREE)
@@ -11239,10 +11426,24 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
 	case LABEL_EXPR:
 	  ret = gimplify_label_expr (expr_p, pre_p);
+	  label = LABEL_EXPR_LABEL (*expr_p);
+	  gcc_assert (decl_function_context (label) == current_function_decl);
+
+	  /* If the label is used in a goto statement, or address of the label
+	     is taken, we need to unpoison all variables that were seen so far.
+	     Doing so would prevent us from reporting a false positives.  */
+	  if (asan_sanitize_use_after_scope ()
+	      && asan_used_labels != NULL
+	      && asan_used_labels->contains (label))
+	    asan_poison_variables (asan_poisoned_variables, false, pre_p);
 	  break;
 
 	case CASE_LABEL_EXPR:
 	  ret = gimplify_case_label_expr (expr_p, pre_p);
+
+	  if (gimplify_ctxp->live_switch_vars)
+	    asan_poison_variables (gimplify_ctxp->live_switch_vars, false,
+				   pre_p);
 	  break;
 
 	case RETURN_EXPR:
@@ -12336,7 +12537,10 @@ gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
+  asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
+  delete asan_poisoned_variables;
+  asan_poisoned_variables = NULL;
 
   /* The tree body of the function is no longer needed, replace it
      with the new GIMPLE body.  */
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 168adc6..cbee97e 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -237,6 +237,15 @@ expand_ASAN_CHECK (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_ASAN_MARK (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 cf2c402..6a0a7f6 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,6 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
 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/opts.c b/gcc/opts.c
index d381cb5..2f230ce 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -979,6 +979,25 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
       opts->x_flag_aggressive_loop_optimizations = 0;
       opts->x_flag_strict_overflow = 0;
     }
+
+  /* Enable -fsanitize-address-use-after-scope if address sanitizer is
+     enabled.  */
+  if (opts->x_flag_sanitize
+      && !opts_set->x_flag_sanitize_address_use_after_scope)
+    opts->x_flag_sanitize_address_use_after_scope = true;
+
+  /* Force -fstack-reuse=none in case -fsanitize-address-use-after-scope
+     is enabled.  */
+  if (opts->x_flag_sanitize_address_use_after_scope)
+    {
+      if (opts->x_flag_stack_reuse != SR_NONE
+	  && opts_set->x_flag_stack_reuse != SR_NONE)
+	error_at (loc,
+		  "-fsanitize-address-use-after-scope requires "
+		  "-fstack-reuse=none option");
+
+      opts->x_flag_stack_reuse = SR_NONE;
+    }
 }
 
 #define LEFT_COLUMN	27
@@ -1452,8 +1471,8 @@ const struct sanitizer_opts_s sanitizer_opts[] =
 {
 #define SANITIZER_OPT(name, flags, recover) \
     { #name, flags, sizeof #name - 1, recover }
-  SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
-  SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
+  SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true),
+  SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
 		 true),
   SANITIZER_OPT (thread, SANITIZE_THREAD, false),
   SANITIZER_OPT (leak, SANITIZE_LEAK, false),
@@ -1781,6 +1800,10 @@ common_handle_option (struct gcc_options *opts,
       /* Deferred.  */
       break;
 
+    case OPT_fsanitize_address_use_after_scope:
+      opts->x_flag_sanitize_address_use_after_scope = value;
+      break;
+
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
diff --git a/gcc/params.def b/gcc/params.def
index ab3eb3d..89f7093 100644
--- a/gcc/params.def
+++ b/gcc/params.def
@@ -1168,6 +1168,12 @@ DEFPARAM (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD,
          "in function becomes greater or equal to this number.",
          7000, 0, INT_MAX)
 
+DEFPARAM (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD,
+	 "use-after-scope-direct-emission-threshold",
+	 "Use direct poisoning/unpoisoning intructions for variables "
+	 "smaller or equal to this number.",
+	 256, 0, INT_MAX)
+
 DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
 	  "uninit-control-dep-attempts",
 	  "Maximum number of nested calls to search for control dependencies "
diff --git a/gcc/params.h b/gcc/params.h
index 97c8d56..0a2905c 100644
--- a/gcc/params.h
+++ b/gcc/params.h
@@ -244,5 +244,7 @@ extern void init_param_values (int *params);
   PARAM_VALUE (PARAM_ASAN_USE_AFTER_RETURN)
 #define ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD \
   PARAM_VALUE (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD)
+#define ASAN_PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD \
+  ((unsigned) PARAM_VALUE (PARAM_USE_AFTER_SCOPE_DIRECT_EMISSION_THRESHOLD))
 
 #endif /* ! GCC_PARAMS_H */
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 303c1e4..1c142e9 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 8a6fbe9..320e14e 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -732,6 +732,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_ASAN_CHECK:
 		  no_next = asan_expand_check_ifn (&gsi, use_calls);
 		  break;
+		case IFN_ASAN_MARK:
+		  no_next = asan_expand_mark_ifn (&gsi);
+		  break;
 		default:
 		  break;
 		}
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope (v3)
  2016-10-03  9:30             ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope Martin Liška
@ 2016-11-07 10:04               ` Martin Liška
  2016-11-07 10:09                 ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-07 10:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 36 bytes --]

Third version of the patch.

Martin

[-- Attachment #2: 0002-Introduce-tests-for-fsanitize-address-use-after-scop.patch --]
[-- Type: text/x-patch, Size: 22559 bytes --]

From e790d926afd3d2d6ad41d14d1e91698bf651b41a Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 19 Sep 2016 17:39:29 +0200
Subject: [PATCH 2/2] Introduce tests for -fsanitize-address-use-after-scope

gcc/testsuite/ChangeLog:

2016-09-26  Martin Liska  <mliska@suse.cz>

	* c-c++-common/asan/force-inline-opt0-1.c: Disable
	-f-sanitize-address-use-after-scope.
	* c-c++-common/asan/inc.c: Change number of expected ASAN_CHECK
	internal fn calls.
	* g++.dg/asan/use-after-scope-1.C: New test.
	* g++.dg/asan/use-after-scope-2.C: Likewise.
	* g++.dg/asan/use-after-scope-3.C: Likewise.
	* g++.dg/asan/use-after-scope-types-1.C: Likewise.
	* g++.dg/asan/use-after-scope-types-2.C: Likewise.
	* g++.dg/asan/use-after-scope-types-3.C: Likewise.
	* g++.dg/asan/use-after-scope-types-4.C: Likewise.
	* g++.dg/asan/use-after-scope-types-5.C: Likewise.
	* g++.dg/asan/use-after-scope-types.h: Likewise.
	* gcc.dg/asan/use-after-scope-1.c: Likewise.
	* gcc.dg/asan/use-after-scope-2.c: Likewise.
	* gcc.dg/asan/use-after-scope-3.c: Likewise.
	* gcc.dg/asan/use-after-scope-4.c: Likewise.
	* gcc.dg/asan/use-after-scope-5.c: Likewise.
	* gcc.dg/asan/use-after-scope-6.c: Likewise.
	* gcc.dg/asan/use-after-scope-7.c: Likewise.
	* gcc.dg/asan/use-after-scope-8.c: Likewise.
	* gcc.dg/asan/use-after-scope-9.c: Likewise.
	* gcc.dg/asan/use-after-scope-switch-1.c: Likewise.
	* gcc.dg/asan/use-after-scope-switch-2.c: Likewise.
	* gcc.dg/asan/use-after-scope-switch-3.c: Likewise.
	* gcc.dg/asan/use-after-scope-goto-1.c: Likewise.
	* gcc.dg/asan/use-after-scope-goto-2.c: Likewise.
---
 .../c-c++-common/asan/force-inline-opt0-1.c        |  1 +
 gcc/testsuite/c-c++-common/asan/inc.c              |  3 +-
 gcc/testsuite/g++.dg/asan/use-after-scope-1.C      | 21 ++++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-2.C      | 40 ++++++++++++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-3.C      | 22 ++++++++++
 .../g++.dg/asan/use-after-scope-types-1.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-2.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-3.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-4.C          | 17 ++++++++
 .../g++.dg/asan/use-after-scope-types-5.C          | 17 ++++++++
 gcc/testsuite/g++.dg/asan/use-after-scope-types.h  | 30 ++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c      | 18 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c      | 47 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c      | 20 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c      | 16 ++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c      | 27 +++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-6.c      | 15 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-7.c      | 15 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c      | 14 +++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c      | 20 +++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c | 47 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c | 25 ++++++++++++
 .../gcc.dg/asan/use-after-scope-switch-1.c         | 25 ++++++++++++
 .../gcc.dg/asan/use-after-scope-switch-2.c         | 33 +++++++++++++++
 .../gcc.dg/asan/use-after-scope-switch-3.c         | 36 +++++++++++++++++
 25 files changed, 559 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-1.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-2.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-3.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-types.h
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-switch-1.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-switch-2.c
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-switch-3.c

diff --git a/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c b/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
index 0576155..2e156f7 100644
--- a/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
+++ b/gcc/testsuite/c-c++-common/asan/force-inline-opt0-1.c
@@ -2,6 +2,7 @@
    (before and after inlining) */
 
 /* { dg-do compile } */
+/* { dg-options "-fno-sanitize-address-use-after-scope" } */
 /* { dg-final { scan-assembler-not "__asan_report_load" } } */
 
 __attribute__((always_inline))
diff --git a/gcc/testsuite/c-c++-common/asan/inc.c b/gcc/testsuite/c-c++-common/asan/inc.c
index 5abf373..98121d2 100644
--- a/gcc/testsuite/c-c++-common/asan/inc.c
+++ b/gcc/testsuite/c-c++-common/asan/inc.c
@@ -16,5 +16,6 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "ASAN_" 1 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_" 4 "asan0" } }  */
 /* { dg-final { scan-tree-dump "ASAN_CHECK \\(.*, 4\\);" "asan0" } }  */
+/* { dg-final { scan-tree-dump "ASAN_CHECK \\(.*, 8\\);" "asan0" } }  */
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
new file mode 100644
index 0000000..fd875ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-1.C
@@ -0,0 +1,21 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include <functional>
+
+int main() {
+  std::function<int()> function;
+  {
+    int v = 0;
+    function = [&v]()
+    {
+      return v;
+    };
+  }
+  return function();
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'v' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
new file mode 100644
index 0000000..92a4bd1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-2.C
@@ -0,0 +1,40 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include <stdio.h>
+
+struct Test
+{
+  Test ()
+    {
+      my_value = 0;
+    }
+
+  ~Test ()
+    {
+      fprintf (stderr, "Value: %d\n", *my_value);
+    }
+
+  void init (int *v)
+    {
+      my_value = v;
+    }
+
+  int *my_value;
+};
+
+int main(int argc, char **argv)
+{
+  Test t;
+
+  {
+    int x = argc;
+    t.init(&x);
+  }
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-3.C b/gcc/testsuite/g++.dg/asan/use-after-scope-3.C
new file mode 100644
index 0000000..172f374
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-3.C
@@ -0,0 +1,22 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+struct IntHolder {
+  int val;
+};
+
+const IntHolder *saved;
+
+void save(const IntHolder &holder) {
+  saved = &holder;
+}
+
+int main(int argc, char *argv[]) {
+  save({10});
+  int x = saved->val;  // BOOM
+  return x;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'<unknown>' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
new file mode 100644
index 0000000..bedcfa4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-1.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<bool>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
new file mode 100644
index 0000000..75a01d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-2.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<float>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
new file mode 100644
index 0000000..3350c69
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-3.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<void *>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
new file mode 100644
index 0000000..dd06e94
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<std::vector<std::string>>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 8 at" }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
new file mode 100644
index 0000000..42abc2a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-5.C
@@ -0,0 +1,17 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+#include "use-after-scope-types.h"
+
+int main()
+{
+  using Tests = void (*)();
+  Tests t = &test<char[1000]>;
+  t();
+
+  return 0;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size " }
+// { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types.h b/gcc/testsuite/g++.dg/asan/use-after-scope-types.h
new file mode 100644
index 0000000..b96b02b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types.h
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+template <class T> struct Ptr {
+  void Store(T *ptr) { t = ptr; }
+
+  void Access() { *t = {}; }
+
+  T *t;
+};
+
+template <class T, size_t N> struct Ptr<T[N]> {
+  using Type = T[N];
+  void Store(Type *ptr) { t = *ptr; }
+
+  void Access() { *t = {}; }
+
+  T *t;
+};
+
+template <class T> __attribute__((noinline)) void test() {
+  Ptr<T> ptr;
+  {
+    T x;
+    ptr.Store(&x);
+  }
+
+  ptr.Access();
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
new file mode 100644
index 0000000..bdbc97b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-1.c
@@ -0,0 +1,18 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  return *(ptr+8);
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
new file mode 100644
index 0000000..dedb734
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-2.c
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int *bar (int *x, int *y) { return y; }
+
+int foo (void)
+{
+  char *p;
+  {
+    char a = 0;
+    p = &a;
+  }
+
+  if (*p)
+    return 1;
+  else
+    return 0;
+}
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  int a[16];
+  int *p, *q = a;
+  {
+    int b[16];
+    p = bar (a, b);
+  }
+  bar (a, q);
+  {
+    int c[16];
+    q = bar (a, c);
+  }
+  int v = *bar (a, q);
+  return v;
+}
+
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'c' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
new file mode 100644
index 0000000..9aeed51
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-3.c
@@ -0,0 +1,20 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "WRITE of size 1 at.*" }
+// { dg-output ".*'my_char' <== Memory access at offset \[0-9\]* overflows this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
new file mode 100644
index 0000000..77d7052
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -0,0 +1,16 @@
+// { dg-do run }
+
+int
+__attribute__((no_sanitize_address))
+main (void)
+{
+  char *ptr;
+  char *ptr2;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
+  }
+
+  *(ptr2+9) = 'c';
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
new file mode 100644
index 0000000..b53712d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-5.c
@@ -0,0 +1,27 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int *ptr;
+
+__attribute__((always_inline))
+inline static void
+foo(int v)
+{
+  int values[10];
+  for (unsigned i = 0; i < 10; i++)
+    values[i] = v;
+
+  ptr = &values[3];
+}
+
+int
+main (int argc, char **argv)
+{
+  foo (argc);
+
+  return *ptr;
+}
+
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
+// { dg-output "READ of size 4 at.*" }
+// { dg-output ".*'values' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
new file mode 100644
index 0000000..bb13cec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-6.c
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-additional-options "--param asan-stack=0" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  *ptr = 'c';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
new file mode 100644
index 0000000..4115205
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-7.c
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-additional-options "-fno-sanitize-address-use-after-scope" }
+
+int
+main (void)
+{
+  char *ptr;
+  {
+    char my_char[9];
+    ptr = &my_char[0];
+  }
+
+  *ptr = 'c';
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
new file mode 100644
index 0000000..b204206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int
+fn1 ()
+{
+  int x = 123;
+  register int a asm("rdi") = 123;
+
+  return x * x;
+}
+
+/* { dg-final { scan-tree-dump-not "ASAN_CHECK" "asan0" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
new file mode 100644
index 0000000..2e30def
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-9.c
@@ -0,0 +1,20 @@
+// { dg-do run }
+// { dg-shouldfail "asan" }
+
+int
+main (int argc, char **argv)
+{
+  int *ptr = 0;
+
+  {
+    int a;
+    ptr = &a;
+    *ptr = 12345;
+  }
+
+  return *ptr;
+}
+
+// { 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/testsuite/gcc.dg/asan/use-after-scope-goto-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
new file mode 100644
index 0000000..c47a5e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-1.c
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+  int b = 123;
+  int c = 123;
+  int d = 123;
+  int e = 123;
+  int f = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    int *ptr2;
+    int *ptr3;
+    int *ptr4;
+    int *ptr5;
+    int *ptr6;
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	ptr2 = &b;
+        *ptr2 = 1;
+	ptr3 = &c;
+        *ptr3 = 1;
+	ptr4 = &d;
+        *ptr4 = 1;
+	ptr5 = &e;
+        *ptr5 = 1;
+	ptr6 = &f;
+        *ptr6 = 1;
+	return 0;
+      }
+  }
+  else
+    goto label;
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &c, 4\\);" 2 "asan0" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &e, 4\\);" 2 "asan0" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c
new file mode 100644
index 0000000..73ef4e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-goto-2.c
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-asan0" }
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+int main(int argc, char **argv)
+{
+  int a = 123;
+
+  if (argc == 0)
+  {
+    int *ptr;
+    /* The label is not used in &label or goto label.  Thus '&a' should be
+       marked just once.  */
+    label:
+      {
+	ptr = &a;
+        *ptr = 1;
+	return 0;
+      }
+  }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, 4\\);" 1 "asan0" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-1.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-1.c
new file mode 100644
index 0000000..a834268
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-1.c
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-gimple" }
+
+int
+main (int argc, char **argv)
+{
+  int *ptr = 0;
+
+  for (unsigned i = 0; i < 2; i++)
+    {
+      switch (argc)
+	{
+	int a;
+      default:
+	ptr = &a;
+	*ptr = 12345;
+	break;
+      }
+    }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, \[0-9\]\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(1, &a, \[0-9\]\\);" 1 "gimple" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-2.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-2.c
new file mode 100644
index 0000000..8aeca5a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-2.c
@@ -0,0 +1,33 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-gimple" }
+
+int
+main (int argc, char **argv)
+{
+  int *ptr = 0;
+  int *ptr2 = 0;
+  int *ptr3 = 0;
+
+  for (unsigned i = 0; i < 2; i++)
+    {
+      switch (argc)
+	{
+	case 1111:;
+	  int a, b, c;
+	default:
+	  ptr = &a;
+	  ptr2 = &b;
+	  ptr3 = &c;
+	  break;
+	}
+    }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, \[0-9\]\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &b, \[0-9\]\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &c, \[0-9\]\\);" 2 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(1, &a, \[0-9\]\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(1, &b, \[0-9\]\\);" 1 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(1, &c, \[0-9\]\\);" 1 "gimple" } }  */
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-3.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-3.c
new file mode 100644
index 0000000..828cb7c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-switch-3.c
@@ -0,0 +1,36 @@
+// { dg-do run }
+// { dg-additional-options "-fdump-tree-gimple" }
+
+int
+main (int argc, char **argv)
+{
+  int *ptr = 0;
+
+  for (unsigned i = 0; i < 2; i++)
+    {
+      switch (argc)
+	{
+	case 11111:;
+	  int a;
+	  ptr = &a;
+	  break;
+	  {
+	    default:
+	      ptr = &a;
+	      *ptr = 12345;
+	    case 222222:
+	    my_label:
+	      ptr = &a;
+	      break;
+	  }
+	}
+    }
+
+  if (argc == 333333)
+    goto my_label;
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(2, &a, \[0-9\]\\);" 4 "gimple" } }  */
+/* { dg-final { scan-tree-dump-times "ASAN_MARK \\(1, &a, \[0-9\]\\);" 1 "gimple" } }  */
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)
  2016-11-07 10:03                                             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3) Martin Liška
@ 2016-11-07 10:08                                               ` Jakub Jelinek
  2016-11-08  8:58                                                 ` Question about lambda function variables Martin Liška
  2016-11-07 16:07                                               ` Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)) David Malcolm
  1 sibling, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-07 10:08 UTC (permalink / raw)
  To: Martin Liška; +Cc: Marek Polacek, GCC Patches

On Mon, Nov 07, 2016 at 11:03:11AM +0100, Martin Liška wrote:
> Hello.
> 
> After discussion with Jakub, I'm resending new version of the patch, where I changed following:
> 1) gimplify_ctxp->live_switch_vars is used to track variables introduced in switch_expr. Every time
>    a case_label_expr is seen, these are unpoisoned. It's quite conservative, however it covers all
>    corner cases on can come up with. Compared to clang, we are much more precise in switch statements
>    where a variable liveness crosses label boundary.
> 2) I found a bug where ASAN_CHECK was optimized out due to missing check of IFN_ASAN_MARK internal fn.
>    Test was added for that.
> 3) Multiple switch tests have been added, which is going to be sent in upcoming email.
> 
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests (+ asan bootstrap finishes
> successfully).

Ok for trunk.  Hopefully we can resolve the most common cases for switch
incrementally, either still during stage1 or early in stage3.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope (v3)
  2016-11-07 10:04               ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope (v3) Martin Liška
@ 2016-11-07 10:09                 ` Jakub Jelinek
  0 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-07 10:09 UTC (permalink / raw)
  To: Martin Liška; +Cc: GCC Patches

On Mon, Nov 07, 2016 at 11:04:23AM +0100, Martin Liška wrote:
> Third version of the patch.
> 
> Martin

> >From e790d926afd3d2d6ad41d14d1e91698bf651b41a Mon Sep 17 00:00:00 2001
> From: marxin <mliska@suse.cz>
> Date: Mon, 19 Sep 2016 17:39:29 +0200
> Subject: [PATCH 2/2] Introduce tests for -fsanitize-address-use-after-scope
> 
> gcc/testsuite/ChangeLog:
> 
> 2016-09-26  Martin Liska  <mliska@suse.cz>
> 
> 	* c-c++-common/asan/force-inline-opt0-1.c: Disable
> 	-f-sanitize-address-use-after-scope.
> 	* c-c++-common/asan/inc.c: Change number of expected ASAN_CHECK
> 	internal fn calls.
> 	* g++.dg/asan/use-after-scope-1.C: New test.
> 	* g++.dg/asan/use-after-scope-2.C: Likewise.
> 	* g++.dg/asan/use-after-scope-3.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types-1.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types-2.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types-3.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types-4.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types-5.C: Likewise.
> 	* g++.dg/asan/use-after-scope-types.h: Likewise.
> 	* gcc.dg/asan/use-after-scope-1.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-2.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-3.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-4.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-5.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-6.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-7.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-8.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-9.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-switch-1.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-switch-2.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-switch-3.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-goto-1.c: Likewise.
> 	* gcc.dg/asan/use-after-scope-goto-2.c: Likewise.

Ok, thanks.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3))
  2016-11-07 10:03                                             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3) Martin Liška
  2016-11-07 10:08                                               ` Jakub Jelinek
@ 2016-11-07 16:07                                               ` David Malcolm
  2016-11-07 16:17                                                 ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: David Malcolm @ 2016-11-07 16:07 UTC (permalink / raw)
  To: Martin Liška, Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, jit

[-- Attachment #1: Type: text/plain, Size: 1825 bytes --]

On Mon, 2016-11-07 at 11:03 +0100, Martin Liška wrote:
> Hello.
> 
> After discussion with Jakub, I'm resending new version of the patch,
> where I changed following:
> 1) gimplify_ctxp->live_switch_vars is used to track variables
> introduced in switch_expr. Every time
>    a case_label_expr is seen, these are unpoisoned. It's quite
> conservative, however it covers all
>    corner cases on can come up with. Compared to clang, we are much
> more precise in switch statements
>    where a variable liveness crosses label boundary.
> 2) I found a bug where ASAN_CHECK was optimized out due to missing
> check of IFN_ASAN_MARK internal fn.
>    Test was added for that.
> 3) Multiple switch tests have been added, which is going to be sent
> in upcoming email.
> 
> Patch can bootstrap on ppc64le-redhat-linux and survives regression
> tests (+ asan bootstrap finishes
> successfully).

The patch (r241896) introduced an error in the build of the jit:

../../src/gcc/jit/jit-builtins.c:62:1: error: invalid conversion from
‘int’ to ‘gcc::jit::built_in_attribute’ [-fpermissive]
 };
 ^

which seems to be due to the "0" for ATTRS in:

--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
+		      BT_FN_VOID_PTR_PTRMODE, 0)

Is the attached patch OK as a fix? (assuming testing passes)  Or should
these builtins have other attrs?  (sorry, am not very familiar with the
sanitizer code).

Dave

[-- Attachment #2: 0001-Fix-build-of-jit.patch --]
[-- Type: text/x-patch, Size: 1659 bytes --]

From 6db5f9e50dc95f504d33970ee553172bbf400ae7 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 7 Nov 2016 11:21:20 -0500
Subject: [PATCH] Fix build of jit

gcc/ChangeLog:
	* asan.c (ATTR_NULL): Define.
	* sanitizer.def (BUILT_IN_ASAN_CLOBBER_N): Use ATTR_NULL rather
	than 0.
	(BUILT_IN_ASAN_UNCLOBBER_N): Likewise.
---
 gcc/asan.c        | 2 ++
 gcc/sanitizer.def | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 1e0ce8d..4a124cb 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -2463,6 +2463,8 @@ initialize_sanitizer_builtins (void)
 #define BT_FN_I16_CONST_VPTR_INT BT_FN_IX_CONST_VPTR_INT[4]
 #define BT_FN_I16_VPTR_I16_INT BT_FN_IX_VPTR_IX_INT[4]
 #define BT_FN_VOID_VPTR_I16_INT BT_FN_VOID_VPTR_IX_INT[4]
+#undef ATTR_NULL
+#define ATTR_NULL 0
 #undef ATTR_NOTHROW_LEAF_LIST
 #define ATTR_NOTHROW_LEAF_LIST ECF_NOTHROW | ECF_LEAF
 #undef ATTR_TMPURE_NOTHROW_LEAF_LIST
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 1c142e9..596b8b0 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -166,9 +166,9 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
-		      BT_FN_VOID_PTR_PTRMODE, 0)
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NULL)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
-		      BT_FN_VOID_PTR_PTRMODE, 0)
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NULL)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
-- 
1.8.5.3


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3))
  2016-11-07 16:07                                               ` Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)) David Malcolm
@ 2016-11-07 16:17                                                 ` Jakub Jelinek
  2016-11-08  9:38                                                   ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-07 16:17 UTC (permalink / raw)
  To: David Malcolm; +Cc: Martin Liška, Marek Polacek, GCC Patches, jit

On Mon, Nov 07, 2016 at 11:07:13AM -0500, David Malcolm wrote:
> The patch (r241896) introduced an error in the build of the jit:
> 
> ../../src/gcc/jit/jit-builtins.c:62:1: error: invalid conversion from
> ‘int’ to ‘gcc::jit::built_in_attribute’ [-fpermissive]
>  };
>  ^
> 
> which seems to be due to the "0" for ATTRS in:
> 
> --- a/gcc/sanitizer.def
> +++ b/gcc/sanitizer.def
> @@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
>  DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
>  		      "__asan_after_dynamic_init",
>  		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
> +		      BT_FN_VOID_PTR_PTRMODE, 0)
> +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
> +		      BT_FN_VOID_PTR_PTRMODE, 0)

I believe the 0 here is a bug, I'd think we should be using something like
ATTR_TMPURE_NOTHROW_LEAF_LIST that we are using __asan_load* - the functions
aren't going to throw, nor call anything in the current TU.  Not 100% sure
about the TMPURE, after all they do write/read memory (the shadow one).
So maybe ATTR_NOTHROW_LEAF_LIST instead for now?  Martin?

> Is the attached patch OK as a fix? (assuming testing passes)  Or should
> these builtins have other attrs?  (sorry, am not very familiar with the
> sanitizer code).

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Question about lambda function variables
  2016-11-07 10:08                                               ` Jakub Jelinek
@ 2016-11-08  8:58                                                 ` Martin Liška
  2016-11-08  9:12                                                   ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-08  8:58 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, jason

Hello.

After I've installed the patch, there's a small fallout I've been working on.
On of issue I met are lambda functions where we current ICE:

$ cat /tmp/use-after-scope-ice-1.ii

class A
{
public:
  A () : value (123) {}
  int value;
};

template <typename StoredFunction> class B
{
public:
  template <typename F> B (F p1) : mFunction (p1) { mFunction (); }
  StoredFunction mFunction;
};
template <typename Function>
void
NS_NewRunnableFunction (Function p1)
{
  (B<Function> (p1));
}
class C
{
  void DispatchConnectionCloseEvent (A);
  void AsyncCloseConnectionWithErrorMsg (const A &);
};
void
C::AsyncCloseConnectionWithErrorMsg (const A &)
{
  {
    A message;
    NS_NewRunnableFunction (
      [this, message] { DispatchConnectionCloseEvent (message); });
  }
}

Problematic is lambda function (use-after-scope-ice-1.ii.004t.gimple):
C::AsyncCloseConnectionWithErrorMsg(const A&)::<lambda()> (const struct __lambda0 * const __closure)
{
  const struct A message [value-expr: __closure->__message];
  struct C * const this [value-expr: __closure->__this];

  try
    {
      ASAN_MARK (2, &message, 4);
      _1 = __closure->__this;
      C::DispatchConnectionCloseEvent (_1, __closure->__message);
    }
  finally
    {
      ASAN_MARK (1, &message, 4);
    }
}

Where for quite obvious reasons variables 'message' can't be put as a stack variable and ICE is triggered in:
/tmp/use-after-scope-ice-1.ii:31:23: internal compiler error: in make_decl_rtl, at varasm.c:1311

My question is how to properly identify local variables defined in __closure context? Is it somehow
related to DECL_HAS_VALUE_EXPR_P field set on a var?

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Question about lambda function variables
  2016-11-08  8:58                                                 ` Question about lambda function variables Martin Liška
@ 2016-11-08  9:12                                                   ` Jakub Jelinek
  2016-11-08  9:35                                                     ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-08  9:12 UTC (permalink / raw)
  To: Martin Liška; +Cc: Marek Polacek, GCC Patches, jason

On Tue, Nov 08, 2016 at 09:58:13AM +0100, Martin Liška wrote:
> Problematic is lambda function (use-after-scope-ice-1.ii.004t.gimple):
> C::AsyncCloseConnectionWithErrorMsg(const A&)::<lambda()> (const struct __lambda0 * const __closure)
> {
>   const struct A message [value-expr: __closure->__message];
>   struct C * const this [value-expr: __closure->__this];
> 
>   try
>     {
>       ASAN_MARK (2, &message, 4);

That shows that the ASAN_MARK adds the &message without going through
gimplify_expr on it and therefore not handling the DECL_VALUE_EXPR in it,
otherwise it would be
  _2 = &__closure->__message;
  ASAN_MARK (2, _2, 4);
or something similar.
That said, poisoning/unpoisoning the lambda captured vars inside of the
lambda is of course wrong, 1) you really don't know where the members
live, it could be on the stack, but could very well be on the heap or
elsewhere, and while for stack and say longjmp we are prepared to unpoison
it, for heap allocated vars you risk you keep the memory poisoned in corner
cases and nothing will ever unpoison it; 2) the captured vars live longer
than just in the lambda method, it is perhaps up to whatever function
creates the lambda var to poison/unpoison it.

>       _1 = __closure->__this;
>       C::DispatchConnectionCloseEvent (_1, __closure->__message);
>     }
>   finally
>     {
>       ASAN_MARK (1, &message, 4);
>     }
> }
> 
> Where for quite obvious reasons variables 'message' can't be put as a stack variable and ICE is triggered in:
> /tmp/use-after-scope-ice-1.ii:31:23: internal compiler error: in make_decl_rtl, at varasm.c:1311
> 
> My question is how to properly identify local variables defined in __closure context? Is it somehow
> related to DECL_HAS_VALUE_EXPR_P field set on a var?

So yes, you should just ignore vars with DECL_HAS_VALUE_EXPR_P.  That can
mean lots of things (e.g. heavily used for OpenMP/OpenACC/Cilk+), but I
can't think of a case which you would like to poison - if it is
DECL_VALUE_EXPR to another var of part thereof, the other var should still
be declared in its scope.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Question about lambda function variables
  2016-11-08  9:12                                                   ` Jakub Jelinek
@ 2016-11-08  9:35                                                     ` Martin Liška
  0 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-08  9:35 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, jason

On 11/08/2016 10:12 AM, Jakub Jelinek wrote:
> On Tue, Nov 08, 2016 at 09:58:13AM +0100, Martin Liška wrote:
>> Problematic is lambda function (use-after-scope-ice-1.ii.004t.gimple):
>> C::AsyncCloseConnectionWithErrorMsg(const A&)::<lambda()> (const struct __lambda0 * const __closure)
>> {
>>   const struct A message [value-expr: __closure->__message];
>>   struct C * const this [value-expr: __closure->__this];
>>
>>   try
>>     {
>>       ASAN_MARK (2, &message, 4);
> 
> That shows that the ASAN_MARK adds the &message without going through
> gimplify_expr on it and therefore not handling the DECL_VALUE_EXPR in it,
> otherwise it would be
>   _2 = &__closure->__message;
>   ASAN_MARK (2, _2, 4);
> or something similar.
> That said, poisoning/unpoisoning the lambda captured vars inside of the
> lambda is of course wrong, 1) you really don't know where the members
> live, it could be on the stack, but could very well be on the heap or
> elsewhere, and while for stack and say longjmp we are prepared to unpoison
> it, for heap allocated vars you risk you keep the memory poisoned in corner
> cases and nothing will ever unpoison it; 2) the captured vars live longer
> than just in the lambda method, it is perhaps up to whatever function
> creates the lambda var to poison/unpoison it.
> 
>>       _1 = __closure->__this;
>>       C::DispatchConnectionCloseEvent (_1, __closure->__message);
>>     }
>>   finally
>>     {
>>       ASAN_MARK (1, &message, 4);
>>     }
>> }
>>
>> Where for quite obvious reasons variables 'message' can't be put as a stack variable and ICE is triggered in:
>> /tmp/use-after-scope-ice-1.ii:31:23: internal compiler error: in make_decl_rtl, at varasm.c:1311
>>
>> My question is how to properly identify local variables defined in __closure context? Is it somehow
>> related to DECL_HAS_VALUE_EXPR_P field set on a var?
> 
> So yes, you should just ignore vars with DECL_HAS_VALUE_EXPR_P.  That can
> mean lots of things (e.g. heavily used for OpenMP/OpenACC/Cilk+), but I
> can't think of a case which you would like to poison - if it is
> DECL_VALUE_EXPR to another var of part thereof, the other var should still
> be declared in its scope.

Thank you for clarification, I'm testing patch for this and other fallout issues.

Martin

> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3))
  2016-11-07 16:17                                                 ` Jakub Jelinek
@ 2016-11-08  9:38                                                   ` Martin Liška
  2016-11-08  9:41                                                     ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-08  9:38 UTC (permalink / raw)
  To: Jakub Jelinek, David Malcolm; +Cc: Marek Polacek, GCC Patches, jit

On 11/07/2016 05:17 PM, Jakub Jelinek wrote:
> On Mon, Nov 07, 2016 at 11:07:13AM -0500, David Malcolm wrote:
>> The patch (r241896) introduced an error in the build of the jit:
>>
>> ../../src/gcc/jit/jit-builtins.c:62:1: error: invalid conversion from
>> ‘int’ to ‘gcc::jit::built_in_attribute’ [-fpermissive]
>>  };
>>  ^
>>
>> which seems to be due to the "0" for ATTRS in:
>>
>> --- a/gcc/sanitizer.def
>> +++ b/gcc/sanitizer.def
>> @@ -165,6 +165,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_BEFORE_DYNAMIC_INIT,
>>  DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
>>  		      "__asan_after_dynamic_init",
>>  		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
>> +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
>> +		      BT_FN_VOID_PTR_PTRMODE, 0)
>> +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
>> +		      BT_FN_VOID_PTR_PTRMODE, 0)
> 
> I believe the 0 here is a bug, I'd think we should be using something like
> ATTR_TMPURE_NOTHROW_LEAF_LIST that we are using __asan_load* - the functions
> aren't going to throw, nor call anything in the current TU.  Not 100% sure
> about the TMPURE, after all they do write/read memory (the shadow one).
> So maybe ATTR_NOTHROW_LEAF_LIST instead for now?  Martin?

Yes, 0 is bug. I'm inclining to ATTR_NOTHROW_LEAF_LIST as __asan_{un}poison_stack_memory
modifies global memory. It would be more safe. I'm also going to change it for ASAN_MARK
internal function (where ECF_TM_PURE is currently selected).

I'm testing patch for that.
Martin

> 
>> Is the attached patch OK as a fix? (assuming testing passes)  Or should
>> these builtins have other attrs?  (sorry, am not very familiar with the
>> sanitizer code).
> 
> 	Jakub
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3))
  2016-11-08  9:38                                                   ` Martin Liška
@ 2016-11-08  9:41                                                     ` Jakub Jelinek
  2016-11-08 12:00                                                       ` [PATCH] use-after-scope fallout Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-08  9:41 UTC (permalink / raw)
  To: Martin Liška; +Cc: David Malcolm, Marek Polacek, GCC Patches, jit

On Tue, Nov 08, 2016 at 10:38:23AM +0100, Martin Liška wrote:
> > I believe the 0 here is a bug, I'd think we should be using something like
> > ATTR_TMPURE_NOTHROW_LEAF_LIST that we are using __asan_load* - the functions
> > aren't going to throw, nor call anything in the current TU.  Not 100% sure
> > about the TMPURE, after all they do write/read memory (the shadow one).
> > So maybe ATTR_NOTHROW_LEAF_LIST instead for now?  Martin?
> 
> Yes, 0 is bug. I'm inclining to ATTR_NOTHROW_LEAF_LIST as __asan_{un}poison_stack_memory
> modifies global memory. It would be more safe. I'm also going to change it for ASAN_MARK
> internal function (where ECF_TM_PURE is currently selected).

The TM stuff needs to be eventually resolved with the TM maintainers
(Richard Henderson and Torvald Riegel), the thing is that we can annotate
stuff even in TM regions, tm_pure functions etc.  I believe we have lots of
other TM issues (internal calls and the like) that haven't been addressed.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* [PATCH] use-after-scope fallout
  2016-11-08  9:41                                                     ` Jakub Jelinek
@ 2016-11-08 12:00                                                       ` Martin Liška
  2016-11-08 12:10                                                         ` Jakub Jelinek
  2016-11-08 18:05                                                         ` David Malcolm
  0 siblings, 2 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-08 12:00 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: David Malcolm, Marek Polacek, GCC Patches, jit

[-- Attachment #1: Type: text/plain, Size: 565 bytes --]

Hello.

This is fallout fix where I changed:

1) Fix ICE for lambda functions (added test-case: use-after-scope-4.C)
2) Fix ICE in gimplify_switch_expr, at gimplify.c:2269 (fixed by not adding
artificial variables)
3) PR testsuite/78242 - I basically removed the test (not interesting)
4) LEAF and NOTHROW flags are properly set on ASAN {un}poison functions
5) dbg_cnt has been added
6) use-after-scope-types-4.C - scanned pattern is updated to work on i686

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

Ready to be installed?
Martin

[-- Attachment #2: 0001-use-after-scope-fallout.patch --]
[-- Type: text/x-patch, Size: 6781 bytes --]

From 36eb4a8b3542729c9c428ac319d8422bea677869 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 7 Nov 2016 14:49:00 +0100
Subject: [PATCH] use-after-scope fallout

gcc/testsuite/ChangeLog:

2016-11-08  Martin Liska  <mliska@suse.cz>

	PR testsuite/78242
	* g++.dg/asan/use-after-scope-4.C: New test.
	* g++.dg/asan/use-after-scope-types-4.C: Update scanned pattern.
	* gcc.dg/asan/use-after-scope-8.c: Remove.

gcc/ChangeLog:

2016-11-08  Martin Liska  <mliska@suse.cz>

	PR testsuite/78242
	* dbgcnt.def: Add new debug counter asan_use_after_scope.
	* gimplify.c (gimplify_decl_expr): Do not sanitize vars
	with a value expr.  Do not add artificial variables to
	live_switch_vars.  Use the debug counter.
	(gimplify_target_expr): Use the debug counter.
	* internal-fn.def: Remove ECF_TM_PURE from ASAN_MARK builtin.
	* sanitizer.def: Set ATTR_NOTHROW_LEAF_LIST to
	BUILT_IN_ASAN_CLOBBER_N and BUILT_IN_ASAN_UNCLOBBER_N.
---
 gcc/dbgcnt.def                                     |  1 +
 gcc/gimplify.c                                     | 10 ++++--
 gcc/internal-fn.def                                |  2 +-
 gcc/sanitizer.def                                  |  4 +--
 gcc/testsuite/g++.dg/asan/use-after-scope-4.C      | 36 ++++++++++++++++++++++
 .../g++.dg/asan/use-after-scope-types-4.C          |  2 +-
 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c      | 14 ---------
 7 files changed, 48 insertions(+), 21 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/asan/use-after-scope-4.C
 delete mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-8.c

diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def
index 78ddcc2..0a45bac 100644
--- a/gcc/dbgcnt.def
+++ b/gcc/dbgcnt.def
@@ -141,6 +141,7 @@ echo ubound: $ub
 */
 
 /* Debug counter definitions.  */
+DEBUG_COUNTER (asan_use_after_scope)
 DEBUG_COUNTER (auto_inc_dec)
 DEBUG_COUNTER (ccp)
 DEBUG_COUNTER (cfg_cleanup)
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index e5930e6..d392450 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -60,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks-def.h"	/* FIXME: for lhd_set_decl_assembler_name */
 #include "builtins.h"
 #include "asan.h"
+#include "dbgcnt.h"
 
 /* Hash set of poisoned variables in a bind expr.  */
 static hash_set<tree> *asan_poisoned_variables = NULL;
@@ -1622,11 +1623,13 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 	  && !asan_no_sanitize_address_p ()
 	  && !is_vla
 	  && TREE_ADDRESSABLE (decl)
-	  && !TREE_STATIC (decl))
+	  && !TREE_STATIC (decl)
+	  && !DECL_HAS_VALUE_EXPR_P (decl)
+	  && dbg_cnt (asan_use_after_scope))
 	{
 	  asan_poisoned_variables->add (decl);
 	  asan_poison_variable (decl, false, seq_p);
-	  if (gimplify_ctxp->live_switch_vars)
+	  if (!DECL_ARTIFICIAL (decl) && gimplify_ctxp->live_switch_vars)
 	    gimplify_ctxp->live_switch_vars->add (decl);
 	}
 
@@ -6399,7 +6402,8 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
 	      else
 		cleanup = clobber;
 	    }
-	  if (asan_sanitize_use_after_scope ())
+	  if (asan_sanitize_use_after_scope ()
+	      && dbg_cnt (asan_use_after_scope))
 	    {
 	      tree asan_cleanup = build_asan_poison_call_expr (temp);
 	      if (asan_cleanup)
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 6a0a7f6..0869b2f 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -158,7 +158,7 @@ DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 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_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".R..")
+DEF_INTERNAL_FN (ASAN_MARK, ECF_LEAF | ECF_NOTHROW, ".R..")
 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/sanitizer.def b/gcc/sanitizer.def
index 1c142e9..c11c95a 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -166,9 +166,9 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_AFTER_DYNAMIC_INIT,
 		      "__asan_after_dynamic_init",
 		      BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_CLOBBER_N, "__asan_poison_stack_memory",
-		      BT_FN_VOID_PTR_PTRMODE, 0)
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_UNCLOBBER_N, "__asan_unpoison_stack_memory",
-		      BT_FN_VOID_PTR_PTRMODE, 0)
+		      BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
 
 /* Thread Sanitizer */
 DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", 
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-4.C b/gcc/testsuite/g++.dg/asan/use-after-scope-4.C
new file mode 100644
index 0000000..c3b6932
--- /dev/null
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-4.C
@@ -0,0 +1,36 @@
+/* Caused ICE in in make_decl_rtl, at varasm.c:1311.  */
+/* { dg-do compile } */
+
+class A
+{
+public:
+  A () : value (123) {}
+  int value;
+};
+
+template <typename StoredFunction> class B
+{
+public:
+  template <typename F> B (F p1) : mFunction (p1) { mFunction (); }
+  StoredFunction mFunction;
+};
+template <typename Function>
+void
+NS_NewRunnableFunction (Function p1)
+{
+  (B<Function> (p1));
+}
+class C
+{
+  void DispatchConnectionCloseEvent (A);
+  void AsyncCloseConnectionWithErrorMsg (const A &);
+};
+void
+C::AsyncCloseConnectionWithErrorMsg (const A &)
+{
+  {
+    A message;
+    NS_NewRunnableFunction (
+      [this, message] { DispatchConnectionCloseEvent (message); });
+  }
+}
diff --git a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
index dd06e94..44f4d3b 100644
--- a/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
+++ b/gcc/testsuite/g++.dg/asan/use-after-scope-types-4.C
@@ -13,5 +13,5 @@ int main()
 }
 
 // { dg-output "ERROR: AddressSanitizer: stack-use-after-scope on address.*(\n|\r\n|\r)" }
-// { dg-output "READ of size 8 at" }
+// { dg-output "READ of size " }
 // { dg-output ".*'x' <== Memory access at offset \[0-9\]* is inside this variable.*" }
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
deleted file mode 100644
index b204206..0000000
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-8.c
+++ /dev/null
@@ -1,14 +0,0 @@
-// { dg-do compile }
-// { dg-additional-options "-fdump-tree-asan0" }
-/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-
-int
-fn1 ()
-{
-  int x = 123;
-  register int a asm("rdi") = 123;
-
-  return x * x;
-}
-
-/* { dg-final { scan-tree-dump-not "ASAN_CHECK" "asan0" } }  */
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] use-after-scope fallout
  2016-11-08 12:00                                                       ` [PATCH] use-after-scope fallout Martin Liška
@ 2016-11-08 12:10                                                         ` Jakub Jelinek
  2016-11-08 18:05                                                         ` David Malcolm
  1 sibling, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-08 12:10 UTC (permalink / raw)
  To: Martin Liška; +Cc: David Malcolm, Marek Polacek, GCC Patches, jit

On Tue, Nov 08, 2016 at 01:00:19PM +0100, Martin Liška wrote:
> This is fallout fix where I changed:
> 
> 1) Fix ICE for lambda functions (added test-case: use-after-scope-4.C)
> 2) Fix ICE in gimplify_switch_expr, at gimplify.c:2269 (fixed by not adding
> artificial variables)
> 3) PR testsuite/78242 - I basically removed the test (not interesting)
> 4) LEAF and NOTHROW flags are properly set on ASAN {un}poison functions
> 5) dbg_cnt has been added
> 6) use-after-scope-types-4.C - scanned pattern is updated to work on i686
> 
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
> 
> Ready to be installed?
> Martin

> >From 36eb4a8b3542729c9c428ac319d8422bea677869 Mon Sep 17 00:00:00 2001
> From: marxin <mliska@suse.cz>
> Date: Mon, 7 Nov 2016 14:49:00 +0100
> Subject: [PATCH] use-after-scope fallout
> 
> gcc/testsuite/ChangeLog:
> 
> 2016-11-08  Martin Liska  <mliska@suse.cz>
> 
> 	PR testsuite/78242
> 	* g++.dg/asan/use-after-scope-4.C: New test.
> 	* g++.dg/asan/use-after-scope-types-4.C: Update scanned pattern.
> 	* gcc.dg/asan/use-after-scope-8.c: Remove.
> 
> gcc/ChangeLog:
> 
> 2016-11-08  Martin Liska  <mliska@suse.cz>
> 
> 	PR testsuite/78242
> 	* dbgcnt.def: Add new debug counter asan_use_after_scope.
> 	* gimplify.c (gimplify_decl_expr): Do not sanitize vars
> 	with a value expr.  Do not add artificial variables to
> 	live_switch_vars.  Use the debug counter.
> 	(gimplify_target_expr): Use the debug counter.
> 	* internal-fn.def: Remove ECF_TM_PURE from ASAN_MARK builtin.
> 	* sanitizer.def: Set ATTR_NOTHROW_LEAF_LIST to
> 	BUILT_IN_ASAN_CLOBBER_N and BUILT_IN_ASAN_UNCLOBBER_N.

Ok.  BTW, in stage3 please also check if/how nested functions (C and
fortran) work, I bet if you ASAN_MARK some vars and then
tree-nested.c moves them into an artificial struct that things might
not work 100% properly (e.g. would there be a guarantee that it is
unpoisoned upon function exit)?

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] use-after-scope fallout
  2016-11-08 12:00                                                       ` [PATCH] use-after-scope fallout Martin Liška
  2016-11-08 12:10                                                         ` Jakub Jelinek
@ 2016-11-08 18:05                                                         ` David Malcolm
  1 sibling, 0 replies; 111+ messages in thread
From: David Malcolm @ 2016-11-08 18:05 UTC (permalink / raw)
  To: Martin Liška, Jakub Jelinek; +Cc: Marek Polacek, GCC Patches, jit

On Tue, 2016-11-08 at 13:00 +0100, Martin Liška wrote:
> Hello.
> 
> This is fallout fix where I changed:
> 
> 1) Fix ICE for lambda functions (added test-case: use-after-scope
> -4.C)
> 2) Fix ICE in gimplify_switch_expr, at gimplify.c:2269 (fixed by not
> adding
> artificial variables)
> 3) PR testsuite/78242 - I basically removed the test (not
> interesting)
> 4) LEAF and NOTHROW flags are properly set on ASAN {un}poison
> functions
> 5) dbg_cnt has been added
> 6) use-after-scope-types-4.C - scanned pattern is updated to work on
> i686
> 
> Patch can bootstrap on ppc64le-redhat-linux and survives regression
> tests.
> 
> Ready to be installed?

Thanks.  The jit build is now fixed (as of r241961).

^ permalink raw reply	[flat|nested] 111+ messages in thread

* [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-02 13:16                                       ` Richard Biener
  2016-11-02 14:38                                         ` Martin Liška
@ 2016-11-16 12:25                                         ` Martin Liška
  2016-11-16 12:53                                           ` Martin Liška
  2016-11-16 13:07                                           ` Jakub Jelinek
  1 sibling, 2 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-16 12:25 UTC (permalink / raw)
  To: Richard Biener, Jakub Jelinek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 889 bytes --]

Hello

Following patch is a candidate that re-writes VAR_DECLs that are
is_gimple_reg_type with:
my_char_25 = ASAN_POISON ();

that is eventually transformed to:
__builtin___asan_report_use_after_scope_noabort ("my_char", 1);

at places where my_char_25 is used. That introduces a new entry point
to ASAN runtime, reporting:

==18378==ERROR: AddressSanitizer: stack-use-after-scope at pc 0x0000004007b4 bp 0x000000000001 sp 0x000000400603
ACCESS of size 1 for variable 'my_char' thread T0
    #0 0x400602 in main (/tmp/a.out+0x400602)
    #1 0x7fa6e572d290 in __libc_start_main (/lib64/libc.so.6+0x20290)
    #2 0x400669 in _start (/tmp/a.out+0x400669)

SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400602) in main

I'm still not sure where exactly do the expansion of ASAN_POISON as some cleanup
after the transformation would be desired.

Thoughts?
Thanks,
Martin 





[-- Attachment #2: 0001-use-after-scope-introduce-ASAN_POISON-internal-fn.patch --]
[-- Type: text/x-patch, Size: 14823 bytes --]

From c115207230a5be979119b6ac6572ae6af2a0ccd7 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 14 Nov 2016 16:49:05 +0100
Subject: [PATCH] use-after-scope: introduce ASAN_POISON internal fn

---
 gcc/asan.c                       | 72 +++++++++++++++++++++++++++++++++++++++-
 gcc/asan.h                       |  1 +
 gcc/internal-fn.c                |  7 ++++
 gcc/internal-fn.def              |  1 +
 gcc/sanitizer.def                |  8 +++++
 gcc/sanopt.c                     |  9 +++++
 gcc/tree-ssa.c                   | 65 ++++++++++++++++++++++++++++++------
 libsanitizer/asan/asan_errors.cc | 21 ++++++++++++
 libsanitizer/asan/asan_errors.h  | 19 +++++++++++
 libsanitizer/asan/asan_report.cc | 10 ++++++
 libsanitizer/asan/asan_report.h  |  3 ++
 libsanitizer/asan/asan_rtl.cc    | 16 +++++++++
 12 files changed, 221 insertions(+), 11 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 6e93ea3..d7d4267 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"
@@ -2979,6 +2979,76 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+
+/* Expand the ASAN_{LOAD,STORE} builtins.  */
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+			bool *need_commit_edge_insert)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree var_decl = SSA_NAME_VAR (poisoned_var);
+
+  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;
+
+  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);
+
+      built_in_function b = (recover_p
+			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
+			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
+      tree fun = builtin_decl_implicit (b);
+      pretty_printer pp;
+      pp_tree_identifier (&pp, DECL_NAME (var_decl));
+
+      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
+				       DECL_SIZE_UNIT (var_decl));
+      gimple_set_location (call, gimple_location (g));
+
+      /* If ASAN_POISON is used in a PHI node, let's insert the call on
+	 the leading to the PHI node BB.  */
+      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);
+		gsi_insert_seq_on_edge (e, call);
+		*need_commit_edge_insert = true;
+
+		break;
+	      }
+	}
+      else
+	{
+	  gimple_stmt_iterator gsi = gsi_for_stmt (use);
+	  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+	}
+    }
+
+  gimple *nop = gimple_build_nop ();
+  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
+  SSA_NAME_DEF_STMT (poisoned_var) = nop;
+  gsi_replace (iter, nop, GSI_NEW_STMT);
+
+  return false;
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
diff --git a/gcc/asan.h b/gcc/asan.h
index 9cf5904..6c25955 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -30,6 +30,7 @@ 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 *);
 
 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 ca347c5..17624e8 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -246,6 +246,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 d1cd1a5..9454afd 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -159,6 +159,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/sanitizer.def b/gcc/sanitizer.def
index 3db08a7..068c55b 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -102,6 +102,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N_NOABORT,
 		      "__asan_report_store_n_noabort",
 		      BT_FN_VOID_PTR_PTRMODE,
 		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE,
+		      "__asan_report_use_after_scope",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT,
+		      "__asan_report_use_after_scope_noabort",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1, "__asan_load1",
 		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2",
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 320e14e..77307d9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -698,6 +698,7 @@ pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -735,6 +736,10 @@ 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);
+		  break;
 		default:
 		  break;
 		}
@@ -766,6 +771,10 @@ pass_sanopt::execute (function *fun)
 	    gsi_next (&gsi);
 	}
     }
+
+  if (need_commit_edge_insert)
+    gsi_commit_edge_inserts ();
+
   return 0;
 }
 
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 135952b..8696c08 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,24 @@ 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))
+      && is_gimple_reg_type (TREE_TYPE (TREE_OPERAND (addr, 0))))
+    return true;
+
+  return false;
+}
+
 /* Compute TREE_ADDRESSABLE and DECL_GIMPLE_REG_P for local variables.  */
 
 void
@@ -1575,17 +1594,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 +1866,26 @@ 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)))
+		      {
+			HOST_WIDE_INT flags
+			  = tree_to_shwi (gimple_call_arg (stmt, 0));
+			unlink_stmt_vdef (stmt);
+			if (flags & ASAN_MARK_CLOBBER)
+			  {
+			    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);
diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc
index 73c4cca..f17c9b7 100644
--- a/libsanitizer/asan/asan_errors.cc
+++ b/libsanitizer/asan/asan_errors.cc
@@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() {
   ReportErrorSummary(bug_type, &stack);
 }
 
+void ErrorUseAfterScope::Print() {
+  const char *bug_type = "stack-use-after-scope";
+  Decorator d;
+  Printf("%s", d.Warning());
+
+  Report("ERROR: AddressSanitizer: stack-use-after-scope at pc %p bp %p sp %p\n",
+         variable_name, variable_size, pc, bp, sp);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+
+  char tname[128];
+  Printf("ACCESS of size %zu for variable '%s' thread T%d%s%s\n",
+         variable_size, variable_name, tid,
+         ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
+
+  GET_STACK_TRACE_FATAL(pc, bp);
+  stack.Print();
+  ReportErrorSummary(bug_type, &stack);
+}
+
+
 static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
   return s[-1] > 127 && s[1] > 127;
 }
diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h
index 6262dcf..4d0698f 100644
--- a/libsanitizer/asan/asan_errors.h
+++ b/libsanitizer/asan/asan_errors.h
@@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase {
   void Print();
 };
 
+struct ErrorUseAfterScope : ErrorBase {
+  uptr pc, bp, sp;
+  const char *variable_name;
+  u32 variable_size;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorUseAfterScope() = default;
+  ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_,
+                     const char *variable_name_, u32 variable_size_)
+      : ErrorBase(tid),
+        pc(pc_),
+        bp(bp_),
+        sp(sp_),
+	variable_name(variable_name_),
+	variable_size(variable_size_) {}
+  void Print();
+};
+
 struct ErrorGeneric : ErrorBase {
   AddressDescription addr_description;
   uptr pc, bp, sp;
@@ -324,6 +342,7 @@ struct ErrorGeneric : ErrorBase {
   macro(BadParamsToAnnotateContiguousContainer) \
   macro(ODRViolation)                           \
   macro(InvalidPointerPair)                     \
+  macro(UseAfterScope)                          \
   macro(Generic)
 // clang-format on
 
diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc
index 84d6764..c03edb9 100644
--- a/libsanitizer/asan/asan_report.cc
+++ b/libsanitizer/asan/asan_report.cc
@@ -353,6 +353,16 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
     return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
   }
 }
+// ----------------------- ReportUseAfterScope ----------- {{{1
+void ReportUseAfterScope(const char *variable_name, u32 variable_size,
+                         bool fatal) {
+  ScopedInErrorReport in_report (fatal);
+  GET_CALLER_PC_BP_SP;
+  ErrorUseAfterScope error(GetCurrentTidOrInvalid(), pc, bp, sp, variable_name,
+                           variable_size);
+  in_report.ReportError(error);
+}
+
 // ----------------------- Mac-specific reports ----------------- {{{1
 
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h
index 111b840..dca6a5e 100644
--- a/libsanitizer/asan/asan_report.h
+++ b/libsanitizer/asan/asan_report.h
@@ -68,6 +68,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
 void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
                         const __asan_global *g2, u32 stack_id2);
 
+void ReportUseAfterScope(const char *variable_name, u32 variable_size,
+                         bool fatal);
+
 // Mac-specific errors and warnings.
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
                                const char *zone_name,
diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc
index 38009d2..f637d71 100644
--- a/libsanitizer/asan/asan_rtl.cc
+++ b/libsanitizer/asan/asan_rtl.cc
@@ -253,6 +253,22 @@ void __asan_storeN_noabort(uptr addr, uptr size) {
   }
 }
 
+#include <stdio.h>
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope(const char *variable_name,
+                                   uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, true);
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope_noabort(const char *variable_name,
+                                           uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, false);
+}
+
 // Force the linker to keep the symbols for various ASan interface functions.
 // We want to keep those in the executable in order to let the instrumented
 // dynamic libraries access the symbol even if it is not used by the executable
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 12:25                                         ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
@ 2016-11-16 12:53                                           ` Martin Liška
  2016-11-16 13:07                                           ` Jakub Jelinek
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-16 12:53 UTC (permalink / raw)
  To: Richard Biener, Jakub Jelinek; +Cc: GCC Patches

On 11/16/2016 01:25 PM, Martin Liška wrote:
> Hello
> 
> Following patch is a candidate that re-writes VAR_DECLs that are
> is_gimple_reg_type with:
> my_char_25 = ASAN_POISON ();
> 
> that is eventually transformed to:
> __builtin___asan_report_use_after_scope_noabort ("my_char", 1);
> 
> at places where my_char_25 is used. That introduces a new entry point
> to ASAN runtime, reporting:
> 
> ==18378==ERROR: AddressSanitizer: stack-use-after-scope at pc 0x0000004007b4 bp 0x000000000001 sp 0x000000400603
> ACCESS of size 1 for variable 'my_char' thread T0
>     #0 0x400602 in main (/tmp/a.out+0x400602)
>     #1 0x7fa6e572d290 in __libc_start_main (/lib64/libc.so.6+0x20290)
>     #2 0x400669 in _start (/tmp/a.out+0x400669)
> 
> SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400602) in main
> 
> I'm still not sure where exactly do the expansion of ASAN_POISON as some cleanup
> after the transformation would be desired.
> 
> Thoughts?
> Thanks,
> Martin 
> 
> 
> 
> 

There's an example:

int
main (void)
{
  char *ptr;
  {
    char my_char;
    ptr = &my_char;
  }

  return *ptr;
}

$ g++ /tmp/use-after-scope-1.c -fsanitize=address -O0 && ./a.out 
=================================================================
==16035==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffe76322240 at pc 0x000000400848 bp 0x7ffe76322200 sp 0x7ffe763221f8
READ of size 1 at 0x7ffe76322240 thread T0
    #0 0x400847 in main (/tmp/a.out+0x400847)
    #1 0x7f0005739290 in __libc_start_main (/lib64/libc.so.6+0x20290)
    #2 0x4006b9 in _start (/tmp/a.out+0x4006b9)

Address 0x7ffe76322240 is located in stack of thread T0 at offset 32 in frame
    #0 0x400786 in main (/tmp/a.out+0x400786)

  This frame has 1 object(s):
    [32, 33) 'my_char' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400847) in main
Shadow bytes around the buggy address:
  0x10004ec5c3f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10004ec5c440: 00 00 00 00 f1 f1 f1 f1[f8]f2 f2 f2 f3 f3 f3 f3
  0x10004ec5c450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10004ec5c490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==16035==ABORTING

$ g++ /tmp/use-after-scope-1.c -fsanitize=address -O2 && ./a.out 
=================================================================
==16049==ERROR: AddressSanitizer: stack-use-after-scope at pc 0x000000400794 bp 0x000000000001 sp 0x0000004005f3
ACCESS of size 1 for variable 'my_char' thread T0
    #0 0x4005f2 in main (/tmp/a.out+0x4005f2)
    #1 0x7f883337e290 in __libc_start_main (/lib64/libc.so.6+0x20290)
    #2 0x400649 in _start (/tmp/a.out+0x400649)

SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x4005f2) in main
==16049==ABORTING

Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 12:25                                         ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
  2016-11-16 12:53                                           ` Martin Liška
@ 2016-11-16 13:07                                           ` Jakub Jelinek
  2016-11-16 16:01                                             ` Martin Liška
  2016-11-16 16:09                                             ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
  1 sibling, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-16 13:07 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

On Wed, Nov 16, 2016 at 01:25:04PM +0100, Martin Liška wrote:
>  
> +
> +/* Expand the ASAN_{LOAD,STORE} builtins.  */

Stale comment.

> +
> +bool
> +asan_expand_poison_ifn (gimple_stmt_iterator *iter,
> +			bool *need_commit_edge_insert)
> +{
...
> +  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);
> +

You want to ignore debug stmts uses here (or reset them).

> +      built_in_function b = (recover_p
> +			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
> +			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
> +      tree fun = builtin_decl_implicit (b);
> +      pretty_printer pp;
> +      pp_tree_identifier (&pp, DECL_NAME (var_decl));
> +
> +      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
> +				       DECL_SIZE_UNIT (var_decl));
> +      gimple_set_location (call, gimple_location (g));

Is that the location you want?  I mean shouldn't it use gimple_location (use)
instead?  The bug is on the use, not on the spot where it went out of scope.
Though the question is what to use if gimple_location (use) is
UNKNOWN_LOCATION.

> +
> +      /* If ASAN_POISON is used in a PHI node, let's insert the call on
> +	 the leading to the PHI node BB.  */

The comment doesn't make sense gramatically to me.

> +      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);
> +		gsi_insert_seq_on_edge (e, call);
> +		*need_commit_edge_insert = true;

What if there are multiple PHI args with that use?
Shouldn't you use just FOR_EACH_USE_ON_STMT or what macros we have?

> --- a/libsanitizer/asan/asan_errors.cc
> +++ b/libsanitizer/asan/asan_errors.cc
> @@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() {
>    ReportErrorSummary(bug_type, &stack);
>  }

As I wrote on IRC, we have to submit this to compiler-rt and only
if it is accepted, cherry-pick it together with the gcc changes.

> --- a/libsanitizer/asan/asan_errors.h
> +++ b/libsanitizer/asan/asan_errors.h
> @@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase {
>    void Print();
>  };
>  
> +struct ErrorUseAfterScope : ErrorBase {
> +  uptr pc, bp, sp;
> +  const char *variable_name;
> +  u32 variable_size;

Shouldn't this be uptr?

> +  ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_,
> +                     const char *variable_name_, u32 variable_size_)

And here.

> +// ----------------------- ReportUseAfterScope ----------- {{{1
> +void ReportUseAfterScope(const char *variable_name, u32 variable_size,

And here?

> +void ReportUseAfterScope(const char *variable_name, u32 variable_size,
> +                         bool fatal);

And here?

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 13:07                                           ` Jakub Jelinek
@ 2016-11-16 16:01                                             ` Martin Liška
  2016-11-16 16:28                                               ` Jakub Jelinek
  2016-11-16 16:09                                             ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-16 16:01 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 3592 bytes --]

On 11/16/2016 02:07 PM, Jakub Jelinek wrote:
> On Wed, Nov 16, 2016 at 01:25:04PM +0100, Martin Liška wrote:
>>  
>> +
>> +/* Expand the ASAN_{LOAD,STORE} builtins.  */
> 
> Stale comment.

Fixed.

> 
>> +
>> +bool
>> +asan_expand_poison_ifn (gimple_stmt_iterator *iter,
>> +			bool *need_commit_edge_insert)
>> +{
> ...
>> +  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);
>> +
> 
> You want to ignore debug stmts uses here (or reset them).

Likewise.

> 
>> +      built_in_function b = (recover_p
>> +			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
>> +			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
>> +      tree fun = builtin_decl_implicit (b);
>> +      pretty_printer pp;
>> +      pp_tree_identifier (&pp, DECL_NAME (var_decl));
>> +
>> +      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
>> +				       DECL_SIZE_UNIT (var_decl));
>> +      gimple_set_location (call, gimple_location (g));
> 
> Is that the location you want?  I mean shouldn't it use gimple_location (use)
> instead?  The bug is on the use, not on the spot where it went out of scope.
> Though the question is what to use if gimple_location (use) is
> UNKNOWN_LOCATION.

I changed the location to gimple_location(use).

> 
>> +
>> +      /* If ASAN_POISON is used in a PHI node, let's insert the call on
>> +	 the leading to the PHI node BB.  */
> 
> The comment doesn't make sense gramatically to me.

Modified.

> 
>> +      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);
>> +		gsi_insert_seq_on_edge (e, call);
>> +		*need_commit_edge_insert = true;
> 
> What if there are multiple PHI args with that use?
> Shouldn't you use just FOR_EACH_USE_ON_STMT or what macros we have?

Well, as I read the macro, I still have to iterate over gphi arguments
to find the proper edge.

> 
>> --- a/libsanitizer/asan/asan_errors.cc
>> +++ b/libsanitizer/asan/asan_errors.cc
>> @@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() {
>>    ReportErrorSummary(bug_type, &stack);
>>  }
> 
> As I wrote on IRC, we have to submit this to compiler-rt and only
> if it is accepted, cherry-pick it together with the gcc changes.

Sure, if we are fine with the GCC part, I can suggest the sanitizer changes.

> 
>> --- a/libsanitizer/asan/asan_errors.h
>> +++ b/libsanitizer/asan/asan_errors.h
>> @@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase {
>>    void Print();
>>  };
>>  
>> +struct ErrorUseAfterScope : ErrorBase {
>> +  uptr pc, bp, sp;
>> +  const char *variable_name;
>> +  u32 variable_size;
> 
> Shouldn't this be uptr?

Yep, changed on all places.
I'm attaching second version of the patch. I've tested the patch on linux kernel and I can see
>10K places where ASAN_POISON is removed (apparently there's not place where we would expand to
the new API entry point).

Martin

> 
>> +  ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_,
>> +                     const char *variable_name_, u32 variable_size_)
> 
> And here.
> 
>> +// ----------------------- ReportUseAfterScope ----------- {{{1
>> +void ReportUseAfterScope(const char *variable_name, u32 variable_size,
> 
> And here?
> 
>> +void ReportUseAfterScope(const char *variable_name, u32 variable_size,
>> +                         bool fatal);
> 
> And here?
> 
> 	Jakub
> 


[-- Attachment #2: 0001-use-after-scope-introduce-ASAN_POISON-internal-fn-v2.patch --]
[-- Type: text/x-patch, Size: 17260 bytes --]

From 9505c31813f224b855c5b2fab6c157e99ce54e59 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 14 Nov 2016 16:49:05 +0100
Subject: [PATCH] use-after-scope: introduce ASAN_POISON internal fn

gcc/ChangeLog:

2016-11-16  Martin Liska  <mliska@suse.cz>

	* asan.c (asan_expand_poison_ifn): New function.
	* asan.h (asan_expand_poison_ifn): Declare the function.
	* internal-fn.c (expand_ASAN_POISON): New function.
	* internal-fn.def (ASAN_POISON): New internal fn.
	* sanitizer.def (BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT):
	New built-in.
	(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE): Likewise.
	* sanopt.c (pass_sanopt::execute): Expand IFN_ASAN_POISON.
	* tree-ssa.c (is_asan_mark_p): New function.
	(execute_update_addresses_taken): Make local variables as not
	addressable if address of these varibles is just taken by
	ASAN_MARK.

gcc/testsuite/ChangeLog:

2016-11-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/use-after-scope-3.c: Run just with -O0.
	* gcc.dg/asan/use-after-scope-9.c: Run just with -O2 and
	change expected output.
---
 gcc/asan.c                                    | 72 ++++++++++++++++++++++++++-
 gcc/asan.h                                    |  1 +
 gcc/internal-fn.c                             |  7 +++
 gcc/internal-fn.def                           |  1 +
 gcc/sanitizer.def                             |  8 +++
 gcc/sanopt.c                                  |  9 ++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c |  6 +--
 gcc/tree-ssa.c                                | 71 ++++++++++++++++++++++----
 libsanitizer/asan/asan_errors.cc              | 21 ++++++++
 libsanitizer/asan/asan_errors.h               | 19 +++++++
 libsanitizer/asan/asan_report.cc              | 10 ++++
 libsanitizer/asan/asan_report.h               |  3 ++
 libsanitizer/asan/asan_rtl.cc                 | 16 ++++++
 14 files changed, 231 insertions(+), 14 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 6e93ea3..3a8e07d 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"
@@ -2979,6 +2979,76 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+
+/* Expand the ASAN_POISON builtins.  */
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+			bool *need_commit_edge_insert)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree var_decl = SSA_NAME_VAR (poisoned_var);
+
+  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;
+
+  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;
+
+      built_in_function b = (recover_p
+			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
+			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
+      tree fun = builtin_decl_implicit (b);
+      pretty_printer pp;
+      pp_tree_identifier (&pp, DECL_NAME (var_decl));
+
+      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
+				       DECL_SIZE_UNIT (var_decl));
+      gimple_set_location (call, gimple_location (use));
+
+      /* 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);
+		gsi_insert_seq_on_edge (e, call);
+		*need_commit_edge_insert = true;
+	      }
+	}
+      else
+	{
+	  gimple_stmt_iterator gsi = gsi_for_stmt (use);
+	  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+	}
+    }
+
+  gimple *nop = gimple_build_nop ();
+  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
+  SSA_NAME_DEF_STMT (poisoned_var) = nop;
+  gsi_replace (iter, nop, GSI_NEW_STMT);
+
+  return false;
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
diff --git a/gcc/asan.h b/gcc/asan.h
index 9cf5904..6c25955 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -30,6 +30,7 @@ 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 *);
 
 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 ca347c5..17624e8 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -246,6 +246,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 d1cd1a5..9454afd 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -159,6 +159,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/sanitizer.def b/gcc/sanitizer.def
index 3db08a7..068c55b 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -102,6 +102,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N_NOABORT,
 		      "__asan_report_store_n_noabort",
 		      BT_FN_VOID_PTR_PTRMODE,
 		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE,
+		      "__asan_report_use_after_scope",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT,
+		      "__asan_report_use_after_scope_noabort",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1, "__asan_load1",
 		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2",
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 320e14e..77307d9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -698,6 +698,7 @@ pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -735,6 +736,10 @@ 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);
+		  break;
 		default:
 		  break;
 		}
@@ -766,6 +771,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 9aeed51..8b11bea 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 2e30def..10d7fb5 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" }
 
 int
 main (int argc, char **argv)
@@ -15,6 +16,5 @@ main (int argc, char **argv)
   return *ptr;
 }
 
-// { 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.*" }
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope at pc.*(\n|\r\n|\r)" }
+// { dg-output "ACCESS of size .* for variable 'a'" }
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 135952b..f253d7e 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,26 @@ 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)))
+		      {
+			HOST_WIDE_INT flags
+			  = tree_to_shwi (gimple_call_arg (stmt, 0));
+			unlink_stmt_vdef (stmt);
+			if (flags & ASAN_MARK_CLOBBER)
+			  {
+			    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);
diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc
index 73c4cca..f17c9b7 100644
--- a/libsanitizer/asan/asan_errors.cc
+++ b/libsanitizer/asan/asan_errors.cc
@@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() {
   ReportErrorSummary(bug_type, &stack);
 }
 
+void ErrorUseAfterScope::Print() {
+  const char *bug_type = "stack-use-after-scope";
+  Decorator d;
+  Printf("%s", d.Warning());
+
+  Report("ERROR: AddressSanitizer: stack-use-after-scope at pc %p bp %p sp %p\n",
+         variable_name, variable_size, pc, bp, sp);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+
+  char tname[128];
+  Printf("ACCESS of size %zu for variable '%s' thread T%d%s%s\n",
+         variable_size, variable_name, tid,
+         ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
+
+  GET_STACK_TRACE_FATAL(pc, bp);
+  stack.Print();
+  ReportErrorSummary(bug_type, &stack);
+}
+
+
 static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
   return s[-1] > 127 && s[1] > 127;
 }
diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h
index 6262dcf..a843859 100644
--- a/libsanitizer/asan/asan_errors.h
+++ b/libsanitizer/asan/asan_errors.h
@@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase {
   void Print();
 };
 
+struct ErrorUseAfterScope : ErrorBase {
+  uptr pc, bp, sp;
+  const char *variable_name;
+  uptr variable_size;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorUseAfterScope() = default;
+  ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_,
+                     const char *variable_name_, uptr variable_size_)
+      : ErrorBase(tid),
+        pc(pc_),
+        bp(bp_),
+        sp(sp_),
+	variable_name(variable_name_),
+	variable_size(variable_size_) {}
+  void Print();
+};
+
 struct ErrorGeneric : ErrorBase {
   AddressDescription addr_description;
   uptr pc, bp, sp;
@@ -324,6 +342,7 @@ struct ErrorGeneric : ErrorBase {
   macro(BadParamsToAnnotateContiguousContainer) \
   macro(ODRViolation)                           \
   macro(InvalidPointerPair)                     \
+  macro(UseAfterScope)                          \
   macro(Generic)
 // clang-format on
 
diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc
index 84d6764..db39d42 100644
--- a/libsanitizer/asan/asan_report.cc
+++ b/libsanitizer/asan/asan_report.cc
@@ -353,6 +353,16 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
     return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
   }
 }
+// ----------------------- ReportUseAfterScope ----------- {{{1
+void ReportUseAfterScope(const char *variable_name, uptr variable_size,
+                         bool fatal) {
+  ScopedInErrorReport in_report (fatal);
+  GET_CALLER_PC_BP_SP;
+  ErrorUseAfterScope error(GetCurrentTidOrInvalid(), pc, bp, sp, variable_name,
+                           variable_size);
+  in_report.ReportError(error);
+}
+
 // ----------------------- Mac-specific reports ----------------- {{{1
 
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h
index 111b840..2fa158a 100644
--- a/libsanitizer/asan/asan_report.h
+++ b/libsanitizer/asan/asan_report.h
@@ -68,6 +68,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
 void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
                         const __asan_global *g2, u32 stack_id2);
 
+void ReportUseAfterScope(const char *variable_name, uptr variable_size,
+                         bool fatal);
+
 // Mac-specific errors and warnings.
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
                                const char *zone_name,
diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc
index 38009d2..f637d71 100644
--- a/libsanitizer/asan/asan_rtl.cc
+++ b/libsanitizer/asan/asan_rtl.cc
@@ -253,6 +253,22 @@ void __asan_storeN_noabort(uptr addr, uptr size) {
   }
 }
 
+#include <stdio.h>
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope(const char *variable_name,
+                                   uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, true);
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope_noabort(const char *variable_name,
+                                           uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, false);
+}
+
 // Force the linker to keep the symbols for various ASan interface functions.
 // We want to keep those in the executable in order to let the instrumented
 // dynamic libraries access the symbol even if it is not used by the executable
-- 
2.10.1


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 13:07                                           ` Jakub Jelinek
  2016-11-16 16:01                                             ` Martin Liška
@ 2016-11-16 16:09                                             ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Martin Liška @ 2016-11-16 16:09 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

As the patch quite significantly slowed down tramp3d, there's analysis
of # of variables which are poisoned by the sanitizer:

== normal variables ==
   24 B:  348x (5.80%)
   16 B:  273x (4.55%)
    8 B:  237x (3.95%)
    1 B:  177x (2.95%)
    4 B:  119x (1.98%)
   40 B:   89x (1.48%)
  144 B:   83x (1.38%)

== C++ artifical variables ==
    1 B: 1325x (22.08%)
    8 B:  983x (16.38%)
   24 B:  586x (9.77%)
  144 B:  415x (6.92%)
    4 B:  310x (5.17%)
   12 B:  274x (4.57%)
   16 B:  119x (1.98%)

Where sample of C++ artificial can be seen here:

  struct iterator D.608813;
  struct iterator D.369241;

  try
    {
      ASAN_MARK (2, &D.369241, 8);
      _1 = &this->D.110510._M_impl._M_start;
      __gnu_cxx::__normal_iterator<Pooma::StatisticsData**, std::vector<Pooma::StatisticsData*> >::__normal_iterator (&D.369241, _1);
      try
        {
          D.608813 = D.369241;
          return D.608813;
        }
      finally
        {
          ASAN_MARK (1, &D.369241, 8);
        }
    }
  catch
    {
      <<<eh_must_not_throw (terminate)>>>
    }

Problem is that these artificial variables (>70% of all in tramp3d) are often passed by reference and many functions in tramp3d either mark the argument
as unused, or just dereference. In situations where a reference is not saved, these variables should not live in memory. However,
do we have a machinery that can help with that?

My next step would be to adapt sanopt algorithm to catch use-after-scope {un}poisoning, however this is a different story that has significant impact
on # of poisoned variables.

Thoughts?
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 16:01                                             ` Martin Liška
@ 2016-11-16 16:28                                               ` Jakub Jelinek
  2016-11-22 11:55                                                 ` Martin Liška
  2016-12-20 11:34                                                 ` [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2) Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-16 16:28 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

On Wed, Nov 16, 2016 at 05:01:31PM +0100, Martin Liška wrote:
> +  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;
> +
> +      built_in_function b = (recover_p
> +			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
> +			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
> +      tree fun = builtin_decl_implicit (b);
> +      pretty_printer pp;
> +      pp_tree_identifier (&pp, DECL_NAME (var_decl));
> +
> +      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
> +				       DECL_SIZE_UNIT (var_decl));
> +      gimple_set_location (call, gimple_location (use));
> +
> +      /* 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);

No space after *.

> +	  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);
> +		gsi_insert_seq_on_edge (e, call);
> +		*need_commit_edge_insert = true;

You clearly don't have a sufficient testsuite coverage for this,
because this won't really work if you have more than one phi
argument equal to poisoned_var.  Inserting the same gimple stmt
into multiple places can't really work.  I bet you want to set
call to NULL after the gsi_insert_seq_on_edge and before that
call if (call == NULL) { call = gimple_build_call (...); gimple_set_location (...); }
Or maybe gimple_copy for the 2nd etc. would work too, dunno.

> +	      }
> +	}
> +      else
> +	{
> +	  gimple_stmt_iterator gsi = gsi_for_stmt (use);
> +	  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
> +	}
> +    }
> +
> +  gimple *nop = gimple_build_nop ();
> +  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
> +  SSA_NAME_DEF_STMT (poisoned_var) = nop;
> +  gsi_replace (iter, nop, GSI_NEW_STMT);

The last argument of gsi_replace is a bool, not GSI_*.
But not sure how this will work anyway, I think SSA_NAME_IS_DEFAULT_DEF
are supposed to have SSA_NAME_DEF_STMT a GIMPLE_NOP that doesn't
have bb set, while you are putting it into the stmt sequence.
Shouldn't you just gsi_remove iter instead?

Otherwise LGTM, but please post the asan patch to llvm-commits
or through their web review interface.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-16 16:28                                               ` Jakub Jelinek
@ 2016-11-22 11:55                                                 ` Martin Liška
  2016-11-23 13:57                                                   ` Martin Liška
  2016-12-20 11:34                                                 ` [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2) Martin Liška
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-22 11:55 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 3540 bytes --]

On 11/16/2016 05:28 PM, Jakub Jelinek wrote:
> On Wed, Nov 16, 2016 at 05:01:31PM +0100, Martin Liška wrote:
>> +  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;
>> +
>> +      built_in_function b = (recover_p
>> +			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
>> +			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
>> +      tree fun = builtin_decl_implicit (b);
>> +      pretty_printer pp;
>> +      pp_tree_identifier (&pp, DECL_NAME (var_decl));
>> +
>> +      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
>> +				       DECL_SIZE_UNIT (var_decl));
>> +      gimple_set_location (call, gimple_location (use));
>> +
>> +      /* 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);
> 
> No space after *.

Done.

> 
>> +	  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);
>> +		gsi_insert_seq_on_edge (e, call);
>> +		*need_commit_edge_insert = true;
> 
> You clearly don't have a sufficient testsuite coverage for this,
> because this won't really work if you have more than one phi
> argument equal to poisoned_var.  Inserting the same gimple stmt
> into multiple places can't really work.  I bet you want to set
> call to NULL after the gsi_insert_seq_on_edge and before that
> call if (call == NULL) { call = gimple_build_call (...); gimple_set_location (...); }
> Or maybe gimple_copy for the 2nd etc. would work too, dunno.

I see, fixed by using gimple_copy functionality.

> 
>> +	      }
>> +	}
>> +      else
>> +	{
>> +	  gimple_stmt_iterator gsi = gsi_for_stmt (use);
>> +	  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
>> +	}
>> +    }
>> +
>> +  gimple *nop = gimple_build_nop ();
>> +  SSA_NAME_IS_DEFAULT_DEF (poisoned_var) = true;
>> +  SSA_NAME_DEF_STMT (poisoned_var) = nop;
>> +  gsi_replace (iter, nop, GSI_NEW_STMT);
> 
> The last argument of gsi_replace is a bool, not GSI_*.
> But not sure how this will work anyway, I think SSA_NAME_IS_DEFAULT_DEF
> are supposed to have SSA_NAME_DEF_STMT a GIMPLE_NOP that doesn't
> have bb set, while you are putting it into the stmt sequence.
> Shouldn't you just gsi_remove iter instead?

gsi_remove does not work as a SSA name would lost a defining statement. However
setting SSA_NAME_DEF_STMT (poisoned_var) = gimple_build_nop () and removing the stmt
works fine. I haven't known that it can't belong to a BB.

Maybe we can add a verifier for that?

diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 2d9c62d..8fd4e91 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -767,6 +767,15 @@ verify_ssa_name (tree ssa_name, bool is_virtual)
       return true;
     }
 
+  if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)
+      && gimple_nop_p (SSA_NAME_DEF_STMT (ssa_name))
+      && gimple_bb (SSA_NAME_DEF_STMT (ssa_name)) != NULL)
+    {
+      error ("defining statement of a default name can't belong to a basic "
+	     "block");
+      return true;
+    }
+
   return false;
 }

That can be eventually done independently.

> 
> Otherwise LGTM, but please post the asan patch to llvm-commits
> or through their web review interface.

Good, I'm going to insert the patch to the tool.

Thanks,
Martin

> 
> 	Jakub
> 


[-- Attachment #2: 0001-use-after-scope-introduce-ASAN_POISON-internal-fn-v3.patch --]
[-- Type: text/x-patch, Size: 17372 bytes --]

From da190584d091eaaa509067918de4f1f77e887484 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 14 Nov 2016 16:49:05 +0100
Subject: [PATCH] use-after-scope: introduce ASAN_POISON internal fn

gcc/ChangeLog:

2016-11-16  Martin Liska  <mliska@suse.cz>

	* asan.c (asan_expand_poison_ifn): New function.
	* asan.h (asan_expand_poison_ifn): Declare the function.
	* internal-fn.c (expand_ASAN_POISON): New function.
	* internal-fn.def (ASAN_POISON): New internal fn.
	* sanitizer.def (BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT):
	New built-in.
	(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE): Likewise.
	* sanopt.c (pass_sanopt::execute): Expand IFN_ASAN_POISON.
	* tree-ssa.c (is_asan_mark_p): New function.
	(execute_update_addresses_taken): Make local variables as not
	addressable if address of these varibles is just taken by
	ASAN_MARK.

gcc/testsuite/ChangeLog:

2016-11-16  Martin Liska  <mliska@suse.cz>

	* gcc.dg/asan/use-after-scope-3.c: Run just with -O0.
	* gcc.dg/asan/use-after-scope-9.c: Run just with -O2 and
	change expected output.
---
 gcc/asan.c                                    | 77 ++++++++++++++++++++++++++-
 gcc/asan.h                                    |  1 +
 gcc/internal-fn.c                             |  7 +++
 gcc/internal-fn.def                           |  1 +
 gcc/sanitizer.def                             |  8 +++
 gcc/sanopt.c                                  |  9 ++++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c |  6 +--
 gcc/tree-ssa.c                                | 71 ++++++++++++++++++++----
 libsanitizer/asan/asan_errors.cc              | 21 ++++++++
 libsanitizer/asan/asan_errors.h               | 19 +++++++
 libsanitizer/asan/asan_report.cc              | 10 ++++
 libsanitizer/asan/asan_report.h               |  3 ++
 libsanitizer/asan/asan_rtl.cc                 | 16 ++++++
 14 files changed, 236 insertions(+), 14 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 6e93ea3..5bf8052 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"
@@ -2979,6 +2979,81 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+
+/* Expand the ASAN_POISON builtins.  */
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+			bool *need_commit_edge_insert)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree var_decl = SSA_NAME_VAR (poisoned_var);
+
+  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;
+
+  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;
+
+      built_in_function b = (recover_p
+			     ? BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT
+			     : BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE);
+      tree fun = builtin_decl_implicit (b);
+      pretty_printer pp;
+      pp_tree_identifier (&pp, DECL_NAME (var_decl));
+
+      gcall *call = gimple_build_call (fun, 2, asan_pp_string (&pp),
+				       DECL_SIZE_UNIT (var_decl));
+      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_remove (iter, true);
+
+  return false;
+}
+
 /* Instrument the current function.  */
 
 static unsigned int
diff --git a/gcc/asan.h b/gcc/asan.h
index 9cf5904..6c25955 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -30,6 +30,7 @@ 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 *);
 
 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 ca347c5..17624e8 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -246,6 +246,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 d1cd1a5..9454afd 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -159,6 +159,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/sanitizer.def b/gcc/sanitizer.def
index 3db08a7..068c55b 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -102,6 +102,14 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N_NOABORT,
 		      "__asan_report_store_n_noabort",
 		      BT_FN_VOID_PTR_PTRMODE,
 		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE,
+		      "__asan_report_use_after_scope",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_USE_AFTER_SCOPE_NOABORT,
+		      "__asan_report_use_after_scope_noabort",
+		      BT_FN_VOID_PTR_PTRMODE,
+		      ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1, "__asan_load1",
 		      BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
 DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2",
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index 320e14e..77307d9 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -698,6 +698,7 @@ pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -735,6 +736,10 @@ 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);
+		  break;
 		default:
 		  break;
 		}
@@ -766,6 +771,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 9aeed51..8b11bea 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 2e30def..10d7fb5 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" }
 
 int
 main (int argc, char **argv)
@@ -15,6 +16,5 @@ main (int argc, char **argv)
   return *ptr;
 }
 
-// { 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.*" }
+// { dg-output "ERROR: AddressSanitizer: stack-use-after-scope at pc.*(\n|\r\n|\r)" }
+// { dg-output "ACCESS of size .* for variable 'a'" }
diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c
index 62eea8b..2d9c62d 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,26 @@ 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)))
+		      {
+			HOST_WIDE_INT flags
+			  = tree_to_shwi (gimple_call_arg (stmt, 0));
+			unlink_stmt_vdef (stmt);
+			if (flags & ASAN_MARK_CLOBBER)
+			  {
+			    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);
diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc
index 73c4cca..f17c9b7 100644
--- a/libsanitizer/asan/asan_errors.cc
+++ b/libsanitizer/asan/asan_errors.cc
@@ -279,6 +279,27 @@ void ErrorInvalidPointerPair::Print() {
   ReportErrorSummary(bug_type, &stack);
 }
 
+void ErrorUseAfterScope::Print() {
+  const char *bug_type = "stack-use-after-scope";
+  Decorator d;
+  Printf("%s", d.Warning());
+
+  Report("ERROR: AddressSanitizer: stack-use-after-scope at pc %p bp %p sp %p\n",
+         variable_name, variable_size, pc, bp, sp);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+
+  char tname[128];
+  Printf("ACCESS of size %zu for variable '%s' thread T%d%s%s\n",
+         variable_size, variable_name, tid,
+         ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
+
+  GET_STACK_TRACE_FATAL(pc, bp);
+  stack.Print();
+  ReportErrorSummary(bug_type, &stack);
+}
+
+
 static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
   return s[-1] > 127 && s[1] > 127;
 }
diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h
index 6262dcf..a843859 100644
--- a/libsanitizer/asan/asan_errors.h
+++ b/libsanitizer/asan/asan_errors.h
@@ -294,6 +294,24 @@ struct ErrorInvalidPointerPair : ErrorBase {
   void Print();
 };
 
+struct ErrorUseAfterScope : ErrorBase {
+  uptr pc, bp, sp;
+  const char *variable_name;
+  uptr variable_size;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorUseAfterScope() = default;
+  ErrorUseAfterScope(u32 tid, uptr pc_, uptr bp_, uptr sp_,
+                     const char *variable_name_, uptr variable_size_)
+      : ErrorBase(tid),
+        pc(pc_),
+        bp(bp_),
+        sp(sp_),
+	variable_name(variable_name_),
+	variable_size(variable_size_) {}
+  void Print();
+};
+
 struct ErrorGeneric : ErrorBase {
   AddressDescription addr_description;
   uptr pc, bp, sp;
@@ -324,6 +342,7 @@ struct ErrorGeneric : ErrorBase {
   macro(BadParamsToAnnotateContiguousContainer) \
   macro(ODRViolation)                           \
   macro(InvalidPointerPair)                     \
+  macro(UseAfterScope)                          \
   macro(Generic)
 // clang-format on
 
diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc
index 84d6764..db39d42 100644
--- a/libsanitizer/asan/asan_report.cc
+++ b/libsanitizer/asan/asan_report.cc
@@ -353,6 +353,16 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
     return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
   }
 }
+// ----------------------- ReportUseAfterScope ----------- {{{1
+void ReportUseAfterScope(const char *variable_name, uptr variable_size,
+                         bool fatal) {
+  ScopedInErrorReport in_report (fatal);
+  GET_CALLER_PC_BP_SP;
+  ErrorUseAfterScope error(GetCurrentTidOrInvalid(), pc, bp, sp, variable_name,
+                           variable_size);
+  in_report.ReportError(error);
+}
+
 // ----------------------- Mac-specific reports ----------------- {{{1
 
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h
index 111b840..2fa158a 100644
--- a/libsanitizer/asan/asan_report.h
+++ b/libsanitizer/asan/asan_report.h
@@ -68,6 +68,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
 void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
                         const __asan_global *g2, u32 stack_id2);
 
+void ReportUseAfterScope(const char *variable_name, uptr variable_size,
+                         bool fatal);
+
 // Mac-specific errors and warnings.
 void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
                                const char *zone_name,
diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc
index 38009d2..f637d71 100644
--- a/libsanitizer/asan/asan_rtl.cc
+++ b/libsanitizer/asan/asan_rtl.cc
@@ -253,6 +253,22 @@ void __asan_storeN_noabort(uptr addr, uptr size) {
   }
 }
 
+#include <stdio.h>
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope(const char *variable_name,
+                                   uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, true);
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_report_use_after_scope_noabort(const char *variable_name,
+                                           uptr variable_size) {
+  ReportUseAfterScope(variable_name, variable_size, false);
+}
+
 // Force the linker to keep the symbols for various ASan interface functions.
 // We want to keep those in the executable in order to let the instrumented
 // dynamic libraries access the symbol even if it is not used by the executable
-- 
2.10.2


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-22 11:55                                                 ` Martin Liška
@ 2016-11-23 13:57                                                   ` Martin Liška
  2016-11-23 14:14                                                     ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-11-23 13:57 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

I started review process in libsanitizer: https://reviews.llvm.org/D26965
And I have a question that was asked in the review: can we distinguish between load and store
in case of having usage of ASAN_POISON?

Load looks as follows:

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

  if (argc != 12312)
  {
    char my_char;
    ptr = &my_char;
  }

  return *ptr;
}

main (int argc, char * * argv)
{
  char my_char;
  int _5;

  <bb 2>:
  if (argc_1(D) != 12312)
    goto <bb 3>;
  else
    goto <bb 5>;

  <bb 5>:
  goto <bb 4>;

  <bb 3>:
  my_char_8 = ASAN_POISON ();

  <bb 4>:
  # my_char_6 = PHI <my_char_7(D)(5), my_char_8(3)>
  _5 = (int) my_char_6;
  return _5;

}

however doing a store:
int
main (int argc, char **argv)
{
  char *ptr;

  if (argc != 12312)
  {
    char my_char;
    ptr = &my_char;
  }

  *ptr = 0;
  return 0;
}

main (int argc, char * * argv)
{
  <bb 2>:
  if (argc_1(D) != 12312)
    goto <bb 3>;
  else
    goto <bb 5>;

  <bb 5>:
  goto <bb 4>;

  <bb 3>:
  ASAN_POISON ();

  <bb 4>:
  return 0;

}

leads to a situation, where LHS of ASAN_POISON assignment is identified as overwritten and eventually
we see just ASAN_POISON call. This is currently removed in sanopt pass, but I'm wondering whether it's
valid optimization or not in this context?

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-23 13:57                                                   ` Martin Liška
@ 2016-11-23 14:14                                                     ` Jakub Jelinek
  2016-12-01 16:30                                                       ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-11-23 14:14 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote:
> I started review process in libsanitizer: https://reviews.llvm.org/D26965
> And I have a question that was asked in the review: can we distinguish between load and store
> in case of having usage of ASAN_POISON?

I think with ASAN_POISON it is indeed just loads from after scope that can
be caught, a store overwrites the variable with a new value and when turning
the store after we make the var no longer addressable into SSA form, we
loose information about the out of scope store.  Furthermore, if there is
first a store and then a read, like:
  if (argc != 12312)
    {
      char my_char;
      ptr = &my_char;
    }
  *ptr = i + 26;
  return *ptr;
we don't notice even the read.  Not sure what could be done against that
though.  I think we'd need to hook into the into-ssa framework, there it
should know the current value of the variable at the point of the store is
result of ASAN_POISON and be able to instead of turning that
  my_char = _23;
into
  my_char_35 = _23;
turn it into:
  my_char_35 = ASAN_POISON (_23);
which would represent after scope store into my_char.

Not really familiar with into-ssa though to know where to do it.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-11-23 14:14                                                     ` Jakub Jelinek
@ 2016-12-01 16:30                                                       ` Martin Liška
  2016-12-02 12:29                                                         ` Richard Biener
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-12-01 16:30 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

On 11/23/2016 03:13 PM, Jakub Jelinek wrote:
> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote:
>> I started review process in libsanitizer: https://reviews.llvm.org/D26965
>> And I have a question that was asked in the review: can we distinguish between load and store
>> in case of having usage of ASAN_POISON?
> 
> I think with ASAN_POISON it is indeed just loads from after scope that can
> be caught, a store overwrites the variable with a new value and when turning
> the store after we make the var no longer addressable into SSA form, we
> loose information about the out of scope store.  Furthermore, if there is
> first a store and then a read, like:
>   if (argc != 12312)
>     {
>       char my_char;
>       ptr = &my_char;
>     }
>   *ptr = i + 26;
>   return *ptr;
> we don't notice even the read.  Not sure what could be done against that
> though.  I think we'd need to hook into the into-ssa framework, there it
> should know the current value of the variable at the point of the store is
> result of ASAN_POISON and be able to instead of turning that
>   my_char = _23;
> into
>   my_char_35 = _23;
> turn it into:
>   my_char_35 = ASAN_POISON (_23);
> which would represent after scope store into my_char.
> 
> Not really familiar with into-ssa though to know where to do it.
> 
> 	Jakub
> 

Richi, may I ask you for help with this question?

Thanks,
Martin

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-12-01 16:30                                                       ` Martin Liška
@ 2016-12-02 12:29                                                         ` Richard Biener
  2016-12-08 12:51                                                           ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Richard Biener @ 2016-12-02 12:29 UTC (permalink / raw)
  To: Martin Liška; +Cc: Jakub Jelinek, GCC Patches

On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote:
> On 11/23/2016 03:13 PM, Jakub Jelinek wrote:
>> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote:
>>> I started review process in libsanitizer: https://reviews.llvm.org/D26965
>>> And I have a question that was asked in the review: can we distinguish between load and store
>>> in case of having usage of ASAN_POISON?
>>
>> I think with ASAN_POISON it is indeed just loads from after scope that can
>> be caught, a store overwrites the variable with a new value and when turning
>> the store after we make the var no longer addressable into SSA form, we
>> loose information about the out of scope store.  Furthermore, if there is
>> first a store and then a read, like:
>>   if (argc != 12312)
>>     {
>>       char my_char;
>>       ptr = &my_char;
>>     }
>>   *ptr = i + 26;
>>   return *ptr;
>> we don't notice even the read.  Not sure what could be done against that
>> though.  I think we'd need to hook into the into-ssa framework, there it
>> should know the current value of the variable at the point of the store is
>> result of ASAN_POISON and be able to instead of turning that
>>   my_char = _23;
>> into
>>   my_char_35 = _23;
>> turn it into:
>>   my_char_35 = ASAN_POISON (_23);
>> which would represent after scope store into my_char.
>>
>> Not really familiar with into-ssa though to know where to do it.
>>
>>       Jakub
>>
>
> Richi, may I ask you for help with this question?

Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def),
we do this for -Wuninitialized.

Richard.

> Thanks,
> Martin
>

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-12-02 12:29                                                         ` Richard Biener
@ 2016-12-08 12:51                                                           ` Martin Liška
  2016-12-13 14:16                                                             ` Richard Biener
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-12-08 12:51 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jakub Jelinek, GCC Patches

On 12/02/2016 01:29 PM, Richard Biener wrote:
> On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote:
>> On 11/23/2016 03:13 PM, Jakub Jelinek wrote:
>>> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote:
>>>> I started review process in libsanitizer: https://reviews.llvm.org/D26965
>>>> And I have a question that was asked in the review: can we distinguish between load and store
>>>> in case of having usage of ASAN_POISON?
>>>
>>> I think with ASAN_POISON it is indeed just loads from after scope that can
>>> be caught, a store overwrites the variable with a new value and when turning
>>> the store after we make the var no longer addressable into SSA form, we
>>> loose information about the out of scope store.  Furthermore, if there is
>>> first a store and then a read, like:
>>>   if (argc != 12312)
>>>     {
>>>       char my_char;
>>>       ptr = &my_char;
>>>     }
>>>   *ptr = i + 26;
>>>   return *ptr;
>>> we don't notice even the read.  Not sure what could be done against that
>>> though.  I think we'd need to hook into the into-ssa framework, there it
>>> should know the current value of the variable at the point of the store is
>>> result of ASAN_POISON and be able to instead of turning that
>>>   my_char = _23;
>>> into
>>>   my_char_35 = _23;
>>> turn it into:
>>>   my_char_35 = ASAN_POISON (_23);
>>> which would represent after scope store into my_char.
>>>
>>> Not really familiar with into-ssa though to know where to do it.
>>>
>>>       Jakub
>>>
>>
>> Richi, may I ask you for help with this question?
> 
> Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def),
> we do this for -Wuninitialized.
> 
> Richard.

Thanks for the tip, however as the optimization of memory address store + load happens
before we rewrite my_char into SSA, it would be probably hard to guess which memory
stores and loads should be preserved:

use-after-scope-20.c.032t.ccp1:
main (int argc, char * * argv)
{
  int my_char;
  int * ptr;
  int _1;
  int _11;

  <bb 2> [0.0%]:
  if (argc_4(D) != 12312)
    goto <bb 3>; [0.0%]
  else
    goto <bb 4>; [0.0%]

  <bb 3> [0.0%]:
  ASAN_MARK (2, &my_char, 4);
  ptr_8 = &my_char;
  ASAN_MARK (1, &my_char, 4);

  <bb 4> [0.0%]:
  # ptr_2 = PHI <ptr_5(D)(2), ptr_8(3)>
  _1 = argc_4(D) + 26;
  *ptr_2 = _1;
  _11 = *ptr_2;
  return _11;

}

I sent updated version of patch to LLVM phabricator:
https://reviews.llvm.org/D26965

Hopefully we can cherry pick the patch very soon to our trunk.

M.

> 
>> Thanks,
>> Martin
>>

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA)
  2016-12-08 12:51                                                           ` Martin Liška
@ 2016-12-13 14:16                                                             ` Richard Biener
  0 siblings, 0 replies; 111+ messages in thread
From: Richard Biener @ 2016-12-13 14:16 UTC (permalink / raw)
  To: Martin Liška; +Cc: Jakub Jelinek, GCC Patches

On Thu, Dec 8, 2016 at 1:51 PM, Martin Liška <mliska@suse.cz> wrote:
> On 12/02/2016 01:29 PM, Richard Biener wrote:
>> On Thu, Dec 1, 2016 at 5:30 PM, Martin Liška <mliska@suse.cz> wrote:
>>> On 11/23/2016 03:13 PM, Jakub Jelinek wrote:
>>>> On Wed, Nov 23, 2016 at 02:57:07PM +0100, Martin Liška wrote:
>>>>> I started review process in libsanitizer: https://reviews.llvm.org/D26965
>>>>> And I have a question that was asked in the review: can we distinguish between load and store
>>>>> in case of having usage of ASAN_POISON?
>>>>
>>>> I think with ASAN_POISON it is indeed just loads from after scope that can
>>>> be caught, a store overwrites the variable with a new value and when turning
>>>> the store after we make the var no longer addressable into SSA form, we
>>>> loose information about the out of scope store.  Furthermore, if there is
>>>> first a store and then a read, like:
>>>>   if (argc != 12312)
>>>>     {
>>>>       char my_char;
>>>>       ptr = &my_char;
>>>>     }
>>>>   *ptr = i + 26;
>>>>   return *ptr;
>>>> we don't notice even the read.  Not sure what could be done against that
>>>> though.  I think we'd need to hook into the into-ssa framework, there it
>>>> should know the current value of the variable at the point of the store is
>>>> result of ASAN_POISON and be able to instead of turning that
>>>>   my_char = _23;
>>>> into
>>>>   my_char_35 = _23;
>>>> turn it into:
>>>>   my_char_35 = ASAN_POISON (_23);
>>>> which would represent after scope store into my_char.
>>>>
>>>> Not really familiar with into-ssa though to know where to do it.
>>>>
>>>>       Jakub
>>>>
>>>
>>> Richi, may I ask you for help with this question?
>>
>> Probably where we handle the CLOBBER case (rewrite_stmt, maybe_register_def),
>> we do this for -Wuninitialized.
>>
>> Richard.
>
> Thanks for the tip, however as the optimization of memory address store + load happens
> before we rewrite my_char into SSA, it would be probably hard to guess which memory
> stores and loads should be preserved:
>
> use-after-scope-20.c.032t.ccp1:
> main (int argc, char * * argv)
> {
>   int my_char;
>   int * ptr;
>   int _1;
>   int _11;
>
>   <bb 2> [0.0%]:
>   if (argc_4(D) != 12312)
>     goto <bb 3>; [0.0%]
>   else
>     goto <bb 4>; [0.0%]
>
>   <bb 3> [0.0%]:
>   ASAN_MARK (2, &my_char, 4);
>   ptr_8 = &my_char;
>   ASAN_MARK (1, &my_char, 4);
>
>   <bb 4> [0.0%]:
>   # ptr_2 = PHI <ptr_5(D)(2), ptr_8(3)>
>   _1 = argc_4(D) + 26;
>   *ptr_2 = _1;
>   _11 = *ptr_2;
>   return _11;
>
> }

The SSA renamer sees

   my_char = ASAN_MARK;
   ptr_8 = &my_char;
   my_char = ASAN_MARK;

?

It does perform a DOM walk when updating the stmts so simply registering the
appropriate current def should do the trick?

> I sent updated version of patch to LLVM phabricator:
> https://reviews.llvm.org/D26965
>
> Hopefully we can cherry pick the patch very soon to our trunk.
>
> M.
>
>>
>>> Thanks,
>>> Martin
>>>
>

^ permalink raw reply	[flat|nested] 111+ messages in thread

* [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2016-11-16 16:28                                               ` Jakub Jelinek
  2016-11-22 11:55                                                 ` Martin Liška
@ 2016-12-20 11:34                                                 ` Martin Liška
  2016-12-21  9:19                                                   ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-12-20 11:34 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 906 bytes --]

On 11/16/2016 05:28 PM, Jakub Jelinek wrote:
> Otherwise LGTM, but please post the asan patch to llvm-commits
> or through their web review interface.
> 
> 	Jakub

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.

I'm not sure about the introduction of 'create_var' function, maybe we would need some
refactoring. Thoughts?

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

Martin

[-- Attachment #2: 0001-Speed-up-use-after-scope-v2-rewrite-into-SSA.patch --]
[-- Type: text/x-patch, Size: 13203 bytes --]

From 03c21ac90503aea7af4d74ea8c34f94efde782b6 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-19  Martin Liska  <mliska@suse.cz>

	* asan.c (asan_expand_poison_ifn): New function.
	* asan.h (asan_expand_poison_ifn):  Likewise.
	* gimple-expr.c (create_var): Likewise.
	* gimple-expr.h (create_var): Likewise.
	* 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-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-19  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                                    | 84 ++++++++++++++++++++++++++-
 gcc/asan.h                                    |  1 +
 gcc/gimple-expr.c                             | 27 +++++++++
 gcc/gimple-expr.h                             |  1 +
 gcc/internal-fn.c                             |  7 +++
 gcc/internal-fn.def                           |  1 +
 gcc/sanopt.c                                  |  9 +++
 gcc/testsuite/gcc.dg/asan/use-after-scope-3.c |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-9.c |  2 +
 gcc/tree-ssa.c                                | 69 ++++++++++++++++++----
 10 files changed, 191 insertions(+), 11 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index 53acff0a2fb..de8ce12f818 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"
@@ -3055,6 +3055,88 @@ asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   return true;
 }
 
+
+/* Expand the ASAN_POISON builtins.  */
+
+bool
+asan_expand_poison_ifn (gimple_stmt_iterator *iter,
+			bool *need_commit_edge_insert)
+{
+  gimple *g = gsi_stmt (*iter);
+  tree poisoned_var = gimple_call_lhs (g);
+  if (!poisoned_var)
+    {
+      gsi_remove (iter, true);
+      return true;
+    }
+
+  tree var_decl = SSA_NAME_VAR (poisoned_var);
+
+  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 shadow_var = create_var (TREE_TYPE (poisoned_var),
+				IDENTIFIER_POINTER (DECL_NAME (var_decl)));
+
+  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..e176e47f4eb 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -30,6 +30,7 @@ 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 *);
 
 extern gimple_stmt_iterator create_cond_insert_point
      (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
diff --git a/gcc/gimple-expr.c b/gcc/gimple-expr.c
index de5cce1f7cc..968e19727f6 100644
--- a/gcc/gimple-expr.c
+++ b/gcc/gimple-expr.c
@@ -477,6 +477,33 @@ create_tmp_var (tree type, const char *prefix)
   return tmp_var;
 }
 
+/* Create a new variable declaration of type TYPE with specified NAME.  */
+
+tree
+create_var (tree type, const char *name)
+{
+  tree tmp_var;
+
+  gcc_assert (!TREE_ADDRESSABLE (type) && COMPLETE_TYPE_P (type));
+
+  tmp_var = build_decl (input_location, VAR_DECL, get_identifier (name), type);
+
+  /* The variable was declared by the compiler.  */
+  DECL_ARTIFICIAL (tmp_var) = 1;
+  /* And we don't want debug info for it.  */
+  DECL_IGNORED_P (tmp_var) = 1;
+
+  /* Make the variable writable.  */
+  TREE_READONLY (tmp_var) = 0;
+
+  DECL_EXTERNAL (tmp_var) = 0;
+  TREE_STATIC (tmp_var) = 0;
+  TREE_USED (tmp_var) = 1;
+
+  gimple_add_tmp_var (tmp_var);
+  return tmp_var;
+}
+
 /* Create a new temporary variable declaration of type TYPE by calling
    create_tmp_var and if TYPE is a vector or a complex number, mark the new
    temporary as gimple register.  */
diff --git a/gcc/gimple-expr.h b/gcc/gimple-expr.h
index f2ccd29a94a..d8b393103fd 100644
--- a/gcc/gimple-expr.h
+++ b/gcc/gimple-expr.h
@@ -29,6 +29,7 @@ extern bool gimple_has_body_p (tree);
 extern const char *gimple_decl_printable_name (tree, int);
 extern tree copy_var_decl (tree, tree, tree);
 extern tree create_tmp_var_name (const char *);
+extern tree create_var (tree, const char *);
 extern tree create_tmp_var_raw (tree, const char * = NULL);
 extern tree create_tmp_var (tree, const char * = NULL);
 extern tree create_tmp_reg (tree, const char * = NULL);
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..204aa09d787 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -894,6 +894,7 @@ pass_sanopt::execute (function *fun)
   bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
     && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
 
+  bool need_commit_edge_insert = false;
   FOR_EACH_BB_FN (bb, fun)
     {
       gimple_stmt_iterator gsi;
@@ -931,6 +932,10 @@ 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);
+		  break;
 		default:
 		  break;
 		}
@@ -962,6 +967,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-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


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2016-12-20 11:34                                                 ` [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2) Martin Liška
@ 2016-12-21  9:19                                                   ` Jakub Jelinek
  2016-12-22 17:11                                                     ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-12-21  9:19 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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
2) as I said, use just a function in sanopt.c for this,
   create_asan_shadow_var or whatever
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'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.

	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2016-12-21  9:19                                                   ` Jakub Jelinek
@ 2016-12-22 17:11                                                     ` Martin Liška
  2016-12-22 17:28                                                       ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2016-12-22 17:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2984 bytes --]

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
> 


[-- Attachment #2: 0001-Speed-up-use-after-scope-v2-rewrite-into-SSA-v2.patch --]
[-- Type: text/x-patch, Size: 13823 bytes --]

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


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2016-12-22 17:11                                                     ` Martin Liška
@ 2016-12-22 17:28                                                       ` Jakub Jelinek
  2017-01-09 14:58                                                         ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2016-12-22 17:28 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2016-12-22 17:28                                                       ` Jakub Jelinek
@ 2017-01-09 14:58                                                         ` Martin Liška
  2017-01-16 14:20                                                           ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2017-01-09 14:58 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

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
> 

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-09 14:58                                                         ` Martin Liška
@ 2017-01-16 14:20                                                           ` Jakub Jelinek
  2017-01-17 16:22                                                             ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-16 14:20 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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.

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-16 14:20                                                           ` Jakub Jelinek
@ 2017-01-17 16:22                                                             ` Martin Liška
  2017-01-17 16:55                                                               ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2017-01-17 16:22 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 7525 bytes --]

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
> 


[-- Attachment #2: 0001-use-after-scope-handle-writes-to-a-poisoned-variable.patch --]
[-- Type: text/x-patch, Size: 7196 bytes --]

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


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-17 16:22                                                             ` Martin Liška
@ 2017-01-17 16:55                                                               ` Jakub Jelinek
  2017-01-18 15:37                                                                 ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-17 16:55 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2558 bytes --]

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

[-- Attachment #2: 4 --]
[-- Type: text/plain, Size: 852 bytes --]

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

[-- Attachment #3: 3 --]
[-- Type: text/plain, Size: 1080 bytes --]

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-17 16:55                                                               ` Jakub Jelinek
@ 2017-01-18 15:37                                                                 ` Martin Liška
  2017-01-19 16:43                                                                   ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2017-01-18 15:37 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-18 15:37                                                                 ` Martin Liška
@ 2017-01-19 16:43                                                                   ` Jakub Jelinek
  2017-01-20 11:55                                                                     ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-19 16:43 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-19 16:43                                                                   ` Jakub Jelinek
@ 2017-01-20 11:55                                                                     ` Martin Liška
  2017-01-20 14:27                                                                       ` Martin Liška
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2017-01-20 11:55 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 1747 bytes --]

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
> 


[-- Attachment #2: 0001-use-after-scope-handle-writes-to-a-poisoned-variable.patch --]
[-- Type: text/x-patch, Size: 9254 bytes --]

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


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-20 11:55                                                                     ` Martin Liška
@ 2017-01-20 14:27                                                                       ` Martin Liška
  2017-01-20 14:30                                                                         ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Martin Liška @ 2017-01-20 14:27 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-20 14:27                                                                       ` Martin Liška
@ 2017-01-20 14:30                                                                         ` Jakub Jelinek
  2017-01-20 14:42                                                                           ` Markus Trippelsdorf
  2017-01-23  9:38                                                                           ` Martin Liška
  0 siblings, 2 replies; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-20 14:30 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-20 14:30                                                                         ` Jakub Jelinek
@ 2017-01-20 14:42                                                                           ` Markus Trippelsdorf
  2017-01-23  9:38                                                                           ` Martin Liška
  1 sibling, 0 replies; 111+ messages in thread
From: Markus Trippelsdorf @ 2017-01-20 14:42 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, Richard Biener, GCC Patches

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.

diff --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.  */

-- 
Markus

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-20 14:30                                                                         ` Jakub Jelinek
  2017-01-20 14:42                                                                           ` Markus Trippelsdorf
@ 2017-01-23  9:38                                                                           ` Martin Liška
  2017-01-23  9:39                                                                             ` Jakub Jelinek
  2017-01-26  9:04                                                                             ` Thomas Schwinge
  1 sibling, 2 replies; 111+ messages in thread
From: Martin Liška @ 2017-01-23  9:38 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2674 bytes --]

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.html


[-- Attachment #2: 0001-Speed-up-use-after-scope-v2-rewrite-into-SSA.patch --]
[-- Type: text/x-patch, Size: 13827 bytes --]

From 97a900584714411521fc8ad3c7b99b0e74955fe6 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Mon, 19 Dec 2016 15:36:11 +0100
Subject: [PATCH 1/2] 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 9a59fe4f100..4aa5a4015ea 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.
@@ -3087,6 +3088,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 2f1f2eeaba7..2895bdee645 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 5d71cb2e08d..45e4ce05b86 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 4bf8383a77e..7b28b6722ff 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 55e07c0c646..70b7aeb80d3 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 42055bd8318..d63c70f2a12 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -5449,7 +5449,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 ecfae6b048e..41402a315ec 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 067143f49b8..f1826b2c9c4 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;
@@ -1575,6 +1576,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
@@ -1600,17 +1625,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.  */
@@ -1866,6 +1897,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


[-- Attachment #3: 0002-use-after-scope-handle-writes-to-a-poisoned-variable.patch --]
[-- Type: text/x-patch, Size: 11653 bytes --]

From 82c77c43969ebd7fcb17094820111a0075311a49 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Tue, 17 Jan 2017 16:49:29 +0100
Subject: [PATCH 2/2] 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.dg/asan/use-after-scope-11.c: New test.
	* g++.dg/asan/use-after-scope-5.C: New test.

gcc/ChangeLog:

2017-01-16  Jakub Jelinek  <jakub@redhat.com>
	    Martin Liska  <mliska@suse.cz>

	* 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.
---
 gcc/asan.c                                     | 19 ++++++++++------
 gcc/gimplify.c                                 | 15 +++++++++++--
 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/testsuite/gcc.dg/asan/use-after-scope-11.c | 30 ++++++++++++++++++++++++++
 gcc/tree-into-ssa.c                            | 27 ++++++++++++++++++++++-
 gcc/tree-ssa-dce.c                             | 16 ++++++++++----
 gcc/tree-ssa.c                                 | 15 ++++++++++++-
 10 files changed, 161 insertions(+), 15 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
 create mode 100644 gcc/testsuite/gcc.dg/asan/use-after-scope-11.c

diff --git a/gcc/asan.c b/gcc/asan.c
index 4aa5a4015ea..040e949fc85 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -3117,6 +3117,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,
@@ -3130,8 +3132,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)
@@ -3145,16 +3147,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,
@@ -3183,7 +3185,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/gimplify.c b/gcc/gimplify.c
index d382eea69f8..c6f7d7c4505 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/internal-fn.c b/gcc/internal-fn.c
index 45e4ce05b86..0d61375462d 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/testsuite/gcc.dg/asan/use-after-scope-11.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c
new file mode 100644
index 00000000000..b3c4c9ec758
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-11.c
@@ -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;
+}
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 f1826b2c9c4..10b77895508 100644
--- a/gcc/tree-ssa.c
+++ b/gcc/tree-ssa.c
@@ -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 ("use_after_scope_memory",
+			    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;
 		      }
 		  }
-- 
2.11.0


[-- Attachment #4: use-after-scope-diff.patch --]
[-- Type: text/x-patch, Size: 1384 bytes --]

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"),
+		     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);

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-23  9:38                                                                           ` Martin Liška
@ 2017-01-23  9:39                                                                             ` Jakub Jelinek
  2017-01-23 12:07                                                                               ` Martin Liška
  2017-01-26  9:04                                                                             ` Thomas Schwinge
  1 sibling, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-23  9:39 UTC (permalink / raw)
  To: Martin Liška; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-23  9:39                                                                             ` Jakub Jelinek
@ 2017-01-23 12:07                                                                               ` Martin Liška
  0 siblings, 0 replies; 111+ messages in thread
From: Martin Liška @ 2017-01-23 12:07 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-23  9:38                                                                           ` Martin Liška
  2017-01-23  9:39                                                                             ` Jakub Jelinek
@ 2017-01-26  9:04                                                                             ` Thomas Schwinge
  2017-01-26 10:55                                                                               ` Jakub Jelinek
  1 sibling, 1 reply; 111+ messages in thread
From: Thomas Schwinge @ 2017-01-26  9:04 UTC (permalink / raw)
  To: Martin Liška, Jakub Jelinek; +Cc: Richard Biener, GCC Patches

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-26  9:04                                                                             ` Thomas Schwinge
@ 2017-01-26 10:55                                                                               ` Jakub Jelinek
  2017-01-26 20:45                                                                                 ` Thomas Schwinge
  0 siblings, 1 reply; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-26 10:55 UTC (permalink / raw)
  To: Thomas Schwinge, Richard Biener; +Cc: Martin Liška, gcc-patches

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.

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


	Jakub

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-26 10:55                                                                               ` Jakub Jelinek
@ 2017-01-26 20:45                                                                                 ` Thomas Schwinge
  2017-01-26 20:52                                                                                   ` Jakub Jelinek
  0 siblings, 1 reply; 111+ messages in thread
From: Thomas Schwinge @ 2017-01-26 20:45 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Martin Liška, gcc-patches, Richard Biener

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2)
  2017-01-26 20:45                                                                                 ` Thomas Schwinge
@ 2017-01-26 20:52                                                                                   ` Jakub Jelinek
  0 siblings, 0 replies; 111+ messages in thread
From: Jakub Jelinek @ 2017-01-26 20:52 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: Martin Liška, gcc-patches, Richard Biener

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

^ permalink raw reply	[flat|nested] 111+ messages in thread

end of thread, other threads:[~2017-01-26 20:47 UTC | newest]

Thread overview: 111+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-06 11:04 [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
2016-05-06 11:08 ` [PATCH] Introduce tests for -fsanitize=use-after-scope Martin Liška
2016-05-11 12:56   ` Martin Liška
2016-05-06 11:16 ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Martin Liška
2016-05-06 11:48 ` Yury Gribov
2016-05-06 12:39   ` Jakub Jelinek
2016-05-06 13:07     ` Martin Liška
2016-05-06 14:22     ` Yury Gribov
2016-05-06 14:39       ` Jakub Jelinek
2016-05-10 15:03         ` Martin Liška
2016-05-10 15:15           ` Jakub Jelinek
2016-05-06 13:17   ` Martin Liška
2016-05-06 13:25     ` Jakub Jelinek
2016-05-06 14:41       ` Martin Liška
2016-05-06 14:46         ` Jakub Jelinek
2016-05-06 12:22 ` Jakub Jelinek
2016-05-11 12:54   ` Martin Liška
2016-05-12 10:42     ` Jakub Jelinek
2016-05-12 14:12       ` Martin Liška
2016-08-12 12:42         ` Martin Liška
2016-08-18 13:36         ` Jakub Jelinek
2016-10-03  9:27           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
2016-10-03  9:30             ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope Martin Liška
2016-11-07 10:04               ` [PATCH, 02/N] Introduce tests for -fsanitize-address-use-after-scope (v3) Martin Liška
2016-11-07 10:09                 ` Jakub Jelinek
2016-10-03  9:39             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Jakub Jelinek
2016-10-07 11:13             ` Jakub Jelinek
2016-10-12 14:08               ` Martin Liška
2016-10-21 14:26                 ` Jakub Jelinek
2016-10-25 13:18                   ` Martin Liška
2016-10-27 14:40                   ` Martin Liška
2016-10-27 17:24                     ` Jakub Jelinek
2016-11-01 14:48                       ` Martin Liška
2016-11-01 14:54                         ` Jakub Jelinek
2016-11-01 15:01                           ` Martin Liška
2016-11-02  9:36                           ` Martin Liška
2016-11-02  9:59                             ` Jakub Jelinek
2016-11-02 10:09                               ` Martin Liška
2016-11-02 10:11                               ` Jakub Jelinek
2016-11-02 14:20                                 ` Marek Polacek
2016-11-02 14:27                                   ` Martin Liška
2016-11-02 14:35                                     ` Jakub Jelinek
2016-11-04  9:17                                       ` Martin Liška
2016-11-04  9:33                                         ` Jakub Jelinek
2016-11-04 10:59                                           ` Martin Liška
2016-11-07 10:03                                             ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3) Martin Liška
2016-11-07 10:08                                               ` Jakub Jelinek
2016-11-08  8:58                                                 ` Question about lambda function variables Martin Liška
2016-11-08  9:12                                                   ` Jakub Jelinek
2016-11-08  9:35                                                     ` Martin Liška
2016-11-07 16:07                                               ` Fix build of jit (was Re: [PATCH, RFC] Introduce -fsanitize=use-after-scope (v3)) David Malcolm
2016-11-07 16:17                                                 ` Jakub Jelinek
2016-11-08  9:38                                                   ` Martin Liška
2016-11-08  9:41                                                     ` Jakub Jelinek
2016-11-08 12:00                                                       ` [PATCH] use-after-scope fallout Martin Liška
2016-11-08 12:10                                                         ` Jakub Jelinek
2016-11-08 18:05                                                         ` David Malcolm
2016-11-01 14:54                       ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
2016-11-01 15:12                         ` Jakub Jelinek
2016-11-02  9:40                           ` Richard Biener
2016-11-02  9:44                             ` Martin Liška
2016-11-02  9:52                             ` Jakub Jelinek
2016-11-02 12:36                               ` Richard Biener
2016-11-02 12:56                                 ` Jakub Jelinek
2016-11-02 12:59                                   ` Richard Biener
2016-11-02 13:06                                     ` Jakub Jelinek
2016-11-02 13:16                                       ` Richard Biener
2016-11-02 14:38                                         ` Martin Liška
2016-11-02 14:51                                           ` Jakub Jelinek
2016-11-02 15:25                                             ` Martin Liška
2016-11-03 13:34                                             ` Martin Liška
2016-11-03 13:44                                               ` Jakub Jelinek
2016-11-03 14:02                                                 ` Martin Liška
2016-11-03 14:04                                                   ` Jakub Jelinek
2016-11-03 14:18                                                     ` Martin Liška
2016-11-16 12:25                                         ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
2016-11-16 12:53                                           ` Martin Liška
2016-11-16 13:07                                           ` Jakub Jelinek
2016-11-16 16:01                                             ` Martin Liška
2016-11-16 16:28                                               ` Jakub Jelinek
2016-11-22 11:55                                                 ` Martin Liška
2016-11-23 13:57                                                   ` Martin Liška
2016-11-23 14:14                                                     ` Jakub Jelinek
2016-12-01 16:30                                                       ` Martin Liška
2016-12-02 12:29                                                         ` Richard Biener
2016-12-08 12:51                                                           ` Martin Liška
2016-12-13 14:16                                                             ` Richard Biener
2016-12-20 11:34                                                 ` [PATCH] Speed-up use-after-scope (re-writing to SSA) (version 2) Martin Liška
2016-12-21  9:19                                                   ` Jakub Jelinek
2016-12-22 17:11                                                     ` Martin Liška
2016-12-22 17:28                                                       ` Jakub Jelinek
2017-01-09 14:58                                                         ` Martin Liška
2017-01-16 14:20                                                           ` Jakub Jelinek
2017-01-17 16:22                                                             ` Martin Liška
2017-01-17 16:55                                                               ` Jakub Jelinek
2017-01-18 15:37                                                                 ` Martin Liška
2017-01-19 16:43                                                                   ` Jakub Jelinek
2017-01-20 11:55                                                                     ` Martin Liška
2017-01-20 14:27                                                                       ` Martin Liška
2017-01-20 14:30                                                                         ` Jakub Jelinek
2017-01-20 14:42                                                                           ` Markus Trippelsdorf
2017-01-23  9:38                                                                           ` Martin Liška
2017-01-23  9:39                                                                             ` Jakub Jelinek
2017-01-23 12:07                                                                               ` Martin Liška
2017-01-26  9:04                                                                             ` Thomas Schwinge
2017-01-26 10:55                                                                               ` Jakub Jelinek
2017-01-26 20:45                                                                                 ` Thomas Schwinge
2017-01-26 20:52                                                                                   ` Jakub Jelinek
2016-11-16 16:09                                             ` [RFC][PATCH] Speed-up use-after-scope (re-writing to SSA) Martin Liška
2016-11-02  9:52                           ` [PATCH, RFC] Introduce -fsanitize=use-after-scope (v2) Martin Liška
2016-09-03 15:23         ` [PATCH, RFC] Introduce -fsanitize=use-after-scope Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).