diff mbox

[v2] Implement no_sanitize function attribute

Message ID dc4e98a7-3003-f255-d6a6-906cd0944ef5@suse.cz
State New
Headers show

Commit Message

Martin Liška June 13, 2017, 11:09 a.m. UTC
On 06/09/2017 03:35 PM, Richard Biener wrote:
> You can directly transform to no_sanitize with integer mask, not sure why
> you'd need an intermediate step with a string?

Hello.

Done in attached patch, I'm sending both incremental and final version (complete patch).
I also decided to support no_sanitize attribute in pretty printer:

__attribute__((no_sanitize (address | shift | shift-base | shift-exponent | integer-divide-by-zero | undefined | unreachable | vla-bound | return | null | signed-integer-overflow | bool | enum | float-divide-by-zero | float-cast-overflow | bounds | bounds-strict | alignment | nonnull-attribute | returns-nonnull-attribute | object-size | vptr)))
fn1 ()
{
  char my_char[9];
  char * ptr2;
  char * ptr;
..


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

Ready to be installed?
Martin

Comments

Richard Biener June 13, 2017, 1:49 p.m. UTC | #1
On Tue, Jun 13, 2017 at 1:09 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 03:35 PM, Richard Biener wrote:
>> You can directly transform to no_sanitize with integer mask, not sure why
>> you'd need an intermediate step with a string?
>
> Hello.
>
> Done in attached patch, I'm sending both incremental and final version (complete patch).
> I also decided to support no_sanitize attribute in pretty printer:
>
> __attribute__((no_sanitize (address | shift | shift-base | shift-exponent | integer-divide-by-zero | undefined | unreachable | vla-bound | return | null | signed-integer-overflow | bool | enum | float-divide-by-zero | float-cast-overflow | bounds | bounds-strict | alignment | nonnull-attribute | returns-nonnull-attribute | object-size | vptr)))
> fn1 ()
> {
>   char my_char[9];
>   char * ptr2;
>   char * ptr;
> ..
>
>
> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>
> Ready to be installed?


+unsigned int
+parse_no_sanitize_attribute (char *value, char **wrong_argument)
+{

functions need a comment.

Otherwise looks ok to me.

Thanks,
Richard.

> Martin
Martin Liška June 13, 2017, 1:59 p.m. UTC | #2
On 06/13/2017 03:49 PM, Richard Biener wrote:
> On Tue, Jun 13, 2017 at 1:09 PM, Martin Liška <mliska@suse.cz> wrote:
>> On 06/09/2017 03:35 PM, Richard Biener wrote:
>>> You can directly transform to no_sanitize with integer mask, not sure why
>>> you'd need an intermediate step with a string?
>>
>> Hello.
>>
>> Done in attached patch, I'm sending both incremental and final version (complete patch).
>> I also decided to support no_sanitize attribute in pretty printer:
>>
>> __attribute__((no_sanitize (address | shift | shift-base | shift-exponent | integer-divide-by-zero | undefined | unreachable | vla-bound | return | null | signed-integer-overflow | bool | enum | float-divide-by-zero | float-cast-overflow | bounds | bounds-strict | alignment | nonnull-attribute | returns-nonnull-attribute | object-size | vptr)))
>> fn1 ()
>> {
>>   char my_char[9];
>>   char * ptr2;
>>   char * ptr;
>> ..
>>
>>
>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>
>> Ready to be installed?
> 
> 
> +unsigned int
> +parse_no_sanitize_attribute (char *value, char **wrong_argument)
> +{
> 
> functions need a comment.
> 
> Otherwise looks ok to me.

Done and patch installed as r249158.

Thanks for help with that.
Martin

> 
> Thanks,
> Richard.
> 
>> Martin
diff mbox

Patch

From 5f120c1b6545ef77e87c9c9a80c5dfd6a48e9d69 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.
	(sanitize_flags_p): New 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
	* tree-cfg.c (print_no_sanitize_attr_value): New function.
	(dump_function_to_file): Use it here.

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                                    |  27 +++++--
 gcc/builtins.def                              |   3 +-
 gcc/c-family/c-attribs.c                      | 110 ++++++++++++++++++++++----
 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                             |   6 +-
 gcc/c/c-decl.c                                |   6 +-
 gcc/c/c-typeck.c                              |  16 ++--
 gcc/common.opt                                |   2 +-
 gcc/convert.c                                 |   4 +-
 gcc/cp/class.c                                |   4 +-
 gcc/cp/cp-gimplify.c                          |  19 +++--
 gcc/cp/cp-ubsan.c                             |   3 +-
 gcc/cp/decl.c                                 |   6 +-
 gcc/cp/decl2.c                                |   2 +-
 gcc/cp/init.c                                 |   4 +-
 gcc/cp/lambda.c                               |   4 +-
 gcc/cp/typeck.c                               |  16 ++--
 gcc/doc/extend.texi                           |  12 +++
 gcc/flag-types.h                              |   4 +-
 gcc/gcc.c                                     |   3 +-
 gcc/gimple-fold.c                             |   3 +-
 gcc/gimplify.c                                |   5 +-
 gcc/ipa-inline.c                              |  11 +--
 gcc/opts.c                                    |  31 +++++++-
 gcc/opts.h                                    |   2 +
 gcc/sanopt.c                                  |   4 +-
 gcc/testsuite/c-c++-common/ubsan/attrib-2.c   |  10 +++
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c |  42 +++++++---
 gcc/tree-cfg.c                                |  29 ++++++-
 gcc/tsan.c                                    |   8 +-
 gcc/ubsan.c                                   |  46 +++++------
 gcc/ubsan.h                                   |   1 -
 35 files changed, 323 insertions(+), 154 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..95bb89e197c 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
@@ -161,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 "no_sanitize" 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 ("no_sanitize", DECL_ATTRIBUTES (fn));
+      if (value)
+	result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
+    }
+
+  return result_flags;
+}
+
 #endif /* TREE_ASAN */
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 1c887db7cb9..f242137a1cb 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..2b6845f2cbd 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,22 +553,98 @@  handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
   return NULL_TREE;
 }
 
-/* Handle a "no_sanitize_address" attribute; arguments as in
+/* 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", DECL_ATTRIBUTES (node));
+  if (attr)
+    {
+      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
+      flags |= old_value;
+
+      if (flags == old_value)
+	return;
+
+      TREE_VALUE (attr) = build_int_cst (unsigned_type_node, flags);
+    }
+  else
+    DECL_ATTRIBUTES (node)
+      = tree_cons (get_identifier ("no_sanitize"),
+		   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_address_attribute (tree *node, tree name, tree, int,
-				      bool *no_add_attrs)
+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;
+    }
+
+  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.  */
+
+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);
+  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)
+{
+  *no_add_attrs = true;
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    warning (OPT_Wattributes, "%qE attribute ignored", name);
+  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.  */
 
@@ -570,13 +652,12 @@  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);
-  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;
+  else
+    add_no_sanitize_value (*node, SANITIZE_ADDRESS);
+
   return NULL_TREE;
 }
 
@@ -587,11 +668,12 @@  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);
 
   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..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
@@ -106,10 +107,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 3a0a4f51737..317d5cdd099 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
@@ -6044,9 +6045,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 ba4440653ac..4d067e96dd3 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.  */
@@ -6378,7 +6379,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);
@@ -9955,7 +9956,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;
@@ -11835,9 +11836,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)
     {
@@ -11846,10 +11846,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 0a10511d468..4f9c3dcac3e 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..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)	     \
@@ -937,8 +938,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 66f42627715..dd1051e294b 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;
@@ -462,7 +463,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 e3802f1820b..3c8f4b041d2 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.  */
 
@@ -1262,8 +1263,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 +1450,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 +1470,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 +1499,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 +1622,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..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.  */
 
@@ -32,7 +33,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 b0df3c9835c..37114761be0 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 {
@@ -9524,8 +9525,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.  */
@@ -15108,7 +15108,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 d3d90020c05..ab32b717018 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..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);
@@ -3911,8 +3912,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 34d475b98f0..05b4fbb79d3 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);
@@ -5253,10 +5254,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.  */
@@ -5264,8 +5264,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
@@ -5278,7 +5278,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);
     }
 
@@ -6823,7 +6823,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,
@@ -6967,7 +6967,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 d467a1652ee..3bad2401172 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..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.
@@ -3479,7 +3480,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 653057fd543..9af95a28704 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 8924f7eb15f..fb20d3723cc 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;
@@ -257,17 +258,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 60ebe56c8a4..b870642f0ee 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1656,6 +1656,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
@@ -1892,11 +1919,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/sanopt.c b/gcc/sanopt.c
index 70b7aeb80d3..16bdba76042 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -948,9 +948,7 @@  pass_sanopt::execute (function *fun)
 	      switch (DECL_FUNCTION_CODE (callee))
 		{
 		case BUILT_IN_UNREACHABLE:
-		  if (flag_sanitize & SANITIZE_UNREACHABLE
-		      && !lookup_attribute ("no_sanitize_undefined",
-					    DECL_ATTRIBUTES (fun->decl)))
+		  if (sanitize_flags_p (SANITIZE_UNREACHABLE))
 		    no_next = ubsan_instrument_unreachable (&gsi);
 		  break;
 		default:
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..44dc79535d2 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,40 @@ 
 // { 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"))))
+__attribute__((no_sanitize(("undefined"))))
+__attribute__((no_sanitize(("address"))))
+__attribute__((no_sanitize(("null"))))
+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-cfg.c b/gcc/tree-cfg.c
index c84e99d0945..7df80f8ee8c 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -60,6 +60,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "attribs.h"
 #include "selftest.h"
+#include "opts.h"
 
 /* This file contains functions for building the Control Flow Graph (CFG)
    for a function tree.  */
@@ -7555,6 +7556,25 @@  dump_default_def (FILE *file, tree def, int spc, dump_flags_t flags)
   fprintf (file, ";\n");
 }
 
+/* Print no_sanitize attribute to FILE for a given attribute VALUE.  */
+
+static void
+print_no_sanitize_attr_value (FILE *file, tree value)
+{
+  unsigned int flags = tree_to_uhwi (value);
+  bool first = true;
+  for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+    {
+      if ((sanitizer_opts[i].flag & flags) == sanitizer_opts[i].flag)
+	{
+	  if (!first)
+	    fprintf (file, " | ");
+	  fprintf (file, "%s", sanitizer_opts[i].name);
+	  first = false;
+	}
+    }
+}
+
 /* Dump FUNCTION_DECL FN to file FILE using FLAGS (see TDF_* in dumpfile.h)
    */
 
@@ -7582,11 +7602,16 @@  dump_function_to_file (tree fndecl, FILE *file, dump_flags_t flags)
 	  if (!first)
 	    fprintf (file, ", ");
 
-	  print_generic_expr (file, get_attribute_name (chain), dump_flags);
+	  tree name = get_attribute_name (chain);
+	  print_generic_expr (file, name, dump_flags);
 	  if (TREE_VALUE (chain) != NULL_TREE)
 	    {
 	      fprintf (file, " (");
-	      print_generic_expr (file, TREE_VALUE (chain), dump_flags);
+
+	      if (strstr (IDENTIFIER_POINTER (name), "no_sanitize"))
+		print_no_sanitize_attr_value (file, TREE_VALUE (chain));
+	      else
+		print_generic_expr (file, TREE_VALUE (chain), dump_flags);
 	      fprintf (file, ")");
 	    }
 	}
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 133409a7813..cee525ccf98 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)))
@@ -1355,13 +1355,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))
@@ -1924,16 +1925,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 =
@@ -1959,13 +1950,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 *);
@@ -1992,11 +1982,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);
@@ -2018,14 +2008,14 @@  pass_ubsan::execute (function *fun)
 		}
 	    }
 
-	  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))
 	    {
@@ -2033,14 +2023,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.1