diff mbox

[v2] Implement no_sanitize function attribute

Message ID 32e841e7-6b3e-5c48-3d83-e93150c4a71e@suse.cz
State New
Headers show

Commit Message

Martin Liška June 9, 2017, 12:51 p.m. UTC
On 06/09/2017 02:27 PM, Richard Biener wrote:
> On Fri, Jun 9, 2017 at 2:08 PM, Martin Liška <mliska@suse.cz> wrote:
>> On 06/09/2017 01:05 PM, Richard Biener wrote:
>>>
>>> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:
>>>>
>>>> On 06/09/2017 12:39 PM, Richard Biener wrote:
>>>>>
>>>>>
>>>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:
>>>>>>
>>>>>>
>>>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Hi!
>>>>>>>>>
>>>>>>>>> I'd still prefer to handle it with the flags infrastructure instead,
>>>>>>>>> but
>>>>>>>>> if
>>>>>>>>> Richard wants to do it this way, then at least:
>>>>>>>>>
>>>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is
>>>>>>>>>> non-null,
>>>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of
>>>>>>>>>> DECL_ATTRIBUTES.
>>>>>>>>>> */
>>>>>>>>>> +
>>>>>>>>>> +bool
>>>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)
>>>>>>>>>> +{
>>>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> This function really should be either inline, or partly inline,
>>>>>>>>> partly
>>>>>>>>> out
>>>>>>>>> of line, to handle the common case (sanitization of something not
>>>>>>>>> enabled)
>>>>>>>>> in the fast path.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Hello.
>>>>>>>>
>>>>>>>> Having that inlined would be great, however we'll need to put it to
>>>>>>>> tree.h
>>>>>>>> and thus we have to include "options.h" before tree.h in multiple
>>>>>>>> source
>>>>>>>> files.
>>>>>>>> Please take a look at partial patch.
>>>>>>>>
>>>>>>>>>
>>>>>>>>> And, it should have an early out,
>>>>>>>>>        if (result_flags == 0)
>>>>>>>>>          return false;
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Good idea!
>>>>>>>>
>>>>>>>>>
>>>>>>>>>> +
>>>>>>>>>> +  if (fn != NULL_TREE)
>>>>>>>>>> +    {
>>>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",
>>>>>>>>>> DECL_ATTRIBUTES (fn));
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> The attribute, if it is internal only, should have spaces or similar
>>>>>>>>> characters in its name, like "fn spec", "omp declare target" and
>>>>>>>>> many
>>>>>>>>> others.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Done that.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Whoo, wait -- this is for internal use only?  Can you step back and
>>>>>>> explain
>>>>>>> why we need this?  We do, after all, have -fsanitize options already.
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Can be seen here:
>>>>>>
>>>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize
>>>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"),
>>>>>> sanitize
>>>>>> no_flags (16777195)))
>>>>>> fn1 ()
>>>>>> {
>>>>>> ...
>>>>>> }
>>>>>>
>>>>>> where no_sanitize_thread and no_sanitize are normal attributes used by
>>>>>> users.
>>>>>> But we want to aggregate all there attributes and build one integer
>>>>>> mask
>>>>>> that
>>>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize
>>>>>> no_flags'
>>>>>> attribute, so that we don't have to iterate all attrs every time in
>>>>>> anitize_flags_p.
>>>>>>
>>>>>> Hope it explains situation?
>>>>>
>>>>>
>>>>>
>>>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute
>>>>> internal rep use a INTEGER_CST instread of a STRING_CST value,
>>>>> updating that in handle_attribute instead of adding new attributes?
>>>>>
>>>>> There's nothing that forces internal representation to match what the
>>>>> user wrote.
>>>>
>>>>
>>>>
>>>> I see, but consider following test-case:
>>>>
>>>> void
>>>> __attribute__((no_sanitize_thread))
>>>> __attribute__((no_sanitize(("address"))))
>>>> __attribute__((no_sanitize(("undefined"))))
>>>> __attribute__((no_sanitize(("address"))))
>>>> __attribute__((no_sanitize(("null"))))
>>>> foo (void) {}
>>>>
>>>> handle_no_sanitize_thread_attribute function is called for
>>>> no_sanitize_thread and
>>>> changing first no_sanitize attribute to integer is wrongly doable.
>>>
>>>
>>> Just change no_sanitize_thread to add no_sanitize instead?
>>
>>
>> Unfortunately, we currently support no_sanitize_{address,thread,undefined}
>> and no_address_safety_analysis. Thus we can't drop these.
> 
> Sure, but the internal representation of no_sanitize_{address,thread,undefined}
> can be the same as no_sanitize("...").  Just add *no_add_attrs = true and
> append/change no_sanitize in the handler.

Yep, that would be possible. That will mean to tranform all no_sanitize_* to
no_sanitize("...") attributes and then merge all these attributes with a string
value to a single one with integer mask. Well, doable, but I still prefer to
merge directly all to the new attribute. Plase take a look at incremental patch.

Martin

> 
>>
>>>
>>>> Apart
>>>> from that,
>>>> we want to merge all flags to a single attribute. Thus said, having an
>>>> unique name
>>>> will enable this.
>>>
>>>
>>> no_sanitize looks like the unique name to me -- I suppose
>>> no_sanitize("thread")
>>> works?
>>
>>
>> Yep, it's unique but as I mentioned we've got const struct attribute_spec
>> c_common_attribute_table[]
>> handlers that are executed on these no sanitize attributes. And as I want to
>> store int mask to
>> an attribute, I prefer to come up with a new attribute "sanitize no_flags"
>> and I can set
>>    *no_add_attrs = true; in order to remove the original attributes:
>>
>> void
>> __attribute__((no_sanitize(("address"))))
>> __attribute__((no_sanitize(("undefined"))))
>> __attribute__((no_sanitize(("address"))))
>> __attribute__((no_sanitize(("null"))))
>> fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0];
>> __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }
>>
>> will become:
>>
>> __attribute__((sanitize no_flags (16777187)))
>> fn1 ()
>> {
>> ...
>> }
>>
>> I'm going to test that.
>>
>> Martin
>>
>>
>>>
>>> Richard.
>>>
>>>> Martin
>>>>
>>>>
>>>>>
>>>>> [historically we've used random flags in decls/types for this kind of
>>>>> caching
>>>>> but I can see we don't want to waste as many bits there]
>>>>>
>>>>> Richard.
>>>>>
>>>>>> Martin
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Richard.
>>>>>>>
>>>>>>>>>
>>>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)
>>>>>>>>> +{
>>>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags",
>>>>>>>>> DECL_ATTRIBUTES
>>>>>>>>> (node));
>>>>>>>>> +  if (attr)
>>>>>>>>> +    {
>>>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
>>>>>>>>> +      flags |= old_value;
>>>>>>>>> +    }
>>>>>>>>> +
>>>>>>>>> +  DECL_ATTRIBUTES (node)
>>>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),
>>>>>>>>> +                build_int_cst (unsigned_type_node, flags),
>>>>>>>>> +                DECL_ATTRIBUTES (node));
>>>>>>>>>
>>>>>>>>> If there is a previous attribute already, can't you modify it in
>>>>>>>>> place?  If not, as it could be perhaps shared? with other functions
>>>>>>>>> somehow, at least you should avoid adding a new attribute if
>>>>>>>>> (old_value | flags) == old_value.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Yep, we should definitely share, I'll add test-case for that.
>>>>>>>> I'm currently testing the incremental patch, may I install it
>>>>>>>> after regression tests?
>>>>>>>>
>>>>>>>> Martin
>>>>>>>>
>>>>>>>>>
>>>>>>>>>             Jakub
>>>>>>>>>
>>>>>>>>
>>>>>>
>>>>
>>

Comments

Richard Biener June 9, 2017, 1:35 p.m. UTC | #1
On Fri, Jun 9, 2017 at 2:51 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 02:27 PM, Richard Biener wrote:
>>
>> On Fri, Jun 9, 2017 at 2:08 PM, Martin Liška <mliska@suse.cz> wrote:
>>>
>>> On 06/09/2017 01:05 PM, Richard Biener wrote:
>>>>
>>>>
>>>> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:
>>>>>
>>>>>
>>>>> On 06/09/2017 12:39 PM, Richard Biener wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz>
>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Hi!
>>>>>>>>>>
>>>>>>>>>> I'd still prefer to handle it with the flags infrastructure
>>>>>>>>>> instead,
>>>>>>>>>> but
>>>>>>>>>> if
>>>>>>>>>> Richard wants to do it this way, then at least:
>>>>>>>>>>
>>>>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is
>>>>>>>>>>> non-null,
>>>>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of
>>>>>>>>>>> DECL_ATTRIBUTES.
>>>>>>>>>>> */
>>>>>>>>>>> +
>>>>>>>>>>> +bool
>>>>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)
>>>>>>>>>>> +{
>>>>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> This function really should be either inline, or partly inline,
>>>>>>>>>> partly
>>>>>>>>>> out
>>>>>>>>>> of line, to handle the common case (sanitization of something not
>>>>>>>>>> enabled)
>>>>>>>>>> in the fast path.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Hello.
>>>>>>>>>
>>>>>>>>> Having that inlined would be great, however we'll need to put it to
>>>>>>>>> tree.h
>>>>>>>>> and thus we have to include "options.h" before tree.h in multiple
>>>>>>>>> source
>>>>>>>>> files.
>>>>>>>>> Please take a look at partial patch.
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> And, it should have an early out,
>>>>>>>>>>        if (result_flags == 0)
>>>>>>>>>>          return false;
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Good idea!
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> +
>>>>>>>>>>> +  if (fn != NULL_TREE)
>>>>>>>>>>> +    {
>>>>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",
>>>>>>>>>>> DECL_ATTRIBUTES (fn));
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> The attribute, if it is internal only, should have spaces or
>>>>>>>>>> similar
>>>>>>>>>> characters in its name, like "fn spec", "omp declare target" and
>>>>>>>>>> many
>>>>>>>>>> others.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Done that.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Whoo, wait -- this is for internal use only?  Can you step back and
>>>>>>>> explain
>>>>>>>> why we need this?  We do, after all, have -fsanitize options
>>>>>>>> already.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Can be seen here:
>>>>>>>
>>>>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize
>>>>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"),
>>>>>>> sanitize
>>>>>>> no_flags (16777195)))
>>>>>>> fn1 ()
>>>>>>> {
>>>>>>> ...
>>>>>>> }
>>>>>>>
>>>>>>> where no_sanitize_thread and no_sanitize are normal attributes used
>>>>>>> by
>>>>>>> users.
>>>>>>> But we want to aggregate all there attributes and build one integer
>>>>>>> mask
>>>>>>> that
>>>>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize
>>>>>>> no_flags'
>>>>>>> attribute, so that we don't have to iterate all attrs every time in
>>>>>>> anitize_flags_p.
>>>>>>>
>>>>>>> Hope it explains situation?
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute
>>>>>> internal rep use a INTEGER_CST instread of a STRING_CST value,
>>>>>> updating that in handle_attribute instead of adding new attributes?
>>>>>>
>>>>>> There's nothing that forces internal representation to match what the
>>>>>> user wrote.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> I see, but consider following test-case:
>>>>>
>>>>> void
>>>>> __attribute__((no_sanitize_thread))
>>>>> __attribute__((no_sanitize(("address"))))
>>>>> __attribute__((no_sanitize(("undefined"))))
>>>>> __attribute__((no_sanitize(("address"))))
>>>>> __attribute__((no_sanitize(("null"))))
>>>>> foo (void) {}
>>>>>
>>>>> handle_no_sanitize_thread_attribute function is called for
>>>>> no_sanitize_thread and
>>>>> changing first no_sanitize attribute to integer is wrongly doable.
>>>>
>>>>
>>>>
>>>> Just change no_sanitize_thread to add no_sanitize instead?
>>>
>>>
>>>
>>> Unfortunately, we currently support
>>> no_sanitize_{address,thread,undefined}
>>> and no_address_safety_analysis. Thus we can't drop these.
>>
>>
>> Sure, but the internal representation of
>> no_sanitize_{address,thread,undefined}
>> can be the same as no_sanitize("...").  Just add *no_add_attrs = true and
>> append/change no_sanitize in the handler.
>
>
> Yep, that would be possible. That will mean to tranform all no_sanitize_* to
> no_sanitize("...") attributes and then merge all these attributes with a
> string
> value to a single one with integer mask. Well, doable, but I still prefer to
> merge directly all to the new attribute. Plase take a look at incremental
> patch.

You can directly transform to no_sanitize with integer mask, not sure why
you'd need an intermediate step with a string?

> Martin
>
>
>>
>>>
>>>>
>>>>> Apart
>>>>> from that,
>>>>> we want to merge all flags to a single attribute. Thus said, having an
>>>>> unique name
>>>>> will enable this.
>>>>
>>>>
>>>>
>>>> no_sanitize looks like the unique name to me -- I suppose
>>>> no_sanitize("thread")
>>>> works?
>>>
>>>
>>>
>>> Yep, it's unique but as I mentioned we've got const struct attribute_spec
>>> c_common_attribute_table[]
>>> handlers that are executed on these no sanitize attributes. And as I want
>>> to
>>> store int mask to
>>> an attribute, I prefer to come up with a new attribute "sanitize
>>> no_flags"
>>> and I can set
>>>    *no_add_attrs = true; in order to remove the original attributes:
>>>
>>> void
>>> __attribute__((no_sanitize(("address"))))
>>> __attribute__((no_sanitize(("undefined"))))
>>> __attribute__((no_sanitize(("address"))))
>>> __attribute__((no_sanitize(("null"))))
>>> fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0];
>>> __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }
>>>
>>> will become:
>>>
>>> __attribute__((sanitize no_flags (16777187)))
>>> fn1 ()
>>> {
>>> ...
>>> }
>>>
>>> I'm going to test that.
>>>
>>> Martin
>>>
>>>
>>>>
>>>> Richard.
>>>>
>>>>> Martin
>>>>>
>>>>>
>>>>>>
>>>>>> [historically we've used random flags in decls/types for this kind of
>>>>>> caching
>>>>>> but I can see we don't want to waste as many bits there]
>>>>>>
>>>>>> Richard.
>>>>>>
>>>>>>> Martin
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Richard.
>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)
>>>>>>>>>> +{
>>>>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags",
>>>>>>>>>> DECL_ATTRIBUTES
>>>>>>>>>> (node));
>>>>>>>>>> +  if (attr)
>>>>>>>>>> +    {
>>>>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
>>>>>>>>>> +      flags |= old_value;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +  DECL_ATTRIBUTES (node)
>>>>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),
>>>>>>>>>> +                build_int_cst (unsigned_type_node, flags),
>>>>>>>>>> +                DECL_ATTRIBUTES (node));
>>>>>>>>>>
>>>>>>>>>> If there is a previous attribute already, can't you modify it in
>>>>>>>>>> place?  If not, as it could be perhaps shared? with other
>>>>>>>>>> functions
>>>>>>>>>> somehow, at least you should avoid adding a new attribute if
>>>>>>>>>> (old_value | flags) == old_value.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Yep, we should definitely share, I'll add test-case for that.
>>>>>>>>> I'm currently testing the incremental patch, may I install it
>>>>>>>>> after regression tests?
>>>>>>>>>
>>>>>>>>> Martin
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>             Jakub
>>>>>>>>>>
>>>>>>>>>
>>>>>>>
>>>>>
>>>
>
diff mbox

Patch

From 22f08924dc3fdbe31dfb6fa83717a2a694604ef8 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 8 Jun 2017 16:39:33 +0200
Subject: [PATCH] Put sanitize_flags_t to asan.h and merge all no_sanitize_*
 attributes.

---
 gcc/asan.h                                    | 20 +++++++++++++
 gcc/c-family/c-attribs.c                      | 43 +++++++++++++--------------
 gcc/c/c-convert.c                             |  1 +
 gcc/c/c-decl.c                                |  1 +
 gcc/c/c-typeck.c                              |  1 +
 gcc/convert.c                                 |  1 +
 gcc/cp/class.c                                |  1 +
 gcc/cp/cp-gimplify.c                          |  1 +
 gcc/cp/cp-ubsan.c                             |  1 +
 gcc/cp/decl.c                                 |  1 +
 gcc/cp/init.c                                 |  1 +
 gcc/cp/typeck.c                               |  1 +
 gcc/gimple-fold.c                             |  1 +
 gcc/ipa-inline.c                              |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c |  3 ++
 gcc/tree.c                                    | 18 -----------
 gcc/tree.h                                    |  4 ---
 17 files changed, 55 insertions(+), 45 deletions(-)

diff --git a/gcc/asan.h b/gcc/asan.h
index a590d0a5ace..f4ab5b6acec 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -154,4 +154,24 @@  asan_protect_stack_decl (tree decl)
 	|| (asan_sanitize_use_after_scope () && TREE_ADDRESSABLE (decl)));
 }
 
+/* Return true when flag_sanitize & FLAG is non-zero.  If FN is non-null,
+   remove all flags mentioned in "sanitize no_flags" of DECL_ATTRIBUTES.  */
+
+static inline bool
+sanitize_flags_p (unsigned int flag, const_tree fn = current_function_decl)
+{
+  unsigned int result_flags = flag_sanitize & flag;
+  if (result_flags == 0)
+    return false;
+
+  if (fn != NULL_TREE)
+    {
+      tree value = lookup_attribute ("sanitize no_flags", DECL_ATTRIBUTES (fn));
+      if (value)
+	result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
+    }
+
+  return result_flags;
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index abb43d0d02c..a0b554bae59 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -558,17 +558,22 @@  handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
 void
 add_no_sanitize_value (tree node, unsigned int flags)
 {
-  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (node));
+  tree attr = lookup_attribute ("sanitize no_flags", DECL_ATTRIBUTES (node));
   if (attr)
     {
       unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
       flags |= old_value;
-    }
 
-  DECL_ATTRIBUTES (node)
-    = tree_cons (get_identifier ("no_sanitize_flags"),
-		 build_int_cst (unsigned_type_node, flags),
-		 DECL_ATTRIBUTES (node));
+      if (flags == old_value)
+	return;
+
+      TREE_VALUE (attr) = build_int_cst (unsigned_type_node, flags);
+    }
+  else
+    DECL_ATTRIBUTES (node)
+      = tree_cons (get_identifier ("sanitize no_flags"),
+		   build_int_cst (unsigned_type_node, flags),
+		   DECL_ATTRIBUTES (node));
 }
 
 /* Handle a "no_sanitize" attribute; arguments as in
@@ -578,11 +583,11 @@  static tree
 handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
 			      bool *no_add_attrs)
 {
+  *no_add_attrs = true;
   tree id = TREE_VALUE (args);
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
       warning (OPT_Wattributes, "%qE attribute ignored", name);
-      *no_add_attrs = true;
       return NULL_TREE;
     }
 
@@ -614,11 +619,9 @@  static tree
 handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
 				      bool *no_add_attrs)
 {
+  *no_add_attrs = true;
   if (TREE_CODE (*node) != FUNCTION_DECL)
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      *no_add_attrs = true;
-    }
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
   else
     add_no_sanitize_value (*node, SANITIZE_ADDRESS);
 
@@ -632,11 +635,9 @@  static tree
 handle_no_sanitize_thread_attribute (tree *node, tree name, tree, int,
 				      bool *no_add_attrs)
 {
+  *no_add_attrs = true;
   if (TREE_CODE (*node) != FUNCTION_DECL)
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      *no_add_attrs = true;
-    }
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
   else
     add_no_sanitize_value (*node, SANITIZE_THREAD);
 
@@ -651,11 +652,9 @@  static tree
 handle_no_address_safety_analysis_attribute (tree *node, tree name, tree, int,
 					     bool *no_add_attrs)
 {
+  *no_add_attrs = true;
   if (TREE_CODE (*node) != FUNCTION_DECL)
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      *no_add_attrs = true;
-    }
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
   else
     add_no_sanitize_value (*node, SANITIZE_ADDRESS);
 
@@ -669,11 +668,9 @@  static tree
 handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int,
 				      bool *no_add_attrs)
 {
+  *no_add_attrs = true;
   if (TREE_CODE (*node) != FUNCTION_DECL)
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      *no_add_attrs = true;
-    }
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
   else
     add_no_sanitize_value (*node,
 			   SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
diff --git a/gcc/c/c-convert.c b/gcc/c/c-convert.c
index 65852ec71b4..33c9143e354 100644
--- a/gcc/c/c-convert.c
+++ b/gcc/c/c-convert.c
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "langhooks.h"
 #include "ubsan.h"
+#include "asan.h"
 
 /* Change of width--truncation and extension of integers or reals--
    is represented with NOP_EXPR.  Proper functioning of many things
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 82ad178d442..80598b5b614 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -53,6 +53,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "asan.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index b9ffd09c045..7f9ca58f9e3 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -50,6 +50,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gomp-constants.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "asan.h"
 
 /* Possible cases of implicit bad conversions.  Used to select
    diagnostic messages in convert_for_assignment.  */
diff --git a/gcc/convert.c b/gcc/convert.c
index e023888091d..429f988cbde 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "builtins.h"
 #include "ubsan.h"
+#include "asan.h"
 
 #define maybe_fold_build1_loc(FOLD_P, LOC, CODE, TYPE, EXPR) \
   ((FOLD_P) ? fold_build1_loc (LOC, CODE, TYPE, EXPR)	     \
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index e136deed457..a84b8aa0ea6 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -36,6 +36,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "gimplify.h"
 #include "intl.h"
+#include "asan.h"
 
 /* Id for dumping the class hierarchy.  */
 int class_dump_id;
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 0f13ff69efe..fc2c69455c0 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ubsan.h"
 #include "cilk.h"
 #include "cp-cilkplus.h"
+#include "asan.h"
 
 /* Forward declarations.  */
 
diff --git a/gcc/cp/cp-ubsan.c b/gcc/cp/cp-ubsan.c
index 95817dfc1f7..f00f870bd3e 100644
--- a/gcc/cp/cp-ubsan.c
+++ b/gcc/cp/cp-ubsan.c
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "cp-tree.h"
 #include "ubsan.h"
+#include "asan.h"
 
 /* Test if we should instrument vptr access.  */
 
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 27dec0f0981..a02cea3c602 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -51,6 +51,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cilk.h"
 #include "builtins.h"
 #include "gimplify.h"
+#include "asan.h"
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index a742cb83bbc..90abd23a267 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -30,6 +30,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "c-family/c-ubsan.h"
 #include "intl.h"
+#include "asan.h"
 
 static bool begin_init_stmts (tree *, tree *);
 static tree finish_init_stmts (bool, tree, tree);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 925aab4d410..b3a554d9cd0 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ubsan.h"
 #include "params.h"
 #include "gcc-rich-location.h"
+#include "asan.h"
 
 static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 5579115108f..0f8e326a0e8 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -56,6 +56,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "ipa-chkp.h"
 #include "tree-cfg.h"
 #include "fold-const-call.h"
+#include "asan.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 6bd9af0ada6..8fb6f505eb7 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -117,6 +117,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "auto-profile.h"
 #include "builtins.h"
 #include "fibonacci_heap.h"
+#include "asan.h"
 
 typedef fibonacci_heap <sreal, cgraph_edge> edge_heap_t;
 typedef fibonacci_node <sreal, cgraph_edge> edge_heap_node_t;
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
index 781d70d6038..44dc79535d2 100644
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -16,6 +16,9 @@  NAME (void) \
 
 void
 __attribute__((no_sanitize(("address"))))
+__attribute__((no_sanitize(("undefined"))))
+__attribute__((no_sanitize(("address"))))
+__attribute__((no_sanitize(("null"))))
 FN (fn1)
 
 void
diff --git a/gcc/tree.c b/gcc/tree.c
index 8979819adf7..a58f9aaa69e 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14442,24 +14442,6 @@  nonnull_arg_p (const_tree arg)
   return false;
 }
 
-/* Return true when flag_sanitize & FLAG is non-zero.  If FN is non-null,
-   remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES.  */
-
-bool
-sanitize_flags_p (unsigned int flag, const_tree fn)
-{
-  unsigned int result_flags = flag_sanitize & flag;
-
-  if (fn != NULL_TREE)
-    {
-      tree value = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (fn));
-      if (value)
-	result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
-    }
-
-  return result_flags;
-}
-
 /* Combine LOC and BLOCK to a combined adhoc loc, retaining any range
    information.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 22b9ec3f0e7..c6e883c489f 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4248,10 +4248,6 @@  extern tree merge_dllimport_decl_attributes (tree, tree);
 /* Handle a "dllimport" or "dllexport" attribute.  */
 extern tree handle_dll_attribute (tree *, tree, tree, int, bool *);
 
-
-extern bool sanitize_flags_p (unsigned int flag,
-			      const_tree fn = current_function_decl);
-
 /* Returns true iff CAND and BASE have equivalent language-specific
    qualifiers.  */
 
-- 
2.13.0