diff mbox

[RFC] Introduce -fsanitize=use-after-scope

Message ID 57332B69.4040001@suse.cz
State New
Headers show

Commit Message

Martin Liška May 11, 2016, 12:54 p.m. UTC
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

Comments

Jakub Jelinek May 12, 2016, 10:41 a.m. UTC | #1
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
diff mbox

Patch

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