diff mbox

[RFC] Introduce -fsanitize=use-after-scope (v2)

Message ID 98f408c5-7e1e-6fd8-e589-34f8de2f4455@suse.cz
State New
Headers show

Commit Message

Martin Liška Oct. 3, 2016, 9:27 a.m. UTC
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
>

Comments

Jakub Jelinek Oct. 3, 2016, 9:39 a.m. UTC | #1
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
Jakub Jelinek Oct. 7, 2016, 11:13 a.m. UTC | #2
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
diff mbox

Patch

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