diff mbox

[v2] Implement no_sanitize function attribute

Message ID 7a8c52c8-5547-e3f2-02d1-9bc00e69d313@suse.cz
State New
Headers show

Commit Message

Martin Liška June 8, 2017, 1:30 p.m. UTC
On 06/02/2017 12:40 PM, Richard Biener wrote:
> On Wed, May 31, 2017 at 4:13 PM, Martin Liška <mliska@suse.cz> wrote:
>> On 05/31/2017 03:31 PM, Richard Biener wrote:
>>> On Wed, May 31, 2017 at 2:28 PM, Martin Liška <mliska@suse.cz> wrote:
>>>> On 05/31/2017 02:04 PM, Richard Biener wrote:
>>>>> On Wed, May 31, 2017 at 1:51 PM, Jakub Jelinek <jakub@redhat.com> wrote:
>>>>>> On Wed, May 31, 2017 at 01:46:00PM +0200, Richard Biener wrote:
>>>>>>> Just wanting to add that "ab-"using options/variables to implement
>>>>>>> what are really
>>>>>>> function attributes doesn't look very clean.  Unless the plan is to get rid of
>>>>>>> function attributes in favor of per-function options.
>>>>>>
>>>>>> Function attribute here is one thing (the way user writes it) and that
>>>>>> combined with the command line options determines the sanitization performed
>>>>>> (the function attributes only say what sanitization flags should be
>>>>>> ignored).  The proposed per-function variable is just a cache of this
>>>>>> information, because parsing function attributes every time is way too
>>>>>> expensive.
>>>>>
>>>>> True, but isn't that just an excuse to not improve attribute list
>>>>> representation?
>>>>>
>>>>> Ideally we'd have sth like attributes.def and a sorted vector of
>>>>> integer id, args
>>>>> pairs.  Using a sorted vector of the existing stuff (compared to the tree list)
>>>>> might also help.
>>>>
>>>> Then it would be tree-wise very similar to CONSTRUCTOR which also contains vector
>>>> of (index, value) pairs?
>>>>
>>>>>
>>>>> Yes, we'd get (quite?) a bit less attribute list sharing this way but
>>>>> we can still
>>>>> share the actual tree-whatever thing that represents the args.
>>>>
>>>> Any estimation how difficult such transformation would be?
>>>
>>> attribute lists are dealt with in quite some places (with or without
>>> helpers) so I guess it would be somewhat invasive but largely
>>> mechanical.  Using a .def file vs. the current strings can be
>>> done separately -- after all we can also sort strings.  I suspect
>>> doing the string -> ID transform pays off faster (still linear search
>>> but integer comparison instead of string compare).
>>
>> Ok, I'm ready to do the transformation in this stage1. That said, will you be
>> Jakub fine with the original patch (rebase will be needed) as it is, using
>> DECL_ATTRIBUTE?
> 
> It looks ok to me though I miss __attribute__((no_sanitize("all"))) (or is no
> argument equal to 'all' -- spelling out all those opts in the testcases looks
> awkward).

Hi.

"all" value is supported, I fixed small issue with that and it's covered by
a test-case now.

> 
> I'd appreciate a 2nd eye though, the patch is large.
> 
> The use of sanitize_flags_p (...) in pass_ubsan::execute would appreciate
> sth like
> 
>   flags = sanitize_flags (..., fun->decl);
> 
> so it can cache across different flag settings.

Done that.

Martin

> 
> Thanks,
> Richard.
> 
>> Thanks,
>> Martin
>>
>>>
>>> Richard.
>>>
>>>> Martin
>>>>
>>>>>
>>>>> Richard.
>>>>>
>>>>>>
>>>>>>         Jakub
>>>>
>>

Comments

Jakub Jelinek June 8, 2017, 1:47 p.m. UTC | #1
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.

And, it should have an early out,
  if (result_flags == 0)
    return false;

> +
> +  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.

+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.

	Jakub
diff mbox

Patch

From 2c4a169b1415d92e554c3adf7b5ea1143ae825df Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Thu, 1 Jun 2017 11:36:36 +0200
Subject: [PATCH] Implement no_sanitize function attribute

gcc/testsuite/ChangeLog:

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

	* c-c++-common/ubsan/attrib-2.c (float_cast2): Enhance the
	test by adding no_sanitize attribute.
	* gcc.dg/asan/use-after-scope-4.c: Likewise.

gcc/c-family/ChangeLog:

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

	* c-attribs.c (add_no_sanitize_value): New function.
	(handle_no_sanitize_attribute): Likewise.
	(handle_no_sanitize_address_attribute): Use the function.
	(handle_no_sanitize_thread_attribute): New function.
	(handle_no_address_safety_analysis_attribute): Use
	add_no_sanitize_value.
	(handle_no_sanitize_undefined_attribute): Likewise.
	* c-common.h: Declare new functions.
	* c-ubsan.c (ubsan_instrument_division): Use sanitize_flags_p.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_bounds): Likewise.
	(ubsan_maybe_instrument_array_ref): Likewise.
	(ubsan_maybe_instrument_reference_or_call): Likewise.

gcc/ChangeLog:

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

	* asan.c (asan_sanitize_stack_p): Use sanitize_flags_p.
	(gate_asan): Likewise.
	* asan.h (asan_no_sanitize_address_p): Remove the function.
	* builtins.def: Fix coding style.
	* common.opt: Use renamed enum value.
	* convert.c (convert_to_integer_1): Use sanitize_flags_p.
	* doc/extend.texi: Document no_sanitize attribute.
	* flag-types.h (enum sanitize_code): Rename SANITIZE_NONDEFAULT
	to SANITIZE_UNDEFINED_NONDEFAULT.
	* gcc.c (sanitize_spec_function): Use the renamed enum value.
	* gimple-fold.c (optimize_atomic_compare_exchange_p):
	Use sanitize_flags_p.
	* gimplify.c (gimplify_function_tree): Likewise.
	* ipa-inline.c (sanitize_attrs_match_for_inline_p): Likewise.
	* opts.c (parse_no_sanitize_attribute): New function.
	(common_handle_option): Use renamed enum value.
	* opts.h (parse_no_sanitize_attribute): Declare.
	* tree.c (sanitize_flags_p): New function.
	* tree.h: Declared here.
	* tsan.c: Use sanitize_flags_p.
	* ubsan.c (ubsan_expand_null_ifn): Likewise.
	(instrument_mem_ref): Likewise.
	(instrument_bool_enum_load): Likewise.
	(do_ubsan_in_current_function): Remove the function.
	(pass_ubsan::execute): Use sanitize_flags_p.
	* ubsan.h: Remove do_ubsan_in_current_function

gcc/cp/ChangeLog:

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

	* class.c (build_base_path): Use sanitize_flags_p.
	* cp-gimplify.c (cp_genericize_r): Likewise.
	(cp_genericize_tree): Likewise.
	(cp_genericize): Likewise.
	* cp-ubsan.c (cp_ubsan_instrument_vptr_p): Likewise.
	* decl.c (compute_array_index_type): Likewise.
	(start_preparsed_function): Likewise.
	* decl2.c (one_static_initialization_or_destruction): Likewise.
	* init.c (finish_length_check): Likewise.
	* lambda.c (maybe_add_lambda_conv_op): Likewise.
	* typeck.c (cp_build_binary_op): Likewise.
	(build_static_cast_1): Likewise.

gcc/c/ChangeLog:

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

	* c-convert.c (convert): Use sanitize_flags_p.
	* c-decl.c (grokdeclarator): Likewise.
	* c-typeck.c (convert_for_assignment): Likewise.
	(c_finish_return): Likewise.
	(build_binary_op): Likewise.
---
 gcc/asan.c                                    |  8 +--
 gcc/asan.h                                    |  7 --
 gcc/builtins.def                              |  3 +-
 gcc/c-family/c-attribs.c                      | 99 +++++++++++++++++++++++++--
 gcc/c-family/c-common.h                       |  1 +
 gcc/c-family/c-ubsan.c                        | 22 +++---
 gcc/c-family/c-ubsan.h                        |  3 -
 gcc/c/c-convert.c                             |  5 +-
 gcc/c/c-decl.c                                |  5 +-
 gcc/c/c-typeck.c                              | 15 ++--
 gcc/common.opt                                |  2 +-
 gcc/convert.c                                 |  3 +-
 gcc/cp/class.c                                |  3 +-
 gcc/cp/cp-gimplify.c                          | 18 +++--
 gcc/cp/cp-ubsan.c                             |  2 +-
 gcc/cp/decl.c                                 |  5 +-
 gcc/cp/decl2.c                                |  2 +-
 gcc/cp/init.c                                 |  3 +-
 gcc/cp/lambda.c                               |  4 +-
 gcc/cp/typeck.c                               | 15 ++--
 gcc/doc/extend.texi                           | 12 ++++
 gcc/flag-types.h                              |  4 +-
 gcc/gcc.c                                     |  3 +-
 gcc/gimple-fold.c                             |  2 +-
 gcc/gimplify.c                                |  5 +-
 gcc/ipa-inline.c                              | 10 +--
 gcc/opts.c                                    | 31 ++++++++-
 gcc/opts.h                                    |  2 +
 gcc/testsuite/c-c++-common/ubsan/attrib-2.c   | 10 +++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c | 39 ++++++++---
 gcc/tree.c                                    | 18 +++++
 gcc/tree.h                                    |  4 ++
 gcc/tsan.c                                    |  8 +--
 gcc/ubsan.c                                   | 46 +++++--------
 gcc/ubsan.h                                   |  1 -
 35 files changed, 278 insertions(+), 142 deletions(-)

diff --git a/gcc/asan.c b/gcc/asan.c
index bf564a46b28..e730530930b 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -305,9 +305,7 @@  asan_mark_p (gimple *stmt, enum asan_mark_flags flag)
 bool
 asan_sanitize_stack_p (void)
 {
-  return ((flag_sanitize & SANITIZE_ADDRESS)
-	  && ASAN_STACK
-	  && !asan_no_sanitize_address_p ());
+  return (sanitize_flags_p (SANITIZE_ADDRESS) && ASAN_STACK);
 }
 
 /* Checks whether section SEC should be sanitized.  */
@@ -3194,9 +3192,7 @@  asan_instrument (void)
 static bool
 gate_asan (void)
 {
-  return (flag_sanitize & SANITIZE_ADDRESS) != 0
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl));
+  return sanitize_flags_p (SANITIZE_ADDRESS);
 }
 
 namespace {
diff --git a/gcc/asan.h b/gcc/asan.h
index 57663977603..a590d0a5ace 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -144,13 +144,6 @@  asan_sanitize_use_after_scope (void)
   return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ());
 }
 
-static inline bool
-asan_no_sanitize_address_p (void)
-{
-  return lookup_attribute ("no_sanitize_address",
-			   DECL_ATTRIBUTES (current_function_decl));
-}
-
 /* Return true if DECL should be guarded on the stack.  */
 
 static inline bool
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 58d78dbbdee..3b95eb72ae9 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -236,7 +236,8 @@  along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
-				| SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT) \
+				| SANITIZE_UNDEFINED \
+				| SANITIZE_UNDEFINED_NONDEFAULT) \
 	       || flag_sanitize_coverage))
 
 #undef DEF_CILKPLUS_BUILTIN
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 695c58c0a14..abb43d0d02c 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -51,8 +51,11 @@  static tree handle_common_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
 static tree handle_hot_attribute (tree *, tree, tree, int, bool *);
 static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
+static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
 						  int, bool *);
+static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
+						 int, bool *);
 static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
 							 int, bool *);
 static tree handle_no_sanitize_undefined_attribute (tree *, tree, tree, int,
@@ -285,11 +288,14 @@  const struct attribute_spec c_common_attribute_table[] =
 			      0, 0, true, false, false,
 			      handle_no_address_safety_analysis_attribute,
 			      false },
+  { "no_sanitize",	      1, 1, true, false, false,
+			      handle_no_sanitize_attribute,
+			      false },
   { "no_sanitize_address",    0, 0, true, false, false,
 			      handle_no_sanitize_address_attribute,
 			      false },
   { "no_sanitize_thread",     0, 0, true, false, false,
-			      handle_no_sanitize_address_attribute,
+			      handle_no_sanitize_thread_attribute,
 			      false },
   { "no_sanitize_undefined",  0, 0, true, false, false,
 			      handle_no_sanitize_undefined_attribute,
@@ -547,6 +553,60 @@  handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
   return NULL_TREE;
 }
 
+/* Add FLAGS for a function NODE to no_sanitize_flags in DECL_ATTRIBUTES.  */
+
+void
+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));
+}
+
+/* Handle a "no_sanitize" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
+			      bool *no_add_attrs)
+{
+  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;
+    }
+
+  if (TREE_CODE (id) != STRING_CST)
+    {
+      error ("no_sanitize argument not a string");
+      return NULL_TREE;
+    }
+
+  char *error_value = NULL;
+  char *string = ASTRDUP (TREE_STRING_POINTER (id));
+  unsigned int flags = parse_no_sanitize_attribute (string, &error_value);
+
+  if (error_value)
+    {
+      error ("wrong argument: \"%s\"", error_value);
+      return NULL_TREE;
+    }
+
+  add_no_sanitize_value (*node, flags);
+
+  return NULL_TREE;
+}
+
 /* Handle a "no_sanitize_address" attribute; arguments as in
    struct attribute_spec.handler.  */
 
@@ -559,10 +619,31 @@  handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
       warning (OPT_Wattributes, "%qE attribute ignored", name);
       *no_add_attrs = true;
     }
+  else
+    add_no_sanitize_value (*node, SANITIZE_ADDRESS);
+
+  return NULL_TREE;
+}
+
+/* Handle a "no_sanitize_thread" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_sanitize_thread_attribute (tree *node, tree name, tree, int,
+				      bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else
+    add_no_sanitize_value (*node, SANITIZE_THREAD);
 
   return NULL_TREE;
 }
 
+
 /* Handle a "no_address_safety_analysis" attribute; arguments as in
    struct attribute_spec.handler.  */
 
@@ -571,12 +652,13 @@  handle_no_address_safety_analysis_attribute (tree *node, tree name, tree, int,
 					     bool *no_add_attrs)
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
-    warning (OPT_Wattributes, "%qE attribute ignored", name);
-  else if (!lookup_attribute ("no_sanitize_address", DECL_ATTRIBUTES (*node)))
-    DECL_ATTRIBUTES (*node)
-      = tree_cons (get_identifier ("no_sanitize_address"),
-		   NULL_TREE, DECL_ATTRIBUTES (*node));
-  *no_add_attrs = true;
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  else
+    add_no_sanitize_value (*node, SANITIZE_ADDRESS);
+
   return NULL_TREE;
 }
 
@@ -592,6 +674,9 @@  handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int,
       warning (OPT_Wattributes, "%qE attribute ignored", name);
       *no_add_attrs = true;
     }
+  else
+    add_no_sanitize_value (*node,
+			   SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
 
   return NULL_TREE;
 }
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 79072e6a8b7..1748c1979aa 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1552,6 +1552,7 @@  extern enum flt_eval_method
 excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method);
 
 extern int c_flt_eval_method (bool ts18661_p);
+extern void add_no_sanitize_value (tree node, unsigned int flags);
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index e48841a334c..a072d19eda6 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -49,11 +49,11 @@  ubsan_instrument_division (location_t loc, tree op0, tree op1)
   op1 = unshare_expr (op1);
 
   if (TREE_CODE (type) == INTEGER_TYPE
-      && (flag_sanitize & SANITIZE_DIVIDE))
+      && sanitize_flags_p (SANITIZE_DIVIDE))
     t = fold_build2 (EQ_EXPR, boolean_type_node,
 		     op1, build_int_cst (type, 0));
   else if (TREE_CODE (type) == REAL_TYPE
-	   && (flag_sanitize & SANITIZE_FLOAT_DIVIDE))
+	   && sanitize_flags_p (SANITIZE_FLOAT_DIVIDE))
     t = fold_build2 (EQ_EXPR, boolean_type_node,
 		     op1, build_real (type, dconst0));
   else
@@ -61,7 +61,7 @@  ubsan_instrument_division (location_t loc, tree op0, tree op1)
 
   /* We check INT_MIN / -1 only for signed types.  */
   if (TREE_CODE (type) == INTEGER_TYPE
-      && (flag_sanitize & SANITIZE_DIVIDE)
+      && sanitize_flags_p (SANITIZE_DIVIDE)
       && !TYPE_UNSIGNED (type))
     {
       tree x;
@@ -131,7 +131,7 @@  ubsan_instrument_shift (location_t loc, enum tree_code code,
      Also punt on bit-fields.  */
   if (TYPE_OVERFLOW_WRAPS (type0)
       || GET_MODE_BITSIZE (TYPE_MODE (type0)) != TYPE_PRECISION (type0)
-      || (flag_sanitize & SANITIZE_SHIFT_BASE) == 0)
+      || !sanitize_flags_p (SANITIZE_SHIFT_BASE))
     ;
 
   /* For signed x << y, in C99/C11, the following:
@@ -178,7 +178,7 @@  ubsan_instrument_shift (location_t loc, enum tree_code code,
   tree else_t = void_node;
   if (tt)
     {
-      if ((flag_sanitize & SANITIZE_SHIFT_EXPONENT) == 0)
+      if (!sanitize_flags_p (SANITIZE_SHIFT_EXPONENT))
 	{
 	  t = fold_build1 (TRUTH_NOT_EXPR, boolean_type_node, t);
 	  t = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, t, tt);
@@ -301,7 +301,7 @@  ubsan_instrument_bounds (location_t loc, tree array, tree *index,
   /* Detect flexible array members and suchlike, unless
      -fsanitize=bounds-strict.  */
   tree base = get_base_address (array);
-  if ((flag_sanitize & SANITIZE_BOUNDS_STRICT) == 0
+  if (!sanitize_flags_p (SANITIZE_BOUNDS_STRICT)
       && TREE_CODE (array) == COMPONENT_REF
       && base && (INDIRECT_REF_P (base) || TREE_CODE (base) == MEM_REF))
     {
@@ -373,7 +373,7 @@  void
 ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
 {
   if (!ubsan_array_ref_instrumented_p (*expr_p)
-      && do_ubsan_in_current_function ())
+      && sanitize_flags_p (SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT))
     {
       tree op0 = TREE_OPERAND (*expr_p, 0);
       tree op1 = TREE_OPERAND (*expr_p, 1);
@@ -393,7 +393,7 @@  static tree
 ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype,
 					  enum ubsan_null_ckind ckind)
 {
-  if (!do_ubsan_in_current_function ())
+  if (!sanitize_flags_p (SANITIZE_ALIGNMENT | SANITIZE_NULL))
     return NULL_TREE;
 
   tree type = TREE_TYPE (ptype);
@@ -401,7 +401,7 @@  ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype,
   bool instrument = false;
   unsigned int mina = 0;
 
-  if (flag_sanitize & SANITIZE_ALIGNMENT)
+  if (sanitize_flags_p (SANITIZE_ALIGNMENT))
     {
       mina = min_align_of_type (type);
       if (mina <= 1)
@@ -419,7 +419,7 @@  ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype,
     }
   else
     {
-      if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR)
+      if (sanitize_flags_p (SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR)
 	{
 	  bool strict_overflow_p = false;
 	  /* tree_single_nonzero_warnv_p will not return true for non-weak
@@ -435,7 +435,7 @@  ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype,
 	  flag_delete_null_pointer_checks
 	    = save_flag_delete_null_pointer_checks;
 	}
-      else if (flag_sanitize & SANITIZE_NULL)
+      else if (sanitize_flags_p (SANITIZE_NULL))
 	instrument = true;
       if (mina && mina > 1)
 	{
diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h
index 3c3ffc7f7a2..1e2d192bb31 100644
--- a/gcc/c-family/c-ubsan.h
+++ b/gcc/c-family/c-ubsan.h
@@ -31,7 +31,4 @@  extern void ubsan_maybe_instrument_array_ref (tree *, bool);
 extern void ubsan_maybe_instrument_reference (tree *);
 extern void ubsan_maybe_instrument_member_call (tree, bool);
 
-/* Declare this here as well as in ubsan.h. */
-extern bool do_ubsan_in_current_function (void);
-
 #endif  /* GCC_C_UBSAN_H  */
diff --git a/gcc/c/c-convert.c b/gcc/c/c-convert.c
index b8117b49ac9..65852ec71b4 100644
--- a/gcc/c/c-convert.c
+++ b/gcc/c/c-convert.c
@@ -106,10 +106,9 @@  convert (tree type, tree expr)
 
     case INTEGER_TYPE:
     case ENUMERAL_TYPE:
-      if (flag_sanitize & SANITIZE_FLOAT_CAST
+      if (sanitize_flags_p (SANITIZE_FLOAT_CAST)
 	  && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE
-	  && COMPLETE_TYPE_P (type)
-	  && do_ubsan_in_current_function ())
+	  && COMPLETE_TYPE_P (type))
 	{
 	  expr = save_expr (expr);
 	  tree check = ubsan_instrument_float_cast (loc, type, expr);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index f2b8096d84a..82ad178d442 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -6044,9 +6044,8 @@  grokdeclarator (const struct c_declarator *declarator,
 		       with known value.  */
 		    this_size_varies = size_varies = true;
 		    warn_variable_length_array (name, size);
-		    if (flag_sanitize & SANITIZE_VLA
-		        && decl_context == NORMAL
-			&& do_ubsan_in_current_function ())
+		    if (sanitize_flags_p (SANITIZE_VLA)
+			&& decl_context == NORMAL)
 		      {
 			/* Evaluate the array size only once.  */
 			size = save_expr (size);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 95a607a1e76..b9ffd09c045 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -6375,7 +6375,7 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
       if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE
 	  || (coder == REAL_TYPE
 	      && (codel == INTEGER_TYPE || codel == ENUMERAL_TYPE)
-	      && (flag_sanitize & SANITIZE_FLOAT_CAST)))
+	      && sanitize_flags_p (SANITIZE_FLOAT_CAST)))
 	in_late_binary_op = true;
       ret = convert_and_check (expr_loc != UNKNOWN_LOCATION
 			       ? expr_loc : location, type, orig_rhs);
@@ -9952,7 +9952,7 @@  c_finish_return (location_t loc, tree retval, tree origtype)
 	  || (TREE_CODE (TREE_TYPE (t)) == REAL_TYPE
 	      && (TREE_CODE (TREE_TYPE (res)) == INTEGER_TYPE
 		  || TREE_CODE (TREE_TYPE (res)) == ENUMERAL_TYPE)
-	      && (flag_sanitize & SANITIZE_FLOAT_CAST)))
+	      && sanitize_flags_p (SANITIZE_FLOAT_CAST)))
         in_late_binary_op = true;
       inner = t = convert (TREE_TYPE (res), t);
       in_late_binary_op = save;
@@ -11832,9 +11832,8 @@  build_binary_op (location_t location, enum tree_code code,
 	return error_mark_node;
     }
 
-  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
-			| SANITIZE_FLOAT_DIVIDE))
-      && do_ubsan_in_current_function ()
+  if (sanitize_flags_p ((SANITIZE_SHIFT
+			 | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
       && (doing_div_or_mod || doing_shift)
       && !require_constant_value)
     {
@@ -11843,10 +11842,10 @@  build_binary_op (location_t location, enum tree_code code,
       op1 = save_expr (op1);
       op0 = c_fully_fold (op0, false, NULL);
       op1 = c_fully_fold (op1, false, NULL);
-      if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
-						| SANITIZE_FLOAT_DIVIDE)))
+      if (doing_div_or_mod && (sanitize_flags_p ((SANITIZE_DIVIDE
+						  | SANITIZE_FLOAT_DIVIDE))))
 	instrument_expr = ubsan_instrument_division (location, op0, op1);
-      else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT))
+      else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT))
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
diff --git a/gcc/common.opt b/gcc/common.opt
index 13305558d2d..d3d86fc8a84 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -224,7 +224,7 @@  unsigned int flag_sanitize
 
 ; What sanitizers should recover from errors
 Variable
-unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
 
 fsanitize-coverage=trace-pc
 Common Report Var(flag_sanitize_coverage)
diff --git a/gcc/convert.c b/gcc/convert.c
index af8dfda0eb4..e023888091d 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -937,8 +937,7 @@  convert_to_integer_1 (tree type, tree expr, bool dofold)
       return build1 (CONVERT_EXPR, type, expr);
 
     case REAL_TYPE:
-      if (flag_sanitize & SANITIZE_FLOAT_CAST
-	  && do_ubsan_in_current_function ())
+      if (sanitize_flags_p (SANITIZE_FLOAT_CAST))
 	{
 	  expr = save_expr (expr);
 	  tree check = ubsan_instrument_float_cast (loc, type, expr);
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index eddc1188667..e136deed457 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -462,7 +462,8 @@  build_base_path (enum tree_code code,
       else
 	{
 	  tree t = expr;
-	  if ((flag_sanitize & SANITIZE_VPTR) && fixed_type_p == 0)
+	  if (sanitize_flags_p (SANITIZE_VPTR)
+	      && fixed_type_p == 0)
 	    {
 	      t = cp_ubsan_maybe_instrument_cast_to_vbase (input_location,
 							   probe, expr);
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 898a5ae2ae4..0f13ff69efe 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1262,8 +1262,7 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 				     : OMP_CLAUSE_DEFAULT_PRIVATE);
 	      }
 	}
-      if (flag_sanitize
-	  & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
+      if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
 	{
 	  /* The point here is to not sanitize static initializers.  */
 	  bool no_sanitize_p = wtd->no_sanitize_p;
@@ -1450,11 +1449,11 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       *stmt_p = cplus_expand_constant (stmt);
       *walk_subtrees = 0;
     }
-  else if ((flag_sanitize
-	    & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
+  else if (sanitize_flags_p ((SANITIZE_NULL
+			      | SANITIZE_ALIGNMENT | SANITIZE_VPTR))
 	   && !wtd->no_sanitize_p)
     {
-      if ((flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+      if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT)
 	  && TREE_CODE (stmt) == NOP_EXPR
 	  && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
 	ubsan_maybe_instrument_reference (stmt_p);
@@ -1470,9 +1469,9 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 		= TREE_CODE (fn) == ADDR_EXPR
 		  && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
 		  && DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0));
-	      if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+	      if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT))
 		ubsan_maybe_instrument_member_call (stmt, is_ctor);
-	      if ((flag_sanitize & SANITIZE_VPTR) && !is_ctor)
+	      if (sanitize_flags_p (SANITIZE_VPTR) && !is_ctor)
 		cp_ubsan_maybe_instrument_member_call (stmt);
 	    }
 	}
@@ -1499,7 +1498,7 @@  cp_genericize_tree (tree* t_p, bool handle_invisiref_parm_p)
   cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL);
   delete wtd.p_set;
   wtd.bind_expr_stack.release ();
-  if (flag_sanitize & SANITIZE_VPTR)
+  if (sanitize_flags_p (SANITIZE_VPTR))
     cp_ubsan_instrument_member_accesses (t_p);
 }
 
@@ -1622,8 +1621,7 @@  cp_genericize (tree fndecl)
      walk_tree's hash functionality.  */
   cp_genericize_tree (&DECL_SAVED_TREE (fndecl), true);
 
-  if (flag_sanitize & SANITIZE_RETURN
-      && do_ubsan_in_current_function ())
+  if (sanitize_flags_p (SANITIZE_RETURN))
     cp_ubsan_maybe_instrument_return (fndecl);
 
   /* Do everything else.  */
diff --git a/gcc/cp/cp-ubsan.c b/gcc/cp/cp-ubsan.c
index 71d315ec2b4..95817dfc1f7 100644
--- a/gcc/cp/cp-ubsan.c
+++ b/gcc/cp/cp-ubsan.c
@@ -32,7 +32,7 @@  cp_ubsan_instrument_vptr_p (tree type)
   if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
     return false;
 
-  if (!do_ubsan_in_current_function ())
+  if (!sanitize_flags_p (SANITIZE_VPTR))
     return false;
 
   if (type)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index e895fa7642e..27dec0f0981 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9522,8 +9522,7 @@  compute_array_index_type (tree name, tree size, tsubst_flags_t complain)
 
 	  stabilize_vla_size (itype);
 
-	  if (flag_sanitize & SANITIZE_VLA
-	      && do_ubsan_in_current_function ())
+	  if (sanitize_flags_p (SANITIZE_VLA))
 	    {
 	      /* We have to add 1 -- in the ubsan routine we generate
 		 LE_EXPR rather than LT_EXPR.  */
@@ -15106,7 +15105,7 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 
   if (!processing_template_decl
       && DECL_CONSTRUCTOR_P (decl1)
-      && (flag_sanitize & SANITIZE_VPTR)
+      && sanitize_flags_p (SANITIZE_VPTR)
       && !DECL_CLONED_FUNCTION_P (decl1)
       && !implicit_default_ctor_p (decl1))
     cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a095901be09..e6a770aa7de 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3737,7 +3737,7 @@  one_static_initialization_or_destruction (tree decl, tree init, bool initp)
       if (init)
 	{
 	  finish_expr_stmt (init);
-	  if (flag_sanitize & SANITIZE_ADDRESS)
+	  if (sanitize_flags_p (SANITIZE_ADDRESS, decl))
 	    {
 	      varpool_node *vnode = varpool_node::get (decl);
 	      if (vnode)
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 4ad2cae541e..a742cb83bbc 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -3911,8 +3911,7 @@  finish_length_check (tree atype, tree iterator, tree obase, unsigned n)
 	}
       /* Don't check an array new when -fno-exceptions.  */
     }
-  else if (flag_sanitize & SANITIZE_BOUNDS
-	   && do_ubsan_in_current_function ())
+  else if (sanitize_flags_p (SANITIZE_BOUNDS))
     {
       /* Make sure the last element of the initializer is in bounds. */
       finish_expr_stmt
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 5587f6021ea..52e1fb78865 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -1150,9 +1150,7 @@  maybe_add_lambda_conv_op (tree type)
     {
       /* Don't UBsan this function; we're deliberately calling op() with a null
 	 object argument.  */
-      tree attrs = build_tree_list (get_identifier ("no_sanitize_undefined"),
-				    NULL_TREE);
-      cplus_decl_attributes (&fn, attrs, 0);
+      add_no_sanitize_value (fn, SANITIZE_UNDEFINED);
     }
 
   add_method (type, fn, false);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index c657b3b9812..925aab4d410 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5230,10 +5230,9 @@  cp_build_binary_op (location_t location,
   if (build_type == NULL_TREE)
     build_type = result_type;
 
-  if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
-			| SANITIZE_FLOAT_DIVIDE))
+  if (sanitize_flags_p ((SANITIZE_SHIFT
+			 | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
       && !processing_template_decl
-      && do_ubsan_in_current_function ()
       && (doing_div_or_mod || doing_shift))
     {
       /* OP0 and/or OP1 might have side-effects.  */
@@ -5241,8 +5240,8 @@  cp_build_binary_op (location_t location,
       op1 = cp_save_expr (op1);
       op0 = fold_non_dependent_expr (op0);
       op1 = fold_non_dependent_expr (op1);
-      if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
-						| SANITIZE_FLOAT_DIVIDE)))
+      if (doing_div_or_mod
+	  && sanitize_flags_p (SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE))
 	{
 	  /* For diagnostics we want to use the promoted types without
 	     shorten_binary_op.  So convert the arguments to the
@@ -5255,7 +5254,7 @@  cp_build_binary_op (location_t location,
 	    cop1 = cp_convert (orig_type, op1, complain);
 	  instrument_expr = ubsan_instrument_division (location, cop0, cop1);
 	}
-      else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT))
+      else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT))
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
@@ -6797,7 +6796,7 @@  build_static_cast_1 (tree type, tree expr, bool c_cast_p,
 			  NULL, complain);
       expr = build_address (expr);
 
-      if (flag_sanitize & SANITIZE_VPTR)
+      if (sanitize_flags_p (SANITIZE_VPTR))
 	{
 	  tree ubsan_check
 	    = cp_ubsan_maybe_instrument_downcast (input_location, type,
@@ -6941,7 +6940,7 @@  build_static_cast_1 (tree type, tree expr, bool c_cast_p,
       expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false,
 			      complain);
 
-      if (flag_sanitize & SANITIZE_VPTR)
+      if (sanitize_flags_p (SANITIZE_VPTR))
 	{
 	  tree ubsan_check
 	    = cp_ubsan_maybe_instrument_downcast (input_location, type,
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 6cc95a8f7e9..7419c628a88 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2901,6 +2901,18 @@  This has a similar effect
 as the @option{-fno-toplevel-reorder} option, but only applies to the
 marked symbols.
 
+@item no_sanitize ("@var{sanitize_option}")
+@cindex @code{no_sanitize} function attribute
+The @code{no_sanitize} attribute on functions is used
+to inform the compiler that it should not do sanitization of all options
+mentioned in @var{sanitize_option}.  A list of values acceptable by
+@option{-fsanitize} option can be provided.
+
+@smallexample
+void __attribute__ ((no_sanitize ("alignment", "object-size")))
+f () @{ /* @r{Do something.} */; @}
+@end smallexample
+
 @item no_sanitize_address
 @itemx no_address_safety_analysis
 @cindex @code{no_sanitize_address} function attribute
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 27a38efdc8e..5faade53975 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -246,8 +246,8 @@  enum sanitize_code {
 		       | SANITIZE_NONNULL_ATTRIBUTE
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
-  SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+  SANITIZE_UNDEFINED_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
+				  | SANITIZE_BOUNDS_STRICT
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 4724276a318..3292532996b 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -9398,7 +9398,8 @@  sanitize_spec_function (int argc, const char **argv)
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
-    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
+    return ((flag_sanitize
+	     & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT))
 	    && !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
   if (strcmp (argv[0], "leak") == 0)
     return ((flag_sanitize
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d12f9d053c9..5579115108f 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -3479,7 +3479,7 @@  optimize_atomic_compare_exchange_p (gimple *stmt)
   if (gimple_call_num_args (stmt) != 6
       || !flag_inline_atomics
       || !optimize
-      || (flag_sanitize & (SANITIZE_THREAD | SANITIZE_ADDRESS)) != 0
+      || sanitize_flags_p (SANITIZE_THREAD | SANITIZE_ADDRESS)
       || !gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
       || !gimple_vdef (stmt)
       || !gimple_vuse (stmt))
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 2c7fc9fabd1..d5a8821846c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -12647,7 +12647,7 @@  gimplify_function_tree (tree fndecl)
       && !needs_to_live_in_memory (ret))
     DECL_GIMPLE_REG_P (ret) = 1;
 
-  if (asan_sanitize_use_after_scope () && !asan_no_sanitize_address_p ())
+  if (asan_sanitize_use_after_scope () && sanitize_flags_p (SANITIZE_ADDRESS))
     asan_poisoned_variables = new hash_set<tree> ();
   bind = gimplify_body (fndecl, true);
   if (asan_poisoned_variables)
@@ -12714,8 +12714,7 @@  gimplify_function_tree (tree fndecl)
       bind = new_bind;
     }
 
-  if ((flag_sanitize & SANITIZE_THREAD) != 0
-      && !lookup_attribute ("no_sanitize_thread", DECL_ATTRIBUTES (fndecl)))
+  if (sanitize_flags_p (SANITIZE_THREAD))
     {
       gcall *call = gimple_build_call_internal (IFN_TSAN_FUNC_EXIT, 0);
       gimple *tf = gimple_build_try (seq, call, GIMPLE_TRY_FINALLY);
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 0ebe1477f6c..6bd9af0ada6 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -257,17 +257,11 @@  report_inline_failed_reason (struct cgraph_edge *e)
 static bool
 sanitize_attrs_match_for_inline_p (const_tree caller, const_tree callee)
 {
-  /* Don't care if sanitizer is disabled */
-  if (!(flag_sanitize & SANITIZE_ADDRESS))
-    return true;
-
   if (!caller || !callee)
     return true;
 
-  return !!lookup_attribute ("no_sanitize_address",
-      DECL_ATTRIBUTES (caller)) == 
-      !!lookup_attribute ("no_sanitize_address",
-      DECL_ATTRIBUTES (callee));
+  return sanitize_flags_p (SANITIZE_ADDRESS, caller)
+    == sanitize_flags_p (SANITIZE_ADDRESS, callee);
 }
 
 /* Used for flags where it is safe to inline when caller's value is
diff --git a/gcc/opts.c b/gcc/opts.c
index ffedb10f18f..c5620bde5a0 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1655,6 +1655,33 @@  parse_sanitizer_options (const char *p, location_t loc, int scode,
   return flags;
 }
 
+unsigned int
+parse_no_sanitize_attribute (char *value, char **wrong_argument)
+{
+  unsigned int flags = 0;
+  unsigned int i;
+  char *q = strtok (value, ",");
+
+  while (q != NULL)
+    {
+      for (i = 0; sanitizer_opts[i].name != NULL; ++i)
+	if (strcmp (sanitizer_opts[i].name, q) == 0)
+	  {
+	    flags |= sanitizer_opts[i].flag;
+	    if (sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
+	      flags |= SANITIZE_UNDEFINED_NONDEFAULT;
+	    break;
+	  }
+
+      if (sanitizer_opts[i].name == NULL)
+	*wrong_argument = q;
+
+      q = strtok (NULL, ",");
+    }
+
+  return flags;
+}
+
 /* Handle target- and language-independent options.  Return zero to
    generate an "unknown option" message.  Only options that need
    extra handling need to be listed here; if you simply want
@@ -1891,11 +1918,11 @@  common_handle_option (struct gcc_options *opts,
     case OPT_fsanitize_recover:
       if (value)
 	opts->x_flag_sanitize_recover
-	  |= (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT)
+	  |= (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT)
 	     & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN);
       else
 	opts->x_flag_sanitize_recover
-	  &= ~(SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT);
+	  &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
       break;
 
     case OPT_O:
diff --git a/gcc/opts.h b/gcc/opts.h
index eb626aa90ec..16371e8141f 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -378,6 +378,8 @@  extern void print_ignored_options (void);
 extern void handle_common_deferred_options (void);
 unsigned int parse_sanitizer_options (const char *, location_t, int,
 				      unsigned int, int, bool);
+
+unsigned int parse_no_sanitize_attribute (char *value, char **wrong_argument);
 extern bool common_handle_option (struct gcc_options *opts,
 				  struct gcc_options *opts_set,
 				  const struct cl_decoded_option *decoded,
diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-2.c b/gcc/testsuite/c-c++-common/ubsan/attrib-2.c
index 71f2e58ea67..3f0a9c35d98 100644
--- a/gcc/testsuite/c-c++-common/ubsan/attrib-2.c
+++ b/gcc/testsuite/c-c++-common/ubsan/attrib-2.c
@@ -68,4 +68,14 @@  float_cast (void)
   c = d;
 }
 
+__attribute__((no_sanitize(("undefined"))))
+static void
+float_cast2 (void)
+{
+  volatile double d = 300;
+  volatile signed char c;
+  c = d;
+}
+
+
 /* { dg-final { scan-assembler-not "__ubsan_handle" } } */
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 77d7052bd19..781d70d6038 100644
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -1,16 +1,37 @@ 
 // { dg-do run }
 
-int
+#define FN(NAME) \
+NAME (void) \
+{ \
+  char *ptr; \
+  char *ptr2; \
+  { \
+    char my_char[9]; \
+    ptr = &my_char[0]; \
+    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); \
+  } \
+ \
+  *(ptr2+9) = 'c'; \
+}
+
+void
+__attribute__((no_sanitize(("address"))))
+FN (fn1)
+
+void
+__attribute__((no_sanitize(("all"))))
+FN (fn2)
+
+void
 __attribute__((no_sanitize_address))
+FN (fn3)
+
+int
 main (void)
 {
-  char *ptr;
-  char *ptr2;
-  {
-    char my_char[9];
-    ptr = &my_char[0];
-    __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2));
-  }
+  fn1 ();
+  fn2 ();
+  fn3 ();
 
-  *(ptr2+9) = 'c';
+  return 0;
 }
diff --git a/gcc/tree.c b/gcc/tree.c
index a58f9aaa69e..8979819adf7 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14442,6 +14442,24 @@  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 c6e883c489f..22b9ec3f0e7 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4248,6 +4248,10 @@  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.  */
 
diff --git a/gcc/tsan.c b/gcc/tsan.c
index dd8cd85647c..2f98b936c03 100644
--- a/gcc/tsan.c
+++ b/gcc/tsan.c
@@ -896,9 +896,7 @@  public:
   opt_pass * clone () { return new pass_tsan (m_ctxt); }
   virtual bool gate (function *)
 {
-  return ((flag_sanitize & SANITIZE_THREAD) != 0
-	  && !lookup_attribute ("no_sanitize_thread",
-                                DECL_ATTRIBUTES (current_function_decl)));
+  return sanitize_flags_p (SANITIZE_THREAD);
 }
 
   virtual unsigned int execute (function *) { return tsan_pass (); }
@@ -938,9 +936,7 @@  public:
   /* opt_pass methods: */
   virtual bool gate (function *)
     {
-      return ((flag_sanitize & SANITIZE_THREAD) != 0 && !optimize
-	      && !lookup_attribute ("no_sanitize_thread",
-				    DECL_ATTRIBUTES (current_function_decl)));
+      return (sanitize_flags_p (SANITIZE_THREAD) && !optimize);
     }
 
   virtual unsigned int execute (function *) { return tsan_pass (); }
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index a4808d2d60c..95e36a9230a 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -757,7 +757,7 @@  ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
 	  gsi_insert_before (&gsi, g, GSI_SAME_STMT);
 	}
     }
-  check_null = (flag_sanitize & SANITIZE_NULL) != 0;
+  check_null = sanitize_flags_p (SANITIZE_NULL);
 
   if (check_align == NULL_TREE && !check_null)
     {
@@ -1181,13 +1181,13 @@  instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter,
 {
   enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
   unsigned int align = 0;
-  if (flag_sanitize & SANITIZE_ALIGNMENT)
+  if (sanitize_flags_p (SANITIZE_ALIGNMENT))
     {
       align = min_align_of_type (TREE_TYPE (base));
       if (align <= 1)
 	align = 0;
     }
-  if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0)
+  if (align == 0 && !sanitize_flags_p (SANITIZE_NULL))
     return;
   tree t = TREE_OPERAND (base, 0);
   if (!POINTER_TYPE_P (TREE_TYPE (t)))
@@ -1356,13 +1356,14 @@  instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   tree type = TREE_TYPE (rhs);
   tree minv = NULL_TREE, maxv = NULL_TREE;
 
-  if (TREE_CODE (type) == BOOLEAN_TYPE && (flag_sanitize & SANITIZE_BOOL))
+  if (TREE_CODE (type) == BOOLEAN_TYPE
+      && sanitize_flags_p (SANITIZE_BOOL))
     {
       minv = boolean_false_node;
       maxv = boolean_true_node;
     }
   else if (TREE_CODE (type) == ENUMERAL_TYPE
-	   && (flag_sanitize & SANITIZE_ENUM)
+	   && sanitize_flags_p (SANITIZE_ENUM)
 	   && TREE_TYPE (type) != NULL_TREE
 	   && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE
 	   && (TYPE_PRECISION (TREE_TYPE (type))
@@ -1925,16 +1926,6 @@  instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
   gsi_insert_before (gsi, g, GSI_SAME_STMT);
 }
 
-/* True if we want to play UBSan games in the current function.  */
-
-bool
-do_ubsan_in_current_function ()
-{
-  return (current_function_decl != NULL_TREE
-	  && !lookup_attribute ("no_sanitize_undefined",
-				DECL_ATTRIBUTES (current_function_decl)));
-}
-
 namespace {
 
 const pass_data pass_data_ubsan =
@@ -1960,13 +1951,12 @@  public:
   /* opt_pass methods: */
   virtual bool gate (function *)
     {
-      return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
-			      | SANITIZE_BOOL | SANITIZE_ENUM
-			      | SANITIZE_ALIGNMENT
-			      | SANITIZE_NONNULL_ATTRIBUTE
-			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
-			      | SANITIZE_OBJECT_SIZE)
-	&& do_ubsan_in_current_function ();
+      return sanitize_flags_p ((SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+				| SANITIZE_BOOL | SANITIZE_ENUM
+				| SANITIZE_ALIGNMENT
+				| SANITIZE_NONNULL_ATTRIBUTE
+				| SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+				| SANITIZE_OBJECT_SIZE));
     }
 
   virtual unsigned int execute (function *);
@@ -1993,11 +1983,11 @@  pass_ubsan::execute (function *fun)
 	      continue;
 	    }
 
-	  if ((flag_sanitize & SANITIZE_SI_OVERFLOW)
+	  if ((sanitize_flags_p (SANITIZE_SI_OVERFLOW, fun->decl))
 	      && is_gimple_assign (stmt))
 	    instrument_si_overflow (gsi);
 
-	  if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+	  if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT, fun->decl))
 	    {
 	      if (gimple_store_p (stmt))
 		instrument_null (gsi, true);
@@ -2005,14 +1995,14 @@  pass_ubsan::execute (function *fun)
 		instrument_null (gsi, false);
 	    }
 
-	  if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
+	  if (sanitize_flags_p (SANITIZE_BOOL | SANITIZE_ENUM, fun->decl)
 	      && gimple_assign_load_p (stmt))
 	    {
 	      instrument_bool_enum_load (&gsi);
 	      bb = gimple_bb (stmt);
 	    }
 
-	  if ((flag_sanitize & SANITIZE_NONNULL_ATTRIBUTE)
+	  if (sanitize_flags_p (SANITIZE_NONNULL_ATTRIBUTE, fun->decl)
 	      && is_gimple_call (stmt)
 	      && !gimple_call_internal_p (stmt))
 	    {
@@ -2020,14 +2010,14 @@  pass_ubsan::execute (function *fun)
 	      bb = gimple_bb (stmt);
 	    }
 
-	  if ((flag_sanitize & SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+	  if (sanitize_flags_p (SANITIZE_RETURNS_NONNULL_ATTRIBUTE, fun->decl)
 	      && gimple_code (stmt) == GIMPLE_RETURN)
 	    {
 	      instrument_nonnull_return (&gsi);
 	      bb = gimple_bb (stmt);
 	    }
 
-	  if (flag_sanitize & SANITIZE_OBJECT_SIZE)
+	  if (sanitize_flags_p (SANITIZE_OBJECT_SIZE, fun->decl))
 	    {
 	      if (gimple_store_p (stmt))
 		instrument_object_size (&gsi, true);
diff --git a/gcc/ubsan.h b/gcc/ubsan.h
index f04929d6678..fddd359ebc3 100644
--- a/gcc/ubsan.h
+++ b/gcc/ubsan.h
@@ -42,7 +42,6 @@  enum ubsan_print_style {
   UBSAN_PRINT_ARRAY
 };
 
-extern bool do_ubsan_in_current_function (void);
 extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
 extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *);
 extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *);
-- 
2.13.0