diff mbox

Implement no_sanitize function attribute

Message ID 72103b1d-0119-f05d-043d-ca2edf242bf3@suse.cz
State New
Headers show

Commit Message

Martin Liška Dec. 22, 2016, 1:38 p.m. UTC
Hello.

As I previously agreed with Jakub, I prepared patch which adds
no_sanitize function attribute (same what clang support).

That encompasses following changes:
1) all no_sanitize_* function attributes are parsed and stored to no_sanitize_flags
in DECL_ATTRIBUTES
2) instead of flag_sanitize & X, let's call sanitize_flags_p (X), where the DECL_ATTRIBUTES
are checked within the function
3) I prepared many test-cases which test every (almost) single sub-option of UBSAN and all
functions are decorated with no_sanitize attribute disabling all sanitization except the one
tested in a particular test
4) documentation entry is introduced

Misc changes:
a) I would like to rename SANITIZE_NONDEFAULT to SANITIZE_UNDEFINED_NONDEFAULT
b) Documentation fix for -sanitize=bounds-strict is added

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

Martin

Comments

Martin Liška Jan. 6, 2017, 3:26 p.m. UTC | #1
PING^1

On 12/22/2016 02:38 PM, Martin Liška wrote:
> Hello.

> 

> As I previously agreed with Jakub, I prepared patch which adds

> no_sanitize function attribute (same what clang support).

> 

> That encompasses following changes:

> 1) all no_sanitize_* function attributes are parsed and stored to no_sanitize_flags

> in DECL_ATTRIBUTES

> 2) instead of flag_sanitize & X, let's call sanitize_flags_p (X), where the DECL_ATTRIBUTES

> are checked within the function

> 3) I prepared many test-cases which test every (almost) single sub-option of UBSAN and all

> functions are decorated with no_sanitize attribute disabling all sanitization except the one

> tested in a particular test

> 4) documentation entry is introduced

> 

> Misc changes:

> a) I would like to rename SANITIZE_NONDEFAULT to SANITIZE_UNDEFINED_NONDEFAULT

> b) Documentation fix for -sanitize=bounds-strict is added

> 

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

> 

> Martin

>
Alexander Monakov May 31, 2017, 8:31 a.m. UTC | #2
On Wed, 31 May 2017, Martin Liška wrote:
> I added to common.opt:

> Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

> No sanitize flags for a function


This needs a period at the end ("for a function.").

> FAIL: compiler driver --help=optimizers option(s): "^ +-.*[^:.]$" absent from output: "  -###                        No sanitize flags for a function"

> 

> Should I add new flag IgnoreOption for the option?


Adding the missing period should fix this failure.

Alexander
Jakub Jelinek May 31, 2017, 8:35 a.m. UTC | #3
On Wed, May 31, 2017 at 10:04:53AM +0200, Martin Liška wrote:
> diff --git a/gcc/common.opt b/gcc/common.opt

> index 13305558d2d..5e9942d5100 100644

> --- a/gcc/common.opt

> +++ b/gcc/common.opt

> @@ -222,9 +222,13 @@ bool flag_opts_finished

>  Variable

>  unsigned int flag_sanitize

>  

> +###

> +Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

> +No sanitize flags for a function


This looks weird, you are redefining the -### option which is normally
a driver option.
I would have thought you just want a Variable, like the one right below
this.  Aren't all "Variable"s per-function?

	Jakub
Martin Liška May 31, 2017, 11:24 a.m. UTC | #4
On 05/31/2017 10:35 AM, Jakub Jelinek wrote:
> On Wed, May 31, 2017 at 10:04:53AM +0200, Martin Liška wrote:

>> diff --git a/gcc/common.opt b/gcc/common.opt

>> index 13305558d2d..5e9942d5100 100644

>> --- a/gcc/common.opt

>> +++ b/gcc/common.opt

>> @@ -222,9 +222,13 @@ bool flag_opts_finished

>>  Variable

>>  unsigned int flag_sanitize

>>  

>> +###

>> +Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

>> +No sanitize flags for a function

> 

> This looks weird, you are redefining the -### option which is normally

> a driver option.


I know. I was thinking that it's also a 'dummy' value.

> I would have thought you just want a Variable, like the one right below

> this.  Aren't all "Variable"s per-function?


Unfortunately not. Well, probably adding new type 'PerFunctionVariable' would be
solution. Then optc-save-gen.awk needs to be learned how to save/restore these variables.

Is it the way we want to go?

Martin

> 

> 	Jakub

>
Martin Liška May 31, 2017, 11:26 a.m. UTC | #5
On 05/31/2017 10:31 AM, Alexander Monakov wrote:
> On Wed, 31 May 2017, Martin Liška wrote:

>> I added to common.opt:

>> Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

>> No sanitize flags for a function

> 

> This needs a period at the end ("for a function.").


Ah, I see.

> 

>> FAIL: compiler driver --help=optimizers option(s): "^ +-.*[^:.]$" absent from output: "  -###                        No sanitize flags for a function"

>>

>> Should I add new flag IgnoreOption for the option?

> 

> Adding the missing period should fix this failure.


Good, however as Jakub noticed, overwriting '-###' is probably not solution.

Martin

> 

> Alexander

>
Jakub Jelinek May 31, 2017, 11:33 a.m. UTC | #6
On Wed, May 31, 2017 at 01:24:47PM +0200, Martin Liška wrote:
> On 05/31/2017 10:35 AM, Jakub Jelinek wrote:

> > On Wed, May 31, 2017 at 10:04:53AM +0200, Martin Liška wrote:

> >> diff --git a/gcc/common.opt b/gcc/common.opt

> >> index 13305558d2d..5e9942d5100 100644

> >> --- a/gcc/common.opt

> >> +++ b/gcc/common.opt

> >> @@ -222,9 +222,13 @@ bool flag_opts_finished

> >>  Variable

> >>  unsigned int flag_sanitize

> >>  

> >> +###

> >> +Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

> >> +No sanitize flags for a function

> > 

> > This looks weird, you are redefining the -### option which is normally

> > a driver option.

> 

> I know. I was thinking that it's also a 'dummy' value.


It is not.

> > I would have thought you just want a Variable, like the one right below

> > this.  Aren't all "Variable"s per-function?

> 

> Unfortunately not. Well, probably adding new type 'PerFunctionVariable' would be

> solution. Then optc-save-gen.awk needs to be learned how to save/restore these variables.

> 

> Is it the way we want to go?


Yes.  We already have TargetVariable.  Or allow specifying
Variable PerFunction

CCing Joseph as option handling maintainer.

	Jakub
Richard Biener May 31, 2017, 11:46 a.m. UTC | #7
On Wed, May 31, 2017 at 1:33 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, May 31, 2017 at 01:24:47PM +0200, Martin Liška wrote:

>> On 05/31/2017 10:35 AM, Jakub Jelinek wrote:

>> > On Wed, May 31, 2017 at 10:04:53AM +0200, Martin Liška wrote:

>> >> diff --git a/gcc/common.opt b/gcc/common.opt

>> >> index 13305558d2d..5e9942d5100 100644

>> >> --- a/gcc/common.opt

>> >> +++ b/gcc/common.opt

>> >> @@ -222,9 +222,13 @@ bool flag_opts_finished

>> >>  Variable

>> >>  unsigned int flag_sanitize

>> >>

>> >> +###

>> >> +Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

>> >> +No sanitize flags for a function

>> >

>> > This looks weird, you are redefining the -### option which is normally

>> > a driver option.

>>

>> I know. I was thinking that it's also a 'dummy' value.

>

> It is not.

>

>> > I would have thought you just want a Variable, like the one right below

>> > this.  Aren't all "Variable"s per-function?

>>

>> Unfortunately not. Well, probably adding new type 'PerFunctionVariable' would be

>> solution. Then optc-save-gen.awk needs to be learned how to save/restore these variables.

>>

>> Is it the way we want to go?

>

> Yes.  We already have TargetVariable.  Or allow specifying

> Variable PerFunction

>

> CCing Joseph as option handling maintainer.


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.

I'll also note that eventually global variables may want to be no-sanitized
(for asan maybe).  And we don't (yet) have per-variable options.

Richard.

>         Jakub
Jakub Jelinek May 31, 2017, 11:51 a.m. UTC | #8
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.

	Jakub
Martin Liška May 31, 2017, 11:52 a.m. UTC | #9
On 05/31/2017 01:46 PM, Richard Biener wrote:
> On Wed, May 31, 2017 at 1:33 PM, Jakub Jelinek <jakub@redhat.com> wrote:

>> On Wed, May 31, 2017 at 01:24:47PM +0200, Martin Liška wrote:

>>> On 05/31/2017 10:35 AM, Jakub Jelinek wrote:

>>>> On Wed, May 31, 2017 at 10:04:53AM +0200, Martin Liška wrote:

>>>>> diff --git a/gcc/common.opt b/gcc/common.opt

>>>>> index 13305558d2d..5e9942d5100 100644

>>>>> --- a/gcc/common.opt

>>>>> +++ b/gcc/common.opt

>>>>> @@ -222,9 +222,13 @@ bool flag_opts_finished

>>>>>  Variable

>>>>>  unsigned int flag_sanitize

>>>>>

>>>>> +###

>>>>> +Common RejectNegative Joined UInteger Var(flag_no_sanitize_fn) PerFunction

>>>>> +No sanitize flags for a function

>>>>

>>>> This looks weird, you are redefining the -### option which is normally

>>>> a driver option.

>>>

>>> I know. I was thinking that it's also a 'dummy' value.

>>

>> It is not.

>>

>>>> I would have thought you just want a Variable, like the one right below

>>>> this.  Aren't all "Variable"s per-function?

>>>

>>> Unfortunately not. Well, probably adding new type 'PerFunctionVariable' would be

>>> solution. Then optc-save-gen.awk needs to be learned how to save/restore these variables.

>>>

>>> Is it the way we want to go?

>>

>> Yes.  We already have TargetVariable.  Or allow specifying

>> Variable PerFunction

>>

>> CCing Joseph as option handling maintainer.

> 

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


Well, that was what I did in my original version of the patch. I basically transformed
all no_sanitize_address, no_sanitize_undefined and others to a single DECL_ATTRIBUTE
called 'no_sanitize' where I masked in integer all these. Feedback I was given by Jakub
recommended me to not to do it.

> 

> I'll also note that eventually global variables may want to be no-sanitized

> (for asan maybe).  And we don't (yet) have per-variable options.


Good remark.

Martin

> 

> Richard.

> 

>>         Jakub
Martin Liška May 31, 2017, 11:57 a.m. UTC | #10
On 05/31/2017 01:51 PM, Jakub Jelinek 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.


But one the other hand every function decorated with such attribute will lead
to having a separate copy of struct cl_optimization, which is quite big structure.

Another question is how often such attribute is used, I guess usage is quite rare?

Martin

> 

> 	Jakub

>
Jakub Jelinek May 31, 2017, 12:01 p.m. UTC | #11
On Wed, May 31, 2017 at 01:57:48PM +0200, Martin Liška wrote:
> On 05/31/2017 01:51 PM, Jakub Jelinek 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.

> 

> But one the other hand every function decorated with such attribute will lead

> to having a separate copy of struct cl_optimization, which is quite big structure.


Separate?  I thought cl_optimization structs are shared, so if you have 2
functions that have the same no_sanitize* attributes and all other
optimization flags same as well, they should share OPTIMIZATION_NODE.

	Jakub
Richard Biener May 31, 2017, 12:04 p.m. UTC | #12
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.

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.

Richard.

>

>         Jakub
Richard Biener May 31, 2017, 12:06 p.m. UTC | #13
On Wed, May 31, 2017 at 2:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, May 31, 2017 at 01:57:48PM +0200, Martin Liška wrote:

>> On 05/31/2017 01:51 PM, Jakub Jelinek 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.

>>

>> But one the other hand every function decorated with such attribute will lead

>> to having a separate copy of struct cl_optimization, which is quite big structure.

>

> Separate?  I thought cl_optimization structs are shared, so if you have 2

> functions that have the same no_sanitize* attributes and all other

> optimization flags same as well, they should share OPTIMIZATION_NODE.


Yes.  For optimizing size we might want to tweak the machinery to use a bool
bitfield instead of {un,}signed ints for things that are really just
flags (true/false).

Richard.

>         Jakub
Martin Liška May 31, 2017, 12:25 p.m. UTC | #14
On 05/31/2017 02:06 PM, Richard Biener wrote:
> On Wed, May 31, 2017 at 2:01 PM, Jakub Jelinek <jakub@redhat.com> wrote:

>> On Wed, May 31, 2017 at 01:57:48PM +0200, Martin Liška wrote:

>>> On 05/31/2017 01:51 PM, Jakub Jelinek 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.

>>>

>>> But one the other hand every function decorated with such attribute will lead

>>> to having a separate copy of struct cl_optimization, which is quite big structure.

>>

>> Separate?  I thought cl_optimization structs are shared, so if you have 2

>> functions that have the same no_sanitize* attributes and all other

>> optimization flags same as well, they should share OPTIMIZATION_NODE.

> 

> Yes.  For optimizing size we might want to tweak the machinery to use a bool

> bitfield instead of {un,}signed ints for things that are really just

> flags (true/false).


I've got written that on my TODO list. Will work on that some time in the stage1.

Martin

> 

> Richard.

> 

>>         Jakub
Martin Liška May 31, 2017, 12:28 p.m. UTC | #15
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?

Martin

> 

> Richard.

> 

>>

>>         Jakub
Richard Biener May 31, 2017, 1:31 p.m. UTC | #16
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).

Richard.

> Martin

>

>>

>> Richard.

>>

>>>

>>>         Jakub

>
Martin Liška May 31, 2017, 2:13 p.m. UTC | #17
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?

Thanks,
Martin

> 

> Richard.

> 

>> Martin

>>

>>>

>>> Richard.

>>>

>>>>

>>>>         Jakub

>>
Bernhard Reutner-Fischer June 1, 2017, 8:03 a.m. UTC | #18
On 31 May 2017 14:25:09 CEST, "Martin Liška" <mliska@suse.cz> wrote:

>I've got written that on my TODO list. Will work on that some time in

>the stage1.


BTW.. May I ask you to put it below https://gcc.gnu.org/PR65534 (the tailcall resp. IPA-ICF thing :-)

Many TIA and cheers,
Richard Biener June 2, 2017, 10:40 a.m. UTC | #19
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).

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.

Thanks,
Richard.

> Thanks,

> Martin

>

>>

>> Richard.

>>

>>> Martin

>>>

>>>>

>>>> Richard.

>>>>

>>>>>

>>>>>         Jakub

>>>

>
Martin Liška June 8, 2017, 1:30 p.m. UTC | #20
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

>>>>

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


Jakub Jelinek June 8, 2017, 1:47 p.m. UTC | #21
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
Martin Liška June 9, 2017, 9:29 a.m. UTC | #22
On 06/08/2017 03:47 PM, Jakub Jelinek wrote:
> Hi!

> 

> I'd still prefer to handle it with the flags infrastructure instead, but if

> Richard wants to do it this way, then at least:

> 

> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is non-null,

>> +   remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES.  */

>> +

>> +bool

>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>> +{

>> +  unsigned int result_flags = flag_sanitize & flag;

> 

> This function really should be either inline, or partly inline, partly out

> of line, to handle the common case (sanitization of something not enabled)

> in the fast path.


Hello.

Having that inlined would be great, however we'll need to put it to tree.h
and thus we have to include "options.h" before tree.h in multiple source files.
Please take a look at partial patch.

> 

> And, it should have an early out,

>    if (result_flags == 0)

>      return false;


Good idea!

> 

>> +

>> +  if (fn != NULL_TREE)

>> +    {

>> +      tree value = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (fn));

> 

> The attribute, if it is internal only, should have spaces or similar

> characters in its name, like "fn spec", "omp declare target" and many

> others.


Done that.

> 

> +add_no_sanitize_value (tree node, unsigned int flags)

> +{

> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (node));

> +  if (attr)

> +    {

> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

> +      flags |= old_value;

> +    }

> +

> +  DECL_ATTRIBUTES (node)

> +    = tree_cons (get_identifier ("no_sanitize_flags"),

> +		 build_int_cst (unsigned_type_node, flags),

> +		 DECL_ATTRIBUTES (node));

> 

> If there is a previous attribute already, can't you modify it in

> place?  If not, as it could be perhaps shared? with other functions

> somehow, at least you should avoid adding a new attribute if

> (old_value | flags) == old_value.


Yep, we should definitely share, I'll add test-case for that.
I'm currently testing the incremental patch, may I install it
after regression tests?

Martin

> 

> 	Jakub

>From f61cd244ef96d1a0dc278790a279b702ca8b63e5 Mon Sep 17 00:00:00 2001

From: marxin <mliska@suse.cz>

Date: Thu, 8 Jun 2017 16:39:33 +0200
Subject: [PATCH] Put it to tree.h header file.

---
 gcc/c-family/c-attribs.c                      | 17 +++++++++++------
 gcc/c-family/c-common.h                       |  1 +
 gcc/c-family/c-pretty-print.h                 |  1 +
 gcc/c-family/c-target-def.h                   |  1 +
 gcc/c-family/stub-objc.c                      |  1 +
 gcc/c/c-lang.c                                |  1 +
 gcc/c/c-objc-common.c                         |  1 +
 gcc/cp/tree.c                                 |  1 +
 gcc/debug.c                                   |  1 +
 gcc/fold-const-call.c                         |  2 +-
 gcc/fortran/convert.c                         |  1 +
 gcc/fortran/iresolve.c                        |  1 +
 gcc/fortran/target-memory.c                   |  1 +
 gcc/fortran/trans-const.c                     |  1 +
 gcc/fortran/trans-io.c                        |  2 +-
 gcc/ggc-tests.c                               |  1 +
 gcc/godump.c                                  |  1 +
 gcc/objc/objc-encoding.c                      |  2 +-
 gcc/objc/objc-map.c                           |  1 +
 gcc/realmpfr.c                                |  1 +
 gcc/selftest-run-tests.c                      |  1 +
 gcc/stringpool.c                              |  1 +
 gcc/substring-locations.c                     |  1 +
 gcc/testsuite/gcc.dg/asan/use-after-scope-4.c |  3 +++
 gcc/tree-diagnostic.c                         |  1 +
 gcc/tree-dump.c                               |  1 +
 gcc/tree-iterator.c                           |  1 +
 gcc/tree-ssa-scopedtables.c                   |  2 +-
 gcc/tree.c                                    | 18 ------------------
 gcc/tree.h                                    | 24 ++++++++++++++++++++----
 30 files changed, 60 insertions(+), 32 deletions(-)

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index abb43d0d02c..98481b034c8 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -558,17 +558,22 @@ handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
 void
 add_no_sanitize_value (tree node, unsigned int flags)
 {
-  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (node));
+  tree attr = lookup_attribute ("sanitize no_flags", DECL_ATTRIBUTES (node));
   if (attr)
     {
       unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
       flags |= old_value;
-    }
 
-  DECL_ATTRIBUTES (node)
-    = tree_cons (get_identifier ("no_sanitize_flags"),
-		 build_int_cst (unsigned_type_node, flags),
-		 DECL_ATTRIBUTES (node));
+      if (flags == old_value)
+	return;
+
+      TREE_VALUE (attr) = build_int_cst (unsigned_type_node, flags);
+    }
+  else
+    DECL_ATTRIBUTES (node)
+      = tree_cons (get_identifier ("sanitize no_flags"),
+		   build_int_cst (unsigned_type_node, flags),
+		   DECL_ATTRIBUTES (node));
 }
 
 /* Handle a "no_sanitize" attribute; arguments as in
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 1748c1979aa..9d19de2d21e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "splay-tree.h"
 #include "cpplib.h"
 #include "alias.h"
+#include "options.h"
 #include "tree.h"
 #include "fold-const.h"
 
diff --git a/gcc/c-family/c-pretty-print.h b/gcc/c-family/c-pretty-print.h
index 86a4ae32639..38419d69e89 100644
--- a/gcc/c-family/c-pretty-print.h
+++ b/gcc/c-family/c-pretty-print.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_C_PRETTY_PRINTER
 #define GCC_C_PRETTY_PRINTER
 
+#include "options.h"
 #include "tree.h"
 #include "c-family/c-common.h"
 #include "pretty-print.h"
diff --git a/gcc/c-family/c-target-def.h b/gcc/c-family/c-target-def.h
index 781afbc78fe..cf46961039a 100644
--- a/gcc/c-family/c-target-def.h
+++ b/gcc/c-family/c-target-def.h
@@ -16,6 +16,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include "c-family/c-target-hooks-def.h"
+#include "options.h"
 #include "tree.h"
 #include "c-family/c-common.h"
 #include "hooks.h"
diff --git a/gcc/c-family/stub-objc.c b/gcc/c-family/stub-objc.c
index 33dc2a1abd6..58780d17a70 100644
--- a/gcc/c-family/stub-objc.c
+++ b/gcc/c-family/stub-objc.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "c-objc.h"
 
diff --git a/gcc/c/c-lang.c b/gcc/c/c-lang.c
index 510b7e7de47..595433935b3 100644
--- a/gcc/c/c-lang.c
+++ b/gcc/c/c-lang.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "c-tree.h"
 #include "langhooks.h"
 #include "langhooks-def.h"
diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c
index 05212b2cb8e..0ca096acba7 100644
--- a/gcc/c/c-objc-common.c
+++ b/gcc/c/c-objc-common.c
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "c-tree.h"
 #include "intl.h"
 #include "c-family/c-pretty-print.h"
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index bb17278c611..80067dc5211 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "cp-tree.h"
 #include "gimple-expr.h"
diff --git a/gcc/debug.c b/gcc/debug.c
index 860f1e312b9..3a5f447fe86 100644
--- a/gcc/debug.c
+++ b/gcc/debug.c
@@ -18,6 +18,7 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "debug.h"
 
diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c
index 381cb7fd290..a4103f41754 100644
--- a/gcc/fold-const-call.c
+++ b/gcc/fold-const-call.c
@@ -21,9 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "realmpfr.h"
+#include "options.h"
 #include "tree.h"
 #include "stor-layout.h"
-#include "options.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
 #include "case-cfn-macros.h"
diff --git a/gcc/fortran/convert.c b/gcc/fortran/convert.c
index 35203235e8f..e3441b02a95 100644
--- a/gcc/fortran/convert.c
+++ b/gcc/fortran/convert.c
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "fold-const.h"
 #include "convert.h"
diff --git a/gcc/fortran/iresolve.c b/gcc/fortran/iresolve.c
index b784ac339e9..0565f2fd9ed 100644
--- a/gcc/fortran/iresolve.c
+++ b/gcc/fortran/iresolve.c
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "gfortran.h"
 #include "stringpool.h"
diff --git a/gcc/fortran/target-memory.c b/gcc/fortran/target-memory.c
index d239cf114e1..fe5c2f40ccc 100644
--- a/gcc/fortran/target-memory.c
+++ b/gcc/fortran/target-memory.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "gfortran.h"
 #include "trans.h"
diff --git a/gcc/fortran/trans-const.c b/gcc/fortran/trans-const.c
index 128d47d0fa3..ebd10fd24ca 100644
--- a/gcc/fortran/trans-const.c
+++ b/gcc/fortran/trans-const.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "gfortran.h"
 #include "trans.h"
diff --git a/gcc/fortran/trans-io.c b/gcc/fortran/trans-io.c
index c3c56f29623..64d068833ed 100644
--- a/gcc/fortran/trans-io.c
+++ b/gcc/fortran/trans-io.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "gfortran.h"
 #include "trans.h"
@@ -32,7 +33,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "trans-array.h"
 #include "trans-types.h"
 #include "trans-const.h"
-#include "options.h"
 
 /* Members of the ioparm structure.  */
 
diff --git a/gcc/ggc-tests.c b/gcc/ggc-tests.c
index cbb941d9573..c2dbfb36707 100644
--- a/gcc/ggc-tests.c
+++ b/gcc/ggc-tests.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tree-core.h"
+#include "options.h"
 #include "tree.h"
 #include "ggc-internal.h" /* (for ggc_force_collect).  */
 #include "selftest.h"
diff --git a/gcc/godump.c b/gcc/godump.c
index 4884deead80..c326c7bb6a2 100644
--- a/gcc/godump.c
+++ b/gcc/godump.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "wide-int-print.h"
 #include "diagnostic-core.h"
diff --git a/gcc/objc/objc-encoding.c b/gcc/objc/objc-encoding.c
index 2a2dfa51ba5..1ae6f8f992c 100644
--- a/gcc/objc/objc-encoding.c
+++ b/gcc/objc/objc-encoding.c
@@ -20,8 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tree.h"
 #include "options.h"
+#include "tree.h"
 #include "stringpool.h"
 #include "stor-layout.h"
 
diff --git a/gcc/objc/objc-map.c b/gcc/objc/objc-map.c
index 58c902bbb17..0be5dd4a645 100644
--- a/gcc/objc/objc-map.c
+++ b/gcc/objc/objc-map.c
@@ -20,6 +20,7 @@ Boston, MA 02110-1301, USA.  */
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "objc-map.h"
 
diff --git a/gcc/realmpfr.c b/gcc/realmpfr.c
index 69439a4173d..1015958eb44 100644
--- a/gcc/realmpfr.c
+++ b/gcc/realmpfr.c
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "realmpfr.h"
 #include "stor-layout.h"
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index f62bc72b072..d182d1bf469 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "selftest.h"
+#include "options.h"
 #include "tree.h"
 #include "target.h"
 #include "langhooks.h"
diff --git a/gcc/stringpool.c b/gcc/stringpool.c
index 689327548e3..e5ec6bc97a5 100644
--- a/gcc/stringpool.c
+++ b/gcc/stringpool.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 
 /* The "" allocated string.  */
diff --git a/gcc/substring-locations.c b/gcc/substring-locations.c
index 433023d9845..752a981cb17 100644
--- a/gcc/substring-locations.c
+++ b/gcc/substring-locations.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "diagnostic.h"
 #include "cpplib.h"
+#include "options.h"
 #include "tree.h"
 #include "langhooks.h"
 #include "substring-locations.h"
diff --git a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
index 781d70d6038..44dc79535d2 100644
--- a/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
+++ b/gcc/testsuite/gcc.dg/asan/use-after-scope-4.c
@@ -16,6 +16,9 @@ NAME (void) \
 
 void
 __attribute__((no_sanitize(("address"))))
+__attribute__((no_sanitize(("undefined"))))
+__attribute__((no_sanitize(("address"))))
+__attribute__((no_sanitize(("null"))))
 FN (fn1)
 
 void
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 52b7e7f0bb4..27880ae3e92 100644
--- a/gcc/tree-diagnostic.c
+++ b/gcc/tree-diagnostic.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "diagnostic.h"
 #include "tree-pretty-print.h"
diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c
index 347b33ab505..487eaf5e832 100644
--- a/gcc/tree-dump.c
+++ b/gcc/tree-dump.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "tree-pretty-print.h"
 #include "tree-dump.h"
diff --git a/gcc/tree-iterator.c b/gcc/tree-iterator.c
index c485413b5e5..b04e0a4c577 100644
--- a/gcc/tree-iterator.c
+++ b/gcc/tree-iterator.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "options.h"
 #include "tree.h"
 #include "tree-iterator.h"
 
diff --git a/gcc/tree-ssa-scopedtables.c b/gcc/tree-ssa-scopedtables.c
index 814f1c7b05b..3656268aa47 100644
--- a/gcc/tree-ssa-scopedtables.c
+++ b/gcc/tree-ssa-scopedtables.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "function.h"
 #include "basic-block.h"
+#include "options.h"
 #include "tree.h"
 #include "gimple.h"
 #include "tree-pass.h"
@@ -33,7 +34,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-eh.h"
 #include "internal-fn.h"
 #include "tree-dfa.h"
-#include "options.h"
 #include "params.h"
 
 static bool hashable_expr_equal_p (const struct hashable_expr *,
diff --git a/gcc/tree.c b/gcc/tree.c
index 8979819adf7..a58f9aaa69e 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14442,24 +14442,6 @@ nonnull_arg_p (const_tree arg)
   return false;
 }
 
-/* Return true when flag_sanitize & FLAG is non-zero.  If FN is non-null,
-   remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES.  */
-
-bool
-sanitize_flags_p (unsigned int flag, const_tree fn)
-{
-  unsigned int result_flags = flag_sanitize & flag;
-
-  if (fn != NULL_TREE)
-    {
-      tree value = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (fn));
-      if (value)
-	result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
-    }
-
-  return result_flags;
-}
-
 /* Combine LOC and BLOCK to a combined adhoc loc, retaining any range
    information.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 22b9ec3f0e7..061ea0dd97c 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4248,10 +4248,6 @@ extern tree merge_dllimport_decl_attributes (tree, tree);
 /* Handle a "dllimport" or "dllexport" attribute.  */
 extern tree handle_dll_attribute (tree *, tree, tree, int, bool *);
 
-
-extern bool sanitize_flags_p (unsigned int flag,
-			      const_tree fn = current_function_decl);
-
 /* Returns true iff CAND and BASE have equivalent language-specific
    qualifiers.  */
 
@@ -5513,4 +5509,24 @@ desired_pro_or_demotion_p (const_tree to_type, const_tree from_type)
   return to_type_precision <= TYPE_PRECISION (from_type);
 }
 
+/* Return true when flag_sanitize & FLAG is non-zero.  If FN is non-null,
+   remove all flags mentioned in "sanitize no_flags" of DECL_ATTRIBUTES.  */
+
+static inline bool
+sanitize_flags_p (unsigned int flag, const_tree fn = current_function_decl)
+{
+  unsigned int result_flags = flag_sanitize & flag;
+  if (result_flags == 0)
+    return false;
+
+  if (fn != NULL_TREE)
+    {
+      tree value = lookup_attribute ("sanitize no_flags", DECL_ATTRIBUTES (fn));
+      if (value)
+	result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
+    }
+
+  return result_flags;
+}
+
 #endif  /* GCC_TREE_H  */
-- 
2.13.0


Jakub Jelinek June 9, 2017, 9:42 a.m. UTC | #23
On Fri, Jun 09, 2017 at 11:29:06AM +0200, Martin Liška wrote:
> Having that inlined would be great, however we'll need to put it to tree.h

> and thus we have to include "options.h" before tree.h in multiple source files.


Doesn't that mean that tree.h is not the right header to put this into?
It has nothing to do with the stuff normally in tree.h.
We don't have a common sanitizer header, just asan.h (which already has
similar partially inline checks), ubsan.h and tsan.h, but perhaps putting it
into one of those would be certainly better than tree.h.

> > If there is a previous attribute already, can't you modify it in

> > place?  If not, as it could be perhaps shared? with other functions

> > somehow, at least you should avoid adding a new attribute if

> > (old_value | flags) == old_value.

> 

> Yep, we should definitely share, I'll add test-case for that.


I was worried about stuff like templates, where a template has one
no_sanitize attribute and the implied "no sanitize flags", and then
you instantiate it or specialize it with extra attributes.
Are you sure it can't be ever shared between multiple functions?

	Jakub
Richard Biener June 9, 2017, 10:12 a.m. UTC | #24
On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:
> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>

>> Hi!

>>

>> I'd still prefer to handle it with the flags infrastructure instead, but

>> if

>> Richard wants to do it this way, then at least:

>>

>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>

>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>> non-null,

>>> +   remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES.

>>> */

>>> +

>>> +bool

>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>> +{

>>> +  unsigned int result_flags = flag_sanitize & flag;

>>

>>

>> This function really should be either inline, or partly inline, partly out

>> of line, to handle the common case (sanitization of something not enabled)

>> in the fast path.

>

>

> Hello.

>

> Having that inlined would be great, however we'll need to put it to tree.h

> and thus we have to include "options.h" before tree.h in multiple source

> files.

> Please take a look at partial patch.

>

>>

>> And, it should have an early out,

>>    if (result_flags == 0)

>>      return false;

>

>

> Good idea!

>

>>

>>> +

>>> +  if (fn != NULL_TREE)

>>> +    {

>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>> DECL_ATTRIBUTES (fn));

>>

>>

>> The attribute, if it is internal only, should have spaces or similar

>> characters in its name, like "fn spec", "omp declare target" and many

>> others.

>

>

> Done that.


Whoo, wait -- this is for internal use only?  Can you step back and explain
why we need this?  We do, after all, have -fsanitize options already.

Richard.

>>

>> +add_no_sanitize_value (tree node, unsigned int flags)

>> +{

>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>> (node));

>> +  if (attr)

>> +    {

>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>> +      flags |= old_value;

>> +    }

>> +

>> +  DECL_ATTRIBUTES (node)

>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>> +                build_int_cst (unsigned_type_node, flags),

>> +                DECL_ATTRIBUTES (node));

>>

>> If there is a previous attribute already, can't you modify it in

>> place?  If not, as it could be perhaps shared? with other functions

>> somehow, at least you should avoid adding a new attribute if

>> (old_value | flags) == old_value.

>

>

> Yep, we should definitely share, I'll add test-case for that.

> I'm currently testing the incremental patch, may I install it

> after regression tests?

>

> Martin

>

>>

>>         Jakub

>>

>
Martin Liška June 9, 2017, 10:17 a.m. UTC | #25
On 06/09/2017 12:12 PM, Richard Biener wrote:
> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>

>>> Hi!

>>>

>>> I'd still prefer to handle it with the flags infrastructure instead, but

>>> if

>>> Richard wants to do it this way, then at least:

>>>

>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>

>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>> non-null,

>>>> +   remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES.

>>>> */

>>>> +

>>>> +bool

>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>> +{

>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>

>>>

>>> This function really should be either inline, or partly inline, partly out

>>> of line, to handle the common case (sanitization of something not enabled)

>>> in the fast path.

>>

>>

>> Hello.

>>

>> Having that inlined would be great, however we'll need to put it to tree.h

>> and thus we have to include "options.h" before tree.h in multiple source

>> files.

>> Please take a look at partial patch.

>>

>>>

>>> And, it should have an early out,

>>>     if (result_flags == 0)

>>>       return false;

>>

>>

>> Good idea!

>>

>>>

>>>> +

>>>> +  if (fn != NULL_TREE)

>>>> +    {

>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>> DECL_ATTRIBUTES (fn));

>>>

>>>

>>> The attribute, if it is internal only, should have spaces or similar

>>> characters in its name, like "fn spec", "omp declare target" and many

>>> others.

>>

>>

>> Done that.

> 

> Whoo, wait -- this is for internal use only?  Can you step back and explain

> why we need this?  We do, after all, have -fsanitize options already.


Can be seen here:

__attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize ("address"), no_sanitize ("undefined"), no_sanitize ("address"), sanitize no_flags (16777195)))
fn1 ()
{
...
}

where no_sanitize_thread and no_sanitize are normal attributes used by users.
But we want to aggregate all there attributes and build one integer mask that
will drive sanitize_flags_p. And that's why we introduced 'sanitize no_flags'
attribute, so that we don't have to iterate all attrs every time in anitize_flags_p.

Hope it explains situation?

Martin

> 

> Richard.

> 

>>>

>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>> +{

>>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>>> (node));

>>> +  if (attr)

>>> +    {

>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>> +      flags |= old_value;

>>> +    }

>>> +

>>> +  DECL_ATTRIBUTES (node)

>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>> +                build_int_cst (unsigned_type_node, flags),

>>> +                DECL_ATTRIBUTES (node));

>>>

>>> If there is a previous attribute already, can't you modify it in

>>> place?  If not, as it could be perhaps shared? with other functions

>>> somehow, at least you should avoid adding a new attribute if

>>> (old_value | flags) == old_value.

>>

>>

>> Yep, we should definitely share, I'll add test-case for that.

>> I'm currently testing the incremental patch, may I install it

>> after regression tests?

>>

>> Martin

>>

>>>

>>>          Jakub

>>>

>>
Richard Biener June 9, 2017, 10:39 a.m. UTC | #26
On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>

>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>

>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>

>>>>

>>>> Hi!

>>>>

>>>> I'd still prefer to handle it with the flags infrastructure instead, but

>>>> if

>>>> Richard wants to do it this way, then at least:

>>>>

>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>

>>>>>

>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>> non-null,

>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>> DECL_ATTRIBUTES.

>>>>> */

>>>>> +

>>>>> +bool

>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>> +{

>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>

>>>>

>>>>

>>>> This function really should be either inline, or partly inline, partly

>>>> out

>>>> of line, to handle the common case (sanitization of something not

>>>> enabled)

>>>> in the fast path.

>>>

>>>

>>>

>>> Hello.

>>>

>>> Having that inlined would be great, however we'll need to put it to

>>> tree.h

>>> and thus we have to include "options.h" before tree.h in multiple source

>>> files.

>>> Please take a look at partial patch.

>>>

>>>>

>>>> And, it should have an early out,

>>>>     if (result_flags == 0)

>>>>       return false;

>>>

>>>

>>>

>>> Good idea!

>>>

>>>>

>>>>> +

>>>>> +  if (fn != NULL_TREE)

>>>>> +    {

>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>> DECL_ATTRIBUTES (fn));

>>>>

>>>>

>>>>

>>>> The attribute, if it is internal only, should have spaces or similar

>>>> characters in its name, like "fn spec", "omp declare target" and many

>>>> others.

>>>

>>>

>>>

>>> Done that.

>>

>>

>> Whoo, wait -- this is for internal use only?  Can you step back and

>> explain

>> why we need this?  We do, after all, have -fsanitize options already.

>

>

> Can be seen here:

>

> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

> ("address"), no_sanitize ("undefined"), no_sanitize ("address"), sanitize

> no_flags (16777195)))

> fn1 ()

> {

> ...

> }

>

> where no_sanitize_thread and no_sanitize are normal attributes used by

> users.

> But we want to aggregate all there attributes and build one integer mask

> that

> will drive sanitize_flags_p. And that's why we introduced 'sanitize

> no_flags'

> attribute, so that we don't have to iterate all attrs every time in

> anitize_flags_p.

>

> Hope it explains situation?


Hum, ok ... but then you can "simply" have the no_sanitize attribute
internal rep use a INTEGER_CST instread of a STRING_CST value,
updating that in handle_attribute instead of adding new attributes?

There's nothing that forces internal representation to match what the
user wrote.

[historically we've used random flags in decls/types for this kind of caching
but I can see we don't want to waste as many bits there]

Richard.

> Martin

>

>

>>

>> Richard.

>>

>>>>

>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>> +{

>>>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>>>> (node));

>>>> +  if (attr)

>>>> +    {

>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>> +      flags |= old_value;

>>>> +    }

>>>> +

>>>> +  DECL_ATTRIBUTES (node)

>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>> +                build_int_cst (unsigned_type_node, flags),

>>>> +                DECL_ATTRIBUTES (node));

>>>>

>>>> If there is a previous attribute already, can't you modify it in

>>>> place?  If not, as it could be perhaps shared? with other functions

>>>> somehow, at least you should avoid adding a new attribute if

>>>> (old_value | flags) == old_value.

>>>

>>>

>>>

>>> Yep, we should definitely share, I'll add test-case for that.

>>> I'm currently testing the incremental patch, may I install it

>>> after regression tests?

>>>

>>> Martin

>>>

>>>>

>>>>          Jakub

>>>>

>>>

>
Martin Liška June 9, 2017, 10:49 a.m. UTC | #27
On 06/09/2017 12:39 PM, Richard Biener wrote:
> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>

>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>>

>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>

>>>>>

>>>>> Hi!

>>>>>

>>>>> I'd still prefer to handle it with the flags infrastructure instead, but

>>>>> if

>>>>> Richard wants to do it this way, then at least:

>>>>>

>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>

>>>>>>

>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>> non-null,

>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>> DECL_ATTRIBUTES.

>>>>>> */

>>>>>> +

>>>>>> +bool

>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>> +{

>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>

>>>>>

>>>>>

>>>>> This function really should be either inline, or partly inline, partly

>>>>> out

>>>>> of line, to handle the common case (sanitization of something not

>>>>> enabled)

>>>>> in the fast path.

>>>>

>>>>

>>>>

>>>> Hello.

>>>>

>>>> Having that inlined would be great, however we'll need to put it to

>>>> tree.h

>>>> and thus we have to include "options.h" before tree.h in multiple source

>>>> files.

>>>> Please take a look at partial patch.

>>>>

>>>>>

>>>>> And, it should have an early out,

>>>>>      if (result_flags == 0)

>>>>>        return false;

>>>>

>>>>

>>>>

>>>> Good idea!

>>>>

>>>>>

>>>>>> +

>>>>>> +  if (fn != NULL_TREE)

>>>>>> +    {

>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>> DECL_ATTRIBUTES (fn));

>>>>>

>>>>>

>>>>>

>>>>> The attribute, if it is internal only, should have spaces or similar

>>>>> characters in its name, like "fn spec", "omp declare target" and many

>>>>> others.

>>>>

>>>>

>>>>

>>>> Done that.

>>>

>>>

>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>> explain

>>> why we need this?  We do, after all, have -fsanitize options already.

>>

>>

>> Can be seen here:

>>

>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"), sanitize

>> no_flags (16777195)))

>> fn1 ()

>> {

>> ...

>> }

>>

>> where no_sanitize_thread and no_sanitize are normal attributes used by

>> users.

>> But we want to aggregate all there attributes and build one integer mask

>> that

>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>> no_flags'

>> attribute, so that we don't have to iterate all attrs every time in

>> anitize_flags_p.

>>

>> Hope it explains situation?

> 

> Hum, ok ... but then you can "simply" have the no_sanitize attribute

> internal rep use a INTEGER_CST instread of a STRING_CST value,

> updating that in handle_attribute instead of adding new attributes?

> 

> There's nothing that forces internal representation to match what the

> user wrote.


I see, but consider following test-case:

void
__attribute__((no_sanitize_thread))
__attribute__((no_sanitize(("address"))))
__attribute__((no_sanitize(("undefined"))))
__attribute__((no_sanitize(("address"))))
__attribute__((no_sanitize(("null"))))
foo (void) {}

handle_no_sanitize_thread_attribute function is called for no_sanitize_thread and
changing first no_sanitize attribute to integer is wrongly doable. Apart from that,
we want to merge all flags to a single attribute. Thus said, having an unique name
will enable this.

Martin

> 

> [historically we've used random flags in decls/types for this kind of caching

> but I can see we don't want to waste as many bits there]

> 

> Richard.

> 

>> Martin

>>

>>

>>>

>>> Richard.

>>>

>>>>>

>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>> +{

>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>>>>> (node));

>>>>> +  if (attr)

>>>>> +    {

>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>> +      flags |= old_value;

>>>>> +    }

>>>>> +

>>>>> +  DECL_ATTRIBUTES (node)

>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>> +                DECL_ATTRIBUTES (node));

>>>>>

>>>>> If there is a previous attribute already, can't you modify it in

>>>>> place?  If not, as it could be perhaps shared? with other functions

>>>>> somehow, at least you should avoid adding a new attribute if

>>>>> (old_value | flags) == old_value.

>>>>

>>>>

>>>>

>>>> Yep, we should definitely share, I'll add test-case for that.

>>>> I'm currently testing the incremental patch, may I install it

>>>> after regression tests?

>>>>

>>>> Martin

>>>>

>>>>>

>>>>>           Jakub

>>>>>

>>>>

>>
Richard Biener June 9, 2017, 11:05 a.m. UTC | #28
On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 12:39 PM, Richard Biener wrote:

>>

>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>>>

>>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>>

>>>>

>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>>>

>>>>>

>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>>

>>>>>>

>>>>>>

>>>>>> Hi!

>>>>>>

>>>>>> I'd still prefer to handle it with the flags infrastructure instead,

>>>>>> but

>>>>>> if

>>>>>> Richard wants to do it this way, then at least:

>>>>>>

>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>>> non-null,

>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>>> DECL_ATTRIBUTES.

>>>>>>> */

>>>>>>> +

>>>>>>> +bool

>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>>> +{

>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> This function really should be either inline, or partly inline, partly

>>>>>> out

>>>>>> of line, to handle the common case (sanitization of something not

>>>>>> enabled)

>>>>>> in the fast path.

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> Hello.

>>>>>

>>>>> Having that inlined would be great, however we'll need to put it to

>>>>> tree.h

>>>>> and thus we have to include "options.h" before tree.h in multiple

>>>>> source

>>>>> files.

>>>>> Please take a look at partial patch.

>>>>>

>>>>>>

>>>>>> And, it should have an early out,

>>>>>>      if (result_flags == 0)

>>>>>>        return false;

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> Good idea!

>>>>>

>>>>>>

>>>>>>> +

>>>>>>> +  if (fn != NULL_TREE)

>>>>>>> +    {

>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>>> DECL_ATTRIBUTES (fn));

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> The attribute, if it is internal only, should have spaces or similar

>>>>>> characters in its name, like "fn spec", "omp declare target" and many

>>>>>> others.

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> Done that.

>>>>

>>>>

>>>>

>>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>>> explain

>>>> why we need this?  We do, after all, have -fsanitize options already.

>>>

>>>

>>>

>>> Can be seen here:

>>>

>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"), sanitize

>>> no_flags (16777195)))

>>> fn1 ()

>>> {

>>> ...

>>> }

>>>

>>> where no_sanitize_thread and no_sanitize are normal attributes used by

>>> users.

>>> But we want to aggregate all there attributes and build one integer mask

>>> that

>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>>> no_flags'

>>> attribute, so that we don't have to iterate all attrs every time in

>>> anitize_flags_p.

>>>

>>> Hope it explains situation?

>>

>>

>> Hum, ok ... but then you can "simply" have the no_sanitize attribute

>> internal rep use a INTEGER_CST instread of a STRING_CST value,

>> updating that in handle_attribute instead of adding new attributes?

>>

>> There's nothing that forces internal representation to match what the

>> user wrote.

>

>

> I see, but consider following test-case:

>

> void

> __attribute__((no_sanitize_thread))

> __attribute__((no_sanitize(("address"))))

> __attribute__((no_sanitize(("undefined"))))

> __attribute__((no_sanitize(("address"))))

> __attribute__((no_sanitize(("null"))))

> foo (void) {}

>

> handle_no_sanitize_thread_attribute function is called for

> no_sanitize_thread and

> changing first no_sanitize attribute to integer is wrongly doable.


Just change no_sanitize_thread to add no_sanitize instead?

> Apart

> from that,

> we want to merge all flags to a single attribute. Thus said, having an

> unique name

> will enable this.


no_sanitize looks like the unique name to me -- I suppose no_sanitize("thread")
works?

Richard.

> Martin

>

>

>>

>> [historically we've used random flags in decls/types for this kind of

>> caching

>> but I can see we don't want to waste as many bits there]

>>

>> Richard.

>>

>>> Martin

>>>

>>>

>>>>

>>>> Richard.

>>>>

>>>>>>

>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>>> +{

>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>>>>>> (node));

>>>>>> +  if (attr)

>>>>>> +    {

>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>>> +      flags |= old_value;

>>>>>> +    }

>>>>>> +

>>>>>> +  DECL_ATTRIBUTES (node)

>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>>> +                DECL_ATTRIBUTES (node));

>>>>>>

>>>>>> If there is a previous attribute already, can't you modify it in

>>>>>> place?  If not, as it could be perhaps shared? with other functions

>>>>>> somehow, at least you should avoid adding a new attribute if

>>>>>> (old_value | flags) == old_value.

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> Yep, we should definitely share, I'll add test-case for that.

>>>>> I'm currently testing the incremental patch, may I install it

>>>>> after regression tests?

>>>>>

>>>>> Martin

>>>>>

>>>>>>

>>>>>>           Jakub

>>>>>>

>>>>>

>>>

>
Martin Liška June 9, 2017, 12:08 p.m. UTC | #29
On 06/09/2017 01:05 PM, Richard Biener wrote:
> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:

>> On 06/09/2017 12:39 PM, Richard Biener wrote:

>>>

>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>

>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>>>

>>>>>

>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>>>>

>>>>>>

>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Hi!

>>>>>>>

>>>>>>> I'd still prefer to handle it with the flags infrastructure instead,

>>>>>>> but

>>>>>>> if

>>>>>>> Richard wants to do it this way, then at least:

>>>>>>>

>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>>>> non-null,

>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>>>> DECL_ATTRIBUTES.

>>>>>>>> */

>>>>>>>> +

>>>>>>>> +bool

>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>>>> +{

>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> This function really should be either inline, or partly inline, partly

>>>>>>> out

>>>>>>> of line, to handle the common case (sanitization of something not

>>>>>>> enabled)

>>>>>>> in the fast path.

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Hello.

>>>>>>

>>>>>> Having that inlined would be great, however we'll need to put it to

>>>>>> tree.h

>>>>>> and thus we have to include "options.h" before tree.h in multiple

>>>>>> source

>>>>>> files.

>>>>>> Please take a look at partial patch.

>>>>>>

>>>>>>>

>>>>>>> And, it should have an early out,

>>>>>>>       if (result_flags == 0)

>>>>>>>         return false;

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Good idea!

>>>>>>

>>>>>>>

>>>>>>>> +

>>>>>>>> +  if (fn != NULL_TREE)

>>>>>>>> +    {

>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>>>> DECL_ATTRIBUTES (fn));

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> The attribute, if it is internal only, should have spaces or similar

>>>>>>> characters in its name, like "fn spec", "omp declare target" and many

>>>>>>> others.

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Done that.

>>>>>

>>>>>

>>>>>

>>>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>>>> explain

>>>>> why we need this?  We do, after all, have -fsanitize options already.

>>>>

>>>>

>>>>

>>>> Can be seen here:

>>>>

>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"), sanitize

>>>> no_flags (16777195)))

>>>> fn1 ()

>>>> {

>>>> ...

>>>> }

>>>>

>>>> where no_sanitize_thread and no_sanitize are normal attributes used by

>>>> users.

>>>> But we want to aggregate all there attributes and build one integer mask

>>>> that

>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>>>> no_flags'

>>>> attribute, so that we don't have to iterate all attrs every time in

>>>> anitize_flags_p.

>>>>

>>>> Hope it explains situation?

>>>

>>>

>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute

>>> internal rep use a INTEGER_CST instread of a STRING_CST value,

>>> updating that in handle_attribute instead of adding new attributes?

>>>

>>> There's nothing that forces internal representation to match what the

>>> user wrote.

>>

>>

>> I see, but consider following test-case:

>>

>> void

>> __attribute__((no_sanitize_thread))

>> __attribute__((no_sanitize(("address"))))

>> __attribute__((no_sanitize(("undefined"))))

>> __attribute__((no_sanitize(("address"))))

>> __attribute__((no_sanitize(("null"))))

>> foo (void) {}

>>

>> handle_no_sanitize_thread_attribute function is called for

>> no_sanitize_thread and

>> changing first no_sanitize attribute to integer is wrongly doable.

> 

> Just change no_sanitize_thread to add no_sanitize instead?


Unfortunately, we currently support no_sanitize_{address,thread,undefined}
and no_address_safety_analysis. Thus we can't drop these.


> 

>> Apart

>> from that,

>> we want to merge all flags to a single attribute. Thus said, having an

>> unique name

>> will enable this.

> 

> no_sanitize looks like the unique name to me -- I suppose no_sanitize("thread")

> works?


Yep, it's unique but as I mentioned we've got const struct attribute_spec c_common_attribute_table[]
handlers that are executed on these no sanitize attributes. And as I want to store int mask to
an attribute, I prefer to come up with a new attribute "sanitize no_flags" and I can set
   *no_add_attrs = true; in order to remove the original attributes:

void
__attribute__((no_sanitize(("address"))))
__attribute__((no_sanitize(("undefined"))))
__attribute__((no_sanitize(("address"))))
__attribute__((no_sanitize(("null"))))
fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0]; __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }

will become:

__attribute__((sanitize no_flags (16777187)))
fn1 ()
{
...
}

I'm going to test that.

Martin

> 

> Richard.

> 

>> Martin

>>

>>

>>>

>>> [historically we've used random flags in decls/types for this kind of

>>> caching

>>> but I can see we don't want to waste as many bits there]

>>>

>>> Richard.

>>>

>>>> Martin

>>>>

>>>>

>>>>>

>>>>> Richard.

>>>>>

>>>>>>>

>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>>>> +{

>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES

>>>>>>> (node));

>>>>>>> +  if (attr)

>>>>>>> +    {

>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>>>> +      flags |= old_value;

>>>>>>> +    }

>>>>>>> +

>>>>>>> +  DECL_ATTRIBUTES (node)

>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>>>> +                DECL_ATTRIBUTES (node));

>>>>>>>

>>>>>>> If there is a previous attribute already, can't you modify it in

>>>>>>> place?  If not, as it could be perhaps shared? with other functions

>>>>>>> somehow, at least you should avoid adding a new attribute if

>>>>>>> (old_value | flags) == old_value.

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Yep, we should definitely share, I'll add test-case for that.

>>>>>> I'm currently testing the incremental patch, may I install it

>>>>>> after regression tests?

>>>>>>

>>>>>> Martin

>>>>>>

>>>>>>>

>>>>>>>            Jakub

>>>>>>>

>>>>>>

>>>>

>>
Martin Liška June 9, 2017, 12:12 p.m. UTC | #30
On 06/09/2017 11:42 AM, Jakub Jelinek wrote:
> On Fri, Jun 09, 2017 at 11:29:06AM +0200, Martin Liška wrote:

>> Having that inlined would be great, however we'll need to put it to tree.h

>> and thus we have to include "options.h" before tree.h in multiple source files.

> 

> Doesn't that mean that tree.h is not the right header to put this into?

> It has nothing to do with the stuff normally in tree.h.

> We don't have a common sanitizer header, just asan.h (which already has

> similar partially inline checks), ubsan.h and tsan.h, but perhaps putting it

> into one of those would be certainly better than tree.h.


I'll do that, currently testing patch.

> 

>>> If there is a previous attribute already, can't you modify it in

>>> place?  If not, as it could be perhaps shared? with other functions

>>> somehow, at least you should avoid adding a new attribute if

>>> (old_value | flags) == old_value.

>>

>> Yep, we should definitely share, I'll add test-case for that.

> 

> I was worried about stuff like templates, where a template has one

> no_sanitize attribute and the implied "no sanitize flags", and then

> you instantiate it or specialize it with extra attributes.

> Are you sure it can't be ever shared between multiple functions?


For following example:

template <typename T>
void
__attribute__((no_sanitize(("thread"))))
baz(T *a)
{
}
   
int
main(int argc, char **argv)
{
   baz<int> (0);
   baz<char> (0);
   baz<char *> (0);
}

all 3 fn instances do share the same attribute list.

Martin

> 

> 	Jakub

>
Richard Biener June 9, 2017, 12:27 p.m. UTC | #31
On Fri, Jun 9, 2017 at 2:08 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 01:05 PM, Richard Biener wrote:

>>

>> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:

>>>

>>> On 06/09/2017 12:39 PM, Richard Biener wrote:

>>>>

>>>>

>>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>>

>>>>>

>>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>>>>

>>>>>>

>>>>>>

>>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Hi!

>>>>>>>>

>>>>>>>> I'd still prefer to handle it with the flags infrastructure instead,

>>>>>>>> but

>>>>>>>> if

>>>>>>>> Richard wants to do it this way, then at least:

>>>>>>>>

>>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>>>>> non-null,

>>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>>>>> DECL_ATTRIBUTES.

>>>>>>>>> */

>>>>>>>>> +

>>>>>>>>> +bool

>>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>>>>> +{

>>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> This function really should be either inline, or partly inline,

>>>>>>>> partly

>>>>>>>> out

>>>>>>>> of line, to handle the common case (sanitization of something not

>>>>>>>> enabled)

>>>>>>>> in the fast path.

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Hello.

>>>>>>>

>>>>>>> Having that inlined would be great, however we'll need to put it to

>>>>>>> tree.h

>>>>>>> and thus we have to include "options.h" before tree.h in multiple

>>>>>>> source

>>>>>>> files.

>>>>>>> Please take a look at partial patch.

>>>>>>>

>>>>>>>>

>>>>>>>> And, it should have an early out,

>>>>>>>>       if (result_flags == 0)

>>>>>>>>         return false;

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Good idea!

>>>>>>>

>>>>>>>>

>>>>>>>>> +

>>>>>>>>> +  if (fn != NULL_TREE)

>>>>>>>>> +    {

>>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>>>>> DECL_ATTRIBUTES (fn));

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> The attribute, if it is internal only, should have spaces or similar

>>>>>>>> characters in its name, like "fn spec", "omp declare target" and

>>>>>>>> many

>>>>>>>> others.

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Done that.

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>>>>> explain

>>>>>> why we need this?  We do, after all, have -fsanitize options already.

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> Can be seen here:

>>>>>

>>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"),

>>>>> sanitize

>>>>> no_flags (16777195)))

>>>>> fn1 ()

>>>>> {

>>>>> ...

>>>>> }

>>>>>

>>>>> where no_sanitize_thread and no_sanitize are normal attributes used by

>>>>> users.

>>>>> But we want to aggregate all there attributes and build one integer

>>>>> mask

>>>>> that

>>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>>>>> no_flags'

>>>>> attribute, so that we don't have to iterate all attrs every time in

>>>>> anitize_flags_p.

>>>>>

>>>>> Hope it explains situation?

>>>>

>>>>

>>>>

>>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute

>>>> internal rep use a INTEGER_CST instread of a STRING_CST value,

>>>> updating that in handle_attribute instead of adding new attributes?

>>>>

>>>> There's nothing that forces internal representation to match what the

>>>> user wrote.

>>>

>>>

>>>

>>> I see, but consider following test-case:

>>>

>>> void

>>> __attribute__((no_sanitize_thread))

>>> __attribute__((no_sanitize(("address"))))

>>> __attribute__((no_sanitize(("undefined"))))

>>> __attribute__((no_sanitize(("address"))))

>>> __attribute__((no_sanitize(("null"))))

>>> foo (void) {}

>>>

>>> handle_no_sanitize_thread_attribute function is called for

>>> no_sanitize_thread and

>>> changing first no_sanitize attribute to integer is wrongly doable.

>>

>>

>> Just change no_sanitize_thread to add no_sanitize instead?

>

>

> Unfortunately, we currently support no_sanitize_{address,thread,undefined}

> and no_address_safety_analysis. Thus we can't drop these.


Sure, but the internal representation of no_sanitize_{address,thread,undefined}
can be the same as no_sanitize("...").  Just add *no_add_attrs = true and
append/change no_sanitize in the handler.

>

>>

>>> Apart

>>> from that,

>>> we want to merge all flags to a single attribute. Thus said, having an

>>> unique name

>>> will enable this.

>>

>>

>> no_sanitize looks like the unique name to me -- I suppose

>> no_sanitize("thread")

>> works?

>

>

> Yep, it's unique but as I mentioned we've got const struct attribute_spec

> c_common_attribute_table[]

> handlers that are executed on these no sanitize attributes. And as I want to

> store int mask to

> an attribute, I prefer to come up with a new attribute "sanitize no_flags"

> and I can set

>   *no_add_attrs = true; in order to remove the original attributes:

>

> void

> __attribute__((no_sanitize(("address"))))

> __attribute__((no_sanitize(("undefined"))))

> __attribute__((no_sanitize(("address"))))

> __attribute__((no_sanitize(("null"))))

> fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0];

> __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }

>

> will become:

>

> __attribute__((sanitize no_flags (16777187)))

> fn1 ()

> {

> ...

> }

>

> I'm going to test that.

>

> Martin

>

>

>>

>> Richard.

>>

>>> Martin

>>>

>>>

>>>>

>>>> [historically we've used random flags in decls/types for this kind of

>>>> caching

>>>> but I can see we don't want to waste as many bits there]

>>>>

>>>> Richard.

>>>>

>>>>> Martin

>>>>>

>>>>>

>>>>>>

>>>>>> Richard.

>>>>>>

>>>>>>>>

>>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>>>>> +{

>>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags",

>>>>>>>> DECL_ATTRIBUTES

>>>>>>>> (node));

>>>>>>>> +  if (attr)

>>>>>>>> +    {

>>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>>>>> +      flags |= old_value;

>>>>>>>> +    }

>>>>>>>> +

>>>>>>>> +  DECL_ATTRIBUTES (node)

>>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>>>>> +                DECL_ATTRIBUTES (node));

>>>>>>>>

>>>>>>>> If there is a previous attribute already, can't you modify it in

>>>>>>>> place?  If not, as it could be perhaps shared? with other functions

>>>>>>>> somehow, at least you should avoid adding a new attribute if

>>>>>>>> (old_value | flags) == old_value.

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Yep, we should definitely share, I'll add test-case for that.

>>>>>>> I'm currently testing the incremental patch, may I install it

>>>>>>> after regression tests?

>>>>>>>

>>>>>>> Martin

>>>>>>>

>>>>>>>>

>>>>>>>>            Jakub

>>>>>>>>

>>>>>>>

>>>>>

>>>

>
Martin Liška June 9, 2017, 12:51 p.m. UTC | #32
On 06/09/2017 02:27 PM, Richard Biener wrote:
> On Fri, Jun 9, 2017 at 2:08 PM, Martin Liška <mliska@suse.cz> wrote:

>> On 06/09/2017 01:05 PM, Richard Biener wrote:

>>>

>>> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>

>>>> On 06/09/2017 12:39 PM, Richard Biener wrote:

>>>>>

>>>>>

>>>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>>>

>>>>>>

>>>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz> wrote:

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> Hi!

>>>>>>>>>

>>>>>>>>> I'd still prefer to handle it with the flags infrastructure instead,

>>>>>>>>> but

>>>>>>>>> if

>>>>>>>>> Richard wants to do it this way, then at least:

>>>>>>>>>

>>>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>>>>>> non-null,

>>>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>>>>>> DECL_ATTRIBUTES.

>>>>>>>>>> */

>>>>>>>>>> +

>>>>>>>>>> +bool

>>>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>>>>>> +{

>>>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> This function really should be either inline, or partly inline,

>>>>>>>>> partly

>>>>>>>>> out

>>>>>>>>> of line, to handle the common case (sanitization of something not

>>>>>>>>> enabled)

>>>>>>>>> in the fast path.

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Hello.

>>>>>>>>

>>>>>>>> Having that inlined would be great, however we'll need to put it to

>>>>>>>> tree.h

>>>>>>>> and thus we have to include "options.h" before tree.h in multiple

>>>>>>>> source

>>>>>>>> files.

>>>>>>>> Please take a look at partial patch.

>>>>>>>>

>>>>>>>>>

>>>>>>>>> And, it should have an early out,

>>>>>>>>>        if (result_flags == 0)

>>>>>>>>>          return false;

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Good idea!

>>>>>>>>

>>>>>>>>>

>>>>>>>>>> +

>>>>>>>>>> +  if (fn != NULL_TREE)

>>>>>>>>>> +    {

>>>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>>>>>> DECL_ATTRIBUTES (fn));

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> The attribute, if it is internal only, should have spaces or similar

>>>>>>>>> characters in its name, like "fn spec", "omp declare target" and

>>>>>>>>> many

>>>>>>>>> others.

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Done that.

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>>>>>> explain

>>>>>>> why we need this?  We do, after all, have -fsanitize options already.

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Can be seen here:

>>>>>>

>>>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>>>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"),

>>>>>> sanitize

>>>>>> no_flags (16777195)))

>>>>>> fn1 ()

>>>>>> {

>>>>>> ...

>>>>>> }

>>>>>>

>>>>>> where no_sanitize_thread and no_sanitize are normal attributes used by

>>>>>> users.

>>>>>> But we want to aggregate all there attributes and build one integer

>>>>>> mask

>>>>>> that

>>>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>>>>>> no_flags'

>>>>>> attribute, so that we don't have to iterate all attrs every time in

>>>>>> anitize_flags_p.

>>>>>>

>>>>>> Hope it explains situation?

>>>>>

>>>>>

>>>>>

>>>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute

>>>>> internal rep use a INTEGER_CST instread of a STRING_CST value,

>>>>> updating that in handle_attribute instead of adding new attributes?

>>>>>

>>>>> There's nothing that forces internal representation to match what the

>>>>> user wrote.

>>>>

>>>>

>>>>

>>>> I see, but consider following test-case:

>>>>

>>>> void

>>>> __attribute__((no_sanitize_thread))

>>>> __attribute__((no_sanitize(("address"))))

>>>> __attribute__((no_sanitize(("undefined"))))

>>>> __attribute__((no_sanitize(("address"))))

>>>> __attribute__((no_sanitize(("null"))))

>>>> foo (void) {}

>>>>

>>>> handle_no_sanitize_thread_attribute function is called for

>>>> no_sanitize_thread and

>>>> changing first no_sanitize attribute to integer is wrongly doable.

>>>

>>>

>>> Just change no_sanitize_thread to add no_sanitize instead?

>>

>>

>> Unfortunately, we currently support no_sanitize_{address,thread,undefined}

>> and no_address_safety_analysis. Thus we can't drop these.

> 

> Sure, but the internal representation of no_sanitize_{address,thread,undefined}

> can be the same as no_sanitize("...").  Just add *no_add_attrs = true and

> append/change no_sanitize in the handler.


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

Martin

> 

>>

>>>

>>>> Apart

>>>> from that,

>>>> we want to merge all flags to a single attribute. Thus said, having an

>>>> unique name

>>>> will enable this.

>>>

>>>

>>> no_sanitize looks like the unique name to me -- I suppose

>>> no_sanitize("thread")

>>> works?

>>

>>

>> Yep, it's unique but as I mentioned we've got const struct attribute_spec

>> c_common_attribute_table[]

>> handlers that are executed on these no sanitize attributes. And as I want to

>> store int mask to

>> an attribute, I prefer to come up with a new attribute "sanitize no_flags"

>> and I can set

>>    *no_add_attrs = true; in order to remove the original attributes:

>>

>> void

>> __attribute__((no_sanitize(("address"))))

>> __attribute__((no_sanitize(("undefined"))))

>> __attribute__((no_sanitize(("address"))))

>> __attribute__((no_sanitize(("null"))))

>> fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0];

>> __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }

>>

>> will become:

>>

>> __attribute__((sanitize no_flags (16777187)))

>> fn1 ()

>> {

>> ...

>> }

>>

>> I'm going to test that.

>>

>> Martin

>>

>>

>>>

>>> Richard.

>>>

>>>> Martin

>>>>

>>>>

>>>>>

>>>>> [historically we've used random flags in decls/types for this kind of

>>>>> caching

>>>>> but I can see we don't want to waste as many bits there]

>>>>>

>>>>> Richard.

>>>>>

>>>>>> Martin

>>>>>>

>>>>>>

>>>>>>>

>>>>>>> Richard.

>>>>>>>

>>>>>>>>>

>>>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>>>>>> +{

>>>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags",

>>>>>>>>> DECL_ATTRIBUTES

>>>>>>>>> (node));

>>>>>>>>> +  if (attr)

>>>>>>>>> +    {

>>>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>>>>>> +      flags |= old_value;

>>>>>>>>> +    }

>>>>>>>>> +

>>>>>>>>> +  DECL_ATTRIBUTES (node)

>>>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>>>>>> +                DECL_ATTRIBUTES (node));

>>>>>>>>>

>>>>>>>>> If there is a previous attribute already, can't you modify it in

>>>>>>>>> place?  If not, as it could be perhaps shared? with other functions

>>>>>>>>> somehow, at least you should avoid adding a new attribute if

>>>>>>>>> (old_value | flags) == old_value.

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Yep, we should definitely share, I'll add test-case for that.

>>>>>>>> I'm currently testing the incremental patch, may I install it

>>>>>>>> after regression tests?

>>>>>>>>

>>>>>>>> Martin

>>>>>>>>

>>>>>>>>>

>>>>>>>>>             Jakub

>>>>>>>>>

>>>>>>>>

>>>>>>

>>>>

>>From 22f08924dc3fdbe31dfb6fa83717a2a694604ef8 Mon Sep 17 00:00:00 2001

From: marxin <mliska@suse.cz>

Date: Thu, 8 Jun 2017 16:39:33 +0200
Subject: [PATCH] Put sanitize_flags_t to asan.h and merge all no_sanitize_*
 attributes.

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

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


Richard Biener June 9, 2017, 1:35 p.m. UTC | #33
On Fri, Jun 9, 2017 at 2:51 PM, Martin Liška <mliska@suse.cz> wrote:
> On 06/09/2017 02:27 PM, Richard Biener wrote:

>>

>> On Fri, Jun 9, 2017 at 2:08 PM, Martin Liška <mliska@suse.cz> wrote:

>>>

>>> On 06/09/2017 01:05 PM, Richard Biener wrote:

>>>>

>>>>

>>>> On Fri, Jun 9, 2017 at 12:49 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>>

>>>>>

>>>>> On 06/09/2017 12:39 PM, Richard Biener wrote:

>>>>>>

>>>>>>

>>>>>>

>>>>>> On Fri, Jun 9, 2017 at 12:17 PM, Martin Liška <mliska@suse.cz> wrote:

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> On 06/09/2017 12:12 PM, Richard Biener wrote:

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> On Fri, Jun 9, 2017 at 11:29 AM, Martin Liška <mliska@suse.cz>

>>>>>>>> wrote:

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> On 06/08/2017 03:47 PM, Jakub Jelinek wrote:

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> Hi!

>>>>>>>>>>

>>>>>>>>>> I'd still prefer to handle it with the flags infrastructure

>>>>>>>>>> instead,

>>>>>>>>>> but

>>>>>>>>>> if

>>>>>>>>>> Richard wants to do it this way, then at least:

>>>>>>>>>>

>>>>>>>>>> On Thu, Jun 08, 2017 at 03:30:49PM +0200, Martin Liška wrote:

>>>>>>>>>>>

>>>>>>>>>>>

>>>>>>>>>>>

>>>>>>>>>>>

>>>>>>>>>>>

>>>>>>>>>>> +/* Return true when flag_sanitize & FLAG is non-zero.  If FN is

>>>>>>>>>>> non-null,

>>>>>>>>>>> +   remove all flags mentioned in "no_sanitize_flags" of

>>>>>>>>>>> DECL_ATTRIBUTES.

>>>>>>>>>>> */

>>>>>>>>>>> +

>>>>>>>>>>> +bool

>>>>>>>>>>> +sanitize_flags_p (unsigned int flag, const_tree fn)

>>>>>>>>>>> +{

>>>>>>>>>>> +  unsigned int result_flags = flag_sanitize & flag;

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> This function really should be either inline, or partly inline,

>>>>>>>>>> partly

>>>>>>>>>> out

>>>>>>>>>> of line, to handle the common case (sanitization of something not

>>>>>>>>>> enabled)

>>>>>>>>>> in the fast path.

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> Hello.

>>>>>>>>>

>>>>>>>>> Having that inlined would be great, however we'll need to put it to

>>>>>>>>> tree.h

>>>>>>>>> and thus we have to include "options.h" before tree.h in multiple

>>>>>>>>> source

>>>>>>>>> files.

>>>>>>>>> Please take a look at partial patch.

>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> And, it should have an early out,

>>>>>>>>>>        if (result_flags == 0)

>>>>>>>>>>          return false;

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> Good idea!

>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>> +

>>>>>>>>>>> +  if (fn != NULL_TREE)

>>>>>>>>>>> +    {

>>>>>>>>>>> +      tree value = lookup_attribute ("no_sanitize_flags",

>>>>>>>>>>> DECL_ATTRIBUTES (fn));

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> The attribute, if it is internal only, should have spaces or

>>>>>>>>>> similar

>>>>>>>>>> characters in its name, like "fn spec", "omp declare target" and

>>>>>>>>>> many

>>>>>>>>>> others.

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> Done that.

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>>

>>>>>>>> Whoo, wait -- this is for internal use only?  Can you step back and

>>>>>>>> explain

>>>>>>>> why we need this?  We do, after all, have -fsanitize options

>>>>>>>> already.

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>>

>>>>>>> Can be seen here:

>>>>>>>

>>>>>>> __attribute__((no_sanitize_thread, no_sanitize ("null"), no_sanitize

>>>>>>> ("address"), no_sanitize ("undefined"), no_sanitize ("address"),

>>>>>>> sanitize

>>>>>>> no_flags (16777195)))

>>>>>>> fn1 ()

>>>>>>> {

>>>>>>> ...

>>>>>>> }

>>>>>>>

>>>>>>> where no_sanitize_thread and no_sanitize are normal attributes used

>>>>>>> by

>>>>>>> users.

>>>>>>> But we want to aggregate all there attributes and build one integer

>>>>>>> mask

>>>>>>> that

>>>>>>> will drive sanitize_flags_p. And that's why we introduced 'sanitize

>>>>>>> no_flags'

>>>>>>> attribute, so that we don't have to iterate all attrs every time in

>>>>>>> anitize_flags_p.

>>>>>>>

>>>>>>> Hope it explains situation?

>>>>>>

>>>>>>

>>>>>>

>>>>>>

>>>>>> Hum, ok ... but then you can "simply" have the no_sanitize attribute

>>>>>> internal rep use a INTEGER_CST instread of a STRING_CST value,

>>>>>> updating that in handle_attribute instead of adding new attributes?

>>>>>>

>>>>>> There's nothing that forces internal representation to match what the

>>>>>> user wrote.

>>>>>

>>>>>

>>>>>

>>>>>

>>>>> I see, but consider following test-case:

>>>>>

>>>>> void

>>>>> __attribute__((no_sanitize_thread))

>>>>> __attribute__((no_sanitize(("address"))))

>>>>> __attribute__((no_sanitize(("undefined"))))

>>>>> __attribute__((no_sanitize(("address"))))

>>>>> __attribute__((no_sanitize(("null"))))

>>>>> foo (void) {}

>>>>>

>>>>> handle_no_sanitize_thread_attribute function is called for

>>>>> no_sanitize_thread and

>>>>> changing first no_sanitize attribute to integer is wrongly doable.

>>>>

>>>>

>>>>

>>>> Just change no_sanitize_thread to add no_sanitize instead?

>>>

>>>

>>>

>>> Unfortunately, we currently support

>>> no_sanitize_{address,thread,undefined}

>>> and no_address_safety_analysis. Thus we can't drop these.

>>

>>

>> Sure, but the internal representation of

>> no_sanitize_{address,thread,undefined}

>> can be the same as no_sanitize("...").  Just add *no_add_attrs = true and

>> append/change no_sanitize in the handler.

>

>

> Yep, that would be possible. That will mean to tranform all no_sanitize_* to

> no_sanitize("...") attributes and then merge all these attributes with a

> string

> value to a single one with integer mask. Well, doable, but I still prefer to

> merge directly all to the new attribute. Plase take a look at incremental

> patch.


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

> Martin

>

>

>>

>>>

>>>>

>>>>> Apart

>>>>> from that,

>>>>> we want to merge all flags to a single attribute. Thus said, having an

>>>>> unique name

>>>>> will enable this.

>>>>

>>>>

>>>>

>>>> no_sanitize looks like the unique name to me -- I suppose

>>>> no_sanitize("thread")

>>>> works?

>>>

>>>

>>>

>>> Yep, it's unique but as I mentioned we've got const struct attribute_spec

>>> c_common_attribute_table[]

>>> handlers that are executed on these no sanitize attributes. And as I want

>>> to

>>> store int mask to

>>> an attribute, I prefer to come up with a new attribute "sanitize

>>> no_flags"

>>> and I can set

>>>    *no_add_attrs = true; in order to remove the original attributes:

>>>

>>> void

>>> __attribute__((no_sanitize(("address"))))

>>> __attribute__((no_sanitize(("undefined"))))

>>> __attribute__((no_sanitize(("address"))))

>>> __attribute__((no_sanitize(("null"))))

>>> fn1 (void) { char *ptr; char *ptr2; { char my_char[9]; ptr = &my_char[0];

>>> __builtin_memcpy (&ptr2, &ptr, sizeof (ptr2)); } *(ptr2+9) = 'c'; }

>>>

>>> will become:

>>>

>>> __attribute__((sanitize no_flags (16777187)))

>>> fn1 ()

>>> {

>>> ...

>>> }

>>>

>>> I'm going to test that.

>>>

>>> Martin

>>>

>>>

>>>>

>>>> Richard.

>>>>

>>>>> Martin

>>>>>

>>>>>

>>>>>>

>>>>>> [historically we've used random flags in decls/types for this kind of

>>>>>> caching

>>>>>> but I can see we don't want to waste as many bits there]

>>>>>>

>>>>>> Richard.

>>>>>>

>>>>>>> Martin

>>>>>>>

>>>>>>>

>>>>>>>>

>>>>>>>> Richard.

>>>>>>>>

>>>>>>>>>>

>>>>>>>>>> +add_no_sanitize_value (tree node, unsigned int flags)

>>>>>>>>>> +{

>>>>>>>>>> +  tree attr = lookup_attribute ("no_sanitize_flags",

>>>>>>>>>> DECL_ATTRIBUTES

>>>>>>>>>> (node));

>>>>>>>>>> +  if (attr)

>>>>>>>>>> +    {

>>>>>>>>>> +      unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));

>>>>>>>>>> +      flags |= old_value;

>>>>>>>>>> +    }

>>>>>>>>>> +

>>>>>>>>>> +  DECL_ATTRIBUTES (node)

>>>>>>>>>> +    = tree_cons (get_identifier ("no_sanitize_flags"),

>>>>>>>>>> +                build_int_cst (unsigned_type_node, flags),

>>>>>>>>>> +                DECL_ATTRIBUTES (node));

>>>>>>>>>>

>>>>>>>>>> If there is a previous attribute already, can't you modify it in

>>>>>>>>>> place?  If not, as it could be perhaps shared? with other

>>>>>>>>>> functions

>>>>>>>>>> somehow, at least you should avoid adding a new attribute if

>>>>>>>>>> (old_value | flags) == old_value.

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>>

>>>>>>>>> Yep, we should definitely share, I'll add test-case for that.

>>>>>>>>> I'm currently testing the incremental patch, may I install it

>>>>>>>>> after regression tests?

>>>>>>>>>

>>>>>>>>> Martin

>>>>>>>>>

>>>>>>>>>>

>>>>>>>>>>             Jakub

>>>>>>>>>>

>>>>>>>>>

>>>>>>>

>>>>>

>>>

>
Martin Liška June 13, 2017, 11:09 a.m. UTC | #34
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?
MartinFrom 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


Richard Biener June 13, 2017, 1:49 p.m. UTC | #35
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 | #36
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 046ce41931ea1d4cb51c14b74f2bcddc2cb4c6ca Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Wed, 21 Dec 2016 11:13:34 +0100
Subject: [PATCH] Implement no_sanitize function attribute

gcc/cp/ChangeLog:

2016-12-22  Martin Liska  <mliska@suse.cz>

	* class.c (build_base_path): Use sanitize_flags_p predicate.
	* 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 (build_vec_init): Likewise.
	* lambda.c (maybe_add_lambda_conv_op): Use
	add_no_sanitize_value.
	* typeck.c (cp_build_binary_op): Use sanitize_flags_p.
	(build_static_cast_1): Likewise.

gcc/c-family/ChangeLog:

2016-12-22  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
	add_no_sanitize_value.
	(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 (add_no_sanitize_value): Declare.
	* 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.
	* c-ubsan.h (do_ubsan_in_current_function): Remove declaration.

gcc/testsuite/ChangeLog:

2016-12-22  Martin Liska  <mliska@suse.cz>

	* c-c++-common/ubsan/align-10.c: New test.
	* c-c++-common/ubsan/attrib-5.c: New test.
	* c-c++-common/ubsan/attrib-6.c: New test.
	* c-c++-common/ubsan/bounds-14.c: New test.
	* c-c++-common/ubsan/div-by-zero-8.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-11.c: New test.
	* c-c++-common/ubsan/float-div-by-zero-2.c: New test.
	* c-c++-common/ubsan/load-bool-enum-2.c: New test.
	* c-c++-common/ubsan/nonnull-6.c: New test.
	* c-c++-common/ubsan/null-12.c: New test.
	* c-c++-common/ubsan/object-size-11.c: New test.
	* c-c++-common/ubsan/overflow-3.c: New test.
	* c-c++-common/ubsan/unreachable-5.c: New test.
	* c-c++-common/ubsan/vla-5.c: New test.
	* g++.dg/ubsan/return-8.C: New test.
	* g++.dg/ubsan/vptr-12.C: New test.
	* gcc.dg/ubsan/bounds-4.c: New test.
	* gcc.dg/ubsan/c99-shift-7.c: New test.
	* gcc.dg/ubsan/c99-shift-8.c: New test.

gcc/ChangeLog:

2016-12-22  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.
	* builtins.def: Use renamed SANITIZE_UNDEFINED_NONDEFAULT.
	* common.opt: Likewise.
	* convert.c (convert_to_integer_1): Use sanitize_flags_p.
	* doc/extend.texi: Document the new attribute.
	* doc/invoke.texi: Improve documentation of
	-sanitize=bounds-strict.
	* 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_decl_expr): Likewise.
	(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 the renamed enum value.
	* opts.h (parse_no_sanitize_attribute): Declare.
	* tree.c (sanitize_flags_p): New function.
	* tree.h (sanitize_flags_p): New declaration.
	* tsan.c (gate): 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.
	(pass_ubsan::execute): Use sanitize_flags_p.
	* ubsan.h (do_ubsan_in_current_function): Remove.

gcc/c/ChangeLog:

2016-12-22  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                                         |  14 +-
 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/doc/invoke.texi                                |   2 +
 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/align-10.c        |  56 +++++
 gcc/testsuite/c-c++-common/ubsan/attrib-5.c        |  12 +
 gcc/testsuite/c-c++-common/ubsan/attrib-6.c        |  15 ++
 gcc/testsuite/c-c++-common/ubsan/bounds-14.c       |  17 ++
 gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c   |   9 +
 .../c-c++-common/ubsan/float-cast-overflow-11.c    | 205 ++++++++++++++++
 .../c-c++-common/ubsan/float-div-by-zero-2.c       |  27 +++
 .../c-c++-common/ubsan/load-bool-enum-2.c          |  31 +++
 gcc/testsuite/c-c++-common/ubsan/nonnull-6.c       |  40 ++++
 gcc/testsuite/c-c++-common/ubsan/null-12.c         |  13 +
 gcc/testsuite/c-c++-common/ubsan/object-size-11.c  | 131 +++++++++++
 gcc/testsuite/c-c++-common/ubsan/overflow-3.c      | 262 +++++++++++++++++++++
 gcc/testsuite/c-c++-common/ubsan/unreachable-5.c   |  11 +
 gcc/testsuite/c-c++-common/ubsan/vla-5.c           |  14 ++
 gcc/testsuite/g++.dg/ubsan/return-8.C              |  29 +++
 gcc/testsuite/g++.dg/ubsan/vptr-12.C               | 185 +++++++++++++++
 gcc/testsuite/gcc.dg/ubsan/bounds-4.c              |  20 ++
 gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c           |  11 +
 gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c           |  12 +
 gcc/tree.c                                         |  17 ++
 gcc/tree.h                                         |   4 +
 gcc/tsan.c                                         |  12 +-
 gcc/ubsan.c                                        |  48 ++--
 gcc/ubsan.h                                        |   1 -
 53 files changed, 1345 insertions(+), 139 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-10.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/attrib-5.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/attrib-6.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/bounds-14.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/null-12.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/object-size-11.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/overflow-3.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/unreachable-5.c
 create mode 100644 gcc/testsuite/c-c++-common/ubsan/vla-5.c
 create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8.C
 create mode 100644 gcc/testsuite/g++.dg/ubsan/vptr-12.C
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/bounds-4.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c

diff --git a/gcc/asan.c b/gcc/asan.c
index 53acff0a2fb..8ec6b75d29f 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -304,9 +304,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.  */
@@ -3067,11 +3065,9 @@  asan_instrument (void)
 }
 
 static bool
-gate_asan (void)
+gate_asan (function *fn)
 {
-  return (flag_sanitize & SANITIZE_ADDRESS) != 0
-	  && !lookup_attribute ("no_sanitize_address",
-				DECL_ATTRIBUTES (current_function_decl));
+  return sanitize_flags_p (SANITIZE_ADDRESS, fn->decl);
 }
 
 namespace {
@@ -3098,7 +3094,7 @@  public:
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_asan (m_ctxt); }
-  virtual bool gate (function *) { return gate_asan (); }
+  virtual bool gate (function *fn) { return gate_asan (fn); }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan
@@ -3134,7 +3130,7 @@  public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *) { return !optimize && gate_asan (); }
+  virtual bool gate (function *fn) { return !optimize && gate_asan (fn); }
   virtual unsigned int execute (function *) { return asan_instrument (); }
 
 }; // class pass_asan_O0
diff --git a/gcc/asan.h b/gcc/asan.h
index 355a350bfeb..bbd81a1ebe2 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -140,13 +140,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 24b34e80cf1..60d0e2a8147 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 f5adadee3af..1231b10c9d7 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 (integer_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 a23193ee9db..23f4a64ca5a 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1550,6 +1550,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 6f93d80a746..11b88b44cc3 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:
@@ -177,7 +177,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);
@@ -300,7 +300,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))
     {
@@ -372,7 +372,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);
@@ -392,7 +392,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);
@@ -400,7 +400,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)
@@ -418,7 +418,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
@@ -434,7 +434,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 30d4f971a7e..8a36ac6c088 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 4167c3461f3..8a146aafa33 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))
 	{
 	  if (in_late_binary_op)
 	    expr = save_expr (expr);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index db293fe60d1..cc0b9e77ce6 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -6051,9 +6051,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 = c_save_expr (size);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index c134280325d..3ee7cde2b39 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -6320,7 +6320,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);
@@ -9867,7 +9867,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;
@@ -11747,9 +11747,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)
     {
@@ -11758,10 +11757,10 @@  build_binary_op (location_t location, enum tree_code code,
       op1 = c_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 de068447d31..039375ab9b2 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 54b0a5d8327..d9b9c418a46 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -917,8 +917,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 df4d73c4d30..df89aaf8bc5 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -458,7 +458,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 0678243b36f..f0a3ce9a280 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1288,8 +1288,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;
@@ -1476,11 +1475,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);
@@ -1496,9 +1495,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);
 	    }
 	}
@@ -1525,7 +1524,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);
 }
 
@@ -1648,8 +1647,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 9c8f6e67d54..51f50737a74 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 e83b542d424..afdb337acad 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9529,8 +9529,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.  */
@@ -15066,7 +15065,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 257d21133c1..9dcc038869b 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3730,7 +3730,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 47428b96f3d..981bb5301de 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -3991,8 +3991,7 @@  build_vec_init (tree base, tree maxindex, tree init,
 		}
 	      /* 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 c48cd5201e0..f174a8b3bd8 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -1099,9 +1099,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, NULL_TREE);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 68fe19eeadb..0c25e0cbdfc 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5167,10 +5167,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.  */
@@ -5178,8 +5177,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
@@ -5193,7 +5192,7 @@  cp_build_binary_op (location_t location,
 	    }
 	  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);
     }
 
@@ -6707,7 +6706,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,
@@ -6851,7 +6850,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 1f303bcb9aa..b5ed8ddebef 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2894,6 +2894,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/doc/invoke.texi b/gcc/doc/invoke.texi
index b7299648879..12d7089d5c4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10770,6 +10770,8 @@  This option enables strict instrumentation of array bounds.  Most out of bounds
 accesses are detected, including flexible array members and flexible array
 member-like arrays.  Initializers of variables with static storage are not
 instrumented.
+Unlike other similar options, @option{-fsanitize=bounds-strict} is
+not enabled by @option{-fsanitize=undefined}.
 
 @item -fsanitize=alignment
 @opindex fsanitize=alignment
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index d69f8f489b8..70f08a554ca 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 f78acd68606..6e47eb3ddeb 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -9312,7 +9312,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 9c86f158503..756b16852cf 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -3442,7 +3442,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 a3001331181..b93507e4084 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1621,7 +1621,7 @@  gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
 	}
 
       if (asan_sanitize_use_after_scope ()
-	  && !asan_no_sanitize_address_p ()
+	  && sanitize_flags_p (SANITIZE_ADDRESS)
 	  && !is_vla
 	  && TREE_ADDRESSABLE (decl)
 	  && !TREE_STATIC (decl)
@@ -12594,8 +12594,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 5f2371c6b26..6ed2f6076f0 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -256,17 +256,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 890da03e6a1..4473fc66d75 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1582,6 +1582,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
@@ -1818,11 +1845,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 b3e64353c8a..680bfa7fd77 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/align-10.c b/gcc/testsuite/c-c++-common/ubsan/align-10.c
new file mode 100644
index 00000000000..73d6078037f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/align-10.c
@@ -0,0 +1,56 @@ 
+/* Limit this to known non-strict alignment targets.  */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=alignment" } */
+
+struct S { int a; char b; long long c; short d[10]; };
+struct T { char a; long long b; };
+struct U { char a; int b; int c; long long d; struct S e; struct T f; } __attribute__((packed));
+struct V { long long a; struct S b; struct T c; struct U u; } v;
+
+__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) void
+f1 (int *p, int *q, char *r, long long *s)
+{
+  *p =
+      *q
+      + *r
+      + *s;
+}
+
+
+__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) int
+f2 (struct S *p)
+{
+  return p->a;
+}
+
+__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) long long
+f3 (struct S *p, int i)
+{
+  return p->c
+	 + p->d[1]
+	 + p->d[i];
+}
+
+__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) long long
+f4 (long long *p)
+{
+  return *p;
+}
+
+int
+main ()
+{
+  f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d);
+  if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
+/* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-5.c b/gcc/testsuite/c-c++-common/ubsan/attrib-5.c
new file mode 100644
index 00000000000..29cb96e8ebc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/attrib-5.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct S { int a[16]; };
+
+__attribute__((no_sanitize(("undefined")))) long long
+foo (int *a, long long *b, struct S *c)
+{
+  return a[1] + *b + c->a[a[0]];
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-6.c b/gcc/testsuite/c-c++-common/ubsan/attrib-6.c
new file mode 100644
index 00000000000..fc5afa035d2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/attrib-6.c
@@ -0,0 +1,15 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test that we don't instrument functions marked with
+   no_sanitize_undefined attribute.  */
+
+struct S { int a[16]; };
+
+__attribute__((no_sanitize("signed-integer-overflow,null,alignment"))) long long
+foo (int *a, long long *b, struct S *c)
+{
+  return a[1] + *b + c->a[a[0]];
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-14.c b/gcc/testsuite/c-c++-common/ubsan/bounds-14.c
new file mode 100644
index 00000000000..f94634b15f5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/bounds-14.c
@@ -0,0 +1,17 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds-strict" } */
+
+struct V { int l; int a[1]; };
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main (void)
+{
+  /* For strict, do instrument last array in a struct.  */
+  struct V *v = (struct V *) __builtin_malloc (sizeof (struct V) + 10);
+  v->a[1] = 1;
+
+  return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'int \\\[1\\\]'" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c
new file mode 100644
index 00000000000..6e54217f3a1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=integer-divide-by-zero" } */
+
+void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,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"))))
+foo (void)
+{
+  int A[-2 / -1] = {};
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c b/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c
new file mode 100644
index 00000000000..2d453f4e0fd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c
@@ -0,0 +1,205 @@ 
+/* { dg-do run { target { lp64 || ilp32 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { sse2_runtime && ia32 } } } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+  volatile signed char sc;
+  d = SCHAR_MIN;
+  CHECK_BOUNDARY (sc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (sc, d);
+  d = SCHAR_MAX;
+  CHECK_BOUNDARY (sc, d);
+  CHECK_NONNUMBERS (sc);
+
+  volatile unsigned char uc;
+  d = UCHAR_MAX;
+  CHECK_BOUNDARY (uc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (uc, d);
+  CHECK_NONNUMBERS (uc);
+
+  volatile short int s;
+  d = SHRT_MIN;
+  CHECK_BOUNDARY (s, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s, d);
+  d = SHRT_MAX;
+  CHECK_BOUNDARY (s, d);
+  CHECK_NONNUMBERS (s);
+
+  volatile unsigned short int us;
+  d = USHRT_MAX;
+  CHECK_BOUNDARY (us, d);
+  d = 0.0;
+  CHECK_BOUNDARY (us, d);
+  CHECK_NONNUMBERS (us);
+
+  volatile int i;
+  d = INT_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  d = UINT_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  volatile long l;
+  /* 64-bit vs 32-bit longs matter causes too much of a headache.  */
+  d = 0.0;
+  CHECK_BOUNDARY (l, d);
+  CHECK_NONNUMBERS (l);
+
+  volatile unsigned long ul;
+  d = 0.0;
+  CHECK_BOUNDARY (ul, d);
+  CHECK_NONNUMBERS (ul);
+
+  volatile long long ll;
+  d = LLONG_MIN;
+  CHECK_BOUNDARY (ll, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ll, d);
+  d = LLONG_MAX;
+  CHECK_BOUNDARY (ll, d);
+  CHECK_NONNUMBERS (ll);
+
+  volatile unsigned long long ull;
+  d = ULLONG_MAX;
+  CHECK_BOUNDARY (ull, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ull, d);
+  CHECK_NONNUMBERS (ull);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c b/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c
new file mode 100644
index 00000000000..6cfec9564a9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c
@@ -0,0 +1,27 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-divide-by-zero" } */
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main (void)
+{
+  volatile float a = 1.3f;
+  volatile double b = 0.0;
+  volatile int c = 4;
+  volatile float res;
+
+  res = a / b;
+  res = a / 0.0;
+  res = 2.7f / b;
+  res = 3.6 / (b = 0.0, b);
+  res = c / b;
+  res = b / c;
+
+  return 0;
+}
+
+/* { dg-output "division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c
new file mode 100644
index 00000000000..c969578ee48
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c
@@ -0,0 +1,31 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bool,enum" } */
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+enum A { B = -3, C = 2 } a;
+bool b;
+
+__attribute__((noinline, noclone)) enum A
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+foo (bool *p)
+{
+  *p = b;   /* { dg-output "load-bool-enum-2.c:14:\[^\n\r]*runtime error: \[^\n\r]*load of value 4, which is not a valid value for type '(_B|b)ool'\[^\n\r]*(\n|\r\n|\r)*" } */
+  return a; /* { dg-output "\[^\n\r]*load-bool-enum-2.c:15:\[^\n\r]*runtime error: \[^\n\r]*load of value 9, which is not a valid value for type 'A'" { target c++ } } */
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main ()
+{
+  char c = 4;
+  int d = 9;
+  if (sizeof (int) != sizeof (a) || sizeof (b) != 1)
+    return 0;
+  __builtin_memcpy (&a, &d, sizeof (int));
+  __builtin_memcpy (&b, &c, 1);
+  bool e;
+  foo (&e);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
new file mode 100644
index 00000000000..22dbed1d15a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c
@@ -0,0 +1,40 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=nonnull-attribute,returns-nonnull-attribute" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3),no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr"))))
+void *
+foo (void *p, void *q, void *r)
+{
+  a = p;
+  b = r;
+  return q;
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr"))))
+bar (const void *a, const void *b)
+{
+  int c = *(const int *) a;
+  int d = *(const int *) b;
+  return c - d;
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr"))))
+main ()
+{
+  asm volatile ("" : : : "memory");
+  d = foo (c, b, c);
+  e = foo (e, c, f);
+  g = foo (c, f, g);
+  __builtin_memset (d, '\0', q);
+  return 0;
+}
+
+/* { dg-output "\.c:13:\[0-9]*:\[^\n\r]*null pointer returned from function declared to never return null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:32:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:33:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/null-12.c b/gcc/testsuite/c-c++-common/ubsan/null-12.c
new file mode 100644
index 00000000000..a89a5434fae
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/null-12.c
@@ -0,0 +1,13 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main (void)
+{
+  int *p = 0;
+  return *p;
+}
+
+/* { dg-output "load of null pointer of type 'int'" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/object-size-11.c b/gcc/testsuite/c-c++-common/ubsan/object-size-11.c
new file mode 100644
index 00000000000..46ff75c6268
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/object-size-11.c
@@ -0,0 +1,131 @@ 
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Sanity-test -fsanitize=object-size.  We use -fsanitize=undefined option
+   to check that this feature doesn't clash with -fsanitize=bounds et al.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+f4 (void)
+{
+  /* The second argument to __builtin_calloc is intentional.  */
+  int *p = (int *) __builtin_calloc (3, 1);
+  *p = 42;
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^" } */
+
+__attribute__((noinline, noclone)) void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+f5 (int *p)
+{
+  /* This is not instrumented.  But don't ICE, etc.  */
+  volatile int i = p[N];
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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,vptr"))))
+main ()
+{
+  f1 (N);
+  f2 (N);
+  f3 (N);
+  f4 ();
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  f5 (p);
+  __builtin_free (p);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-3.c b/gcc/testsuite/c-c++-common/ubsan/overflow-3.c
new file mode 100644
index 00000000000..d061e8ed55b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/overflow-3.c
@@ -0,0 +1,262 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow" } */
+
+#ifndef ASM1
+# define ASM1(a) /* Nothing */
+#endif
+#ifndef ASM2
+# define ASM2(a, b) /* Nothing */
+#endif
+
+#define CHECK(A, B) ({ if ((A) != (B)) __builtin_abort (); })
+
+#define FN1(T1, T2, OP)				\
+  ({						\
+    T1 a = 14;					\
+    T2 b = 9;					\
+    ASM2 (a, b);				\
+    a OP b;					\
+  })
+
+#define FN2(T, OP)				\
+  ({						\
+    T a = 14;					\
+    ASM1 (a);					\
+    a OP 7;					\
+  })
+
+#define FN3(T1, T2, OP)				\
+  ({						\
+    T1 a = 4;					\
+    T2 b = 1;					\
+    ASM2 (a, b);				\
+    ~a OP b;					\
+  })
+
+#define FN4(T1, T2, OP)				\
+  ({						\
+    T1 a = 4;					\
+    T2 b = 1;					\
+    ASM2 (a, b);				\
+    a OP ~b;					\
+  })
+
+#define FN5(T)					\
+  ({						\
+    T a = 77;					\
+    ASM1 (a);					\
+    -a;						\
+  })
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main (void)
+{
+  CHECK (FN1 (char, char, +), 23);
+  CHECK (FN1 (char, char, -), 5);
+  CHECK (FN1 (char, char, *), 126);
+  CHECK (FN1 (unsigned char, unsigned char, +), 23);
+  CHECK (FN1 (unsigned char, unsigned char, -), 5);
+  CHECK (FN1 (unsigned char, unsigned char, *), 126);
+  CHECK (FN1 (short, short, +), 23);
+  CHECK (FN1 (short, short, -), 5);
+  CHECK (FN1 (short, short, *), 126);
+  CHECK (FN1 (unsigned short, unsigned short, +), 23);
+  CHECK (FN1 (unsigned short, unsigned short, -), 5);
+  CHECK (FN1 (unsigned short, unsigned short, *), 126);
+  CHECK (FN1 (int, int, +), 23);
+  CHECK (FN1 (int, int, -), 5);
+  CHECK (FN1 (int, int, *), 126);
+  CHECK (FN1 (unsigned int, unsigned int, +), 23);
+  CHECK (FN1 (unsigned int, unsigned int, -), 5);
+  CHECK (FN1 (unsigned int, unsigned int, *), 126);
+  CHECK (FN1 (long int, long int, +), 23);
+  CHECK (FN1 (long int, long int, -), 5);
+  CHECK (FN1 (long int, long int, *), 126);
+  CHECK (FN1 (unsigned long int, unsigned long int, +), 23);
+  CHECK (FN1 (unsigned long int, unsigned long int, -), 5);
+  CHECK (FN1 (unsigned long int, unsigned long int, *), 126);
+  CHECK (FN1 (long long int, long int, +), 23);
+  CHECK (FN1 (long long int, long int, -), 5);
+  CHECK (FN1 (long long int, long int, *), 126);
+  CHECK (FN1 (unsigned long long int, unsigned long long int, +), 23);
+  CHECK (FN1 (unsigned long long int, unsigned long long int, -), 5);
+  CHECK (FN1 (unsigned long long int, unsigned long long int, *), 126);
+  CHECK (FN1 (int, unsigned char, +), 23);
+  CHECK (FN1 (int, unsigned char, -), 5);
+  CHECK (FN1 (int, unsigned char, *), 126);
+  CHECK (FN1 (unsigned char, int, +), 23);
+  CHECK (FN1 (unsigned char, int, -), 5);
+  CHECK (FN1 (unsigned char, int, *), 126);
+  CHECK (FN1 (int, long int, +), 23);
+  CHECK (FN1 (int, long int, -), 5);
+  CHECK (FN1 (int, long int, *), 126);
+  CHECK (FN1 (long int, int, +), 23);
+  CHECK (FN1 (long int, int, -), 5);
+  CHECK (FN1 (long int, int, *), 126);
+  CHECK (FN1 (unsigned int, int, +), 23);
+  CHECK (FN1 (unsigned int, int, -), 5);
+  CHECK (FN1 (unsigned int, int, *), 126);
+  CHECK (FN1 (int, unsigned int, +), 23);
+  CHECK (FN1 (int, unsigned int, -), 5);
+  CHECK (FN1 (int, unsigned int, *), 126);
+  CHECK (FN1 (unsigned long int, int, +), 23);
+  CHECK (FN1 (unsigned long int, int, -), 5);
+  CHECK (FN1 (unsigned long int, int, *), 126);
+  CHECK (FN1 (int, unsigned long int, +), 23);
+  CHECK (FN1 (int, unsigned long int, -), 5);
+  CHECK (FN1 (int, unsigned long int, *), 126);
+
+  CHECK (FN2 (char, +), 21);
+  CHECK (FN2 (char, -), 7);
+  CHECK (FN2 (char, *), 98);
+  CHECK (FN2 (unsigned char, +), 21);
+  CHECK (FN2 (unsigned char, -), 7);
+  CHECK (FN2 (unsigned char, *), 98);
+  CHECK (FN2 (short, +), 21);
+  CHECK (FN2 (short, -), 7);
+  CHECK (FN2 (short, *), 98);
+  CHECK (FN2 (unsigned short, +), 21);
+  CHECK (FN2 (unsigned short, -), 7);
+  CHECK (FN2 (unsigned short, *), 98);
+  CHECK (FN2 (int, +), 21);
+  CHECK (FN2 (int, -), 7);
+  CHECK (FN2 (int, *), 98);
+  CHECK (FN2 (unsigned int, +), 21);
+  CHECK (FN2 (unsigned int, -), 7);
+  CHECK (FN2 (unsigned int, *), 98);
+  CHECK (FN2 (long int, +), 21);
+  CHECK (FN2 (long int, -), 7);
+  CHECK (FN2 (long int, *), 98);
+  CHECK (FN2 (unsigned long int, +), 21);
+  CHECK (FN2 (unsigned long int, -), 7);
+  CHECK (FN2 (unsigned long int, *), 98);
+  CHECK (FN2 (long long int, +), 21);
+  CHECK (FN2 (long long int, -), 7);
+  CHECK (FN2 (long long int, *), 98);
+  CHECK (FN2 (unsigned long long int, +), 21);
+  CHECK (FN2 (unsigned long long int, -), 7);
+  CHECK (FN2 (unsigned long long int, *), 98);
+
+  CHECK (FN3 (char, char, +), -4);
+  CHECK (FN3 (char, char, -), -6);
+  CHECK (FN3 (char, char, *), -5);
+  CHECK (FN3 (unsigned char, unsigned char, +), -4);
+  CHECK (FN3 (unsigned char, unsigned char, -), -6);
+  CHECK (FN3 (unsigned char, unsigned char, *), -5);
+  CHECK (FN3 (short, short, +), -4);
+  CHECK (FN3 (short, short, -), -6);
+  CHECK (FN3 (short, short, *), -5);
+  CHECK (FN3 (unsigned short, unsigned short, +), -4);
+  CHECK (FN3 (unsigned short, unsigned short, -), -6);
+  CHECK (FN3 (unsigned short, unsigned short, *), -5);
+  CHECK (FN3 (int, int, +), -4);
+  CHECK (FN3 (int, int, -), -6);
+  CHECK (FN3 (int, int, *), -5);
+  CHECK (FN3 (unsigned int, unsigned int, +), -4);
+  CHECK (FN3 (unsigned int, unsigned int, -), -6);
+  CHECK (FN3 (unsigned int, unsigned int, *), -5);
+  CHECK (FN3 (long int, long int, +), -4);
+  CHECK (FN3 (long int, long int, -), -6);
+  CHECK (FN3 (long int, long int, *), -5);
+  CHECK (FN3 (unsigned long int, unsigned long int, +), -4);
+  CHECK (FN3 (unsigned long int, unsigned long int, -), -6);
+  CHECK (FN3 (unsigned long int, unsigned long int, *), -5);
+  CHECK (FN3 (long long int, long int, +), -4);
+  CHECK (FN3 (long long int, long int, -), -6);
+  CHECK (FN3 (long long int, long int, *), -5);
+  CHECK (FN3 (unsigned long long int, unsigned long long int, +), -4);
+  CHECK (FN3 (unsigned long long int, unsigned long long int, -), -6);
+  CHECK (FN3 (unsigned long long int, unsigned long long int, *), -5);
+  CHECK (FN3 (int, unsigned char, +), -4);
+  CHECK (FN3 (int, unsigned char, -), -6);
+  CHECK (FN3 (int, unsigned char, *), -5);
+  CHECK (FN3 (unsigned char, int, +), -4);
+  CHECK (FN3 (unsigned char, int, -), -6);
+  CHECK (FN3 (unsigned char, int, *), -5);
+  CHECK (FN3 (int, long int, +), -4);
+  CHECK (FN3 (int, long int, -), -6);
+  CHECK (FN3 (int, long int, *), -5);
+  CHECK (FN3 (long int, int, +), -4);
+  CHECK (FN3 (long int, int, -), -6);
+  CHECK (FN3 (long int, int, *), -5);
+  CHECK (FN3 (unsigned int, int, +), -4);
+  CHECK (FN3 (unsigned int, int, -), -6);
+  CHECK (FN3 (unsigned int, int, *), -5);
+  CHECK (FN3 (int, unsigned int, +), -4);
+  CHECK (FN3 (int, unsigned int, -), -6);
+  CHECK (FN3 (int, unsigned int, *), -5);
+  CHECK (FN3 (unsigned long int, int, +), -4);
+  CHECK (FN3 (unsigned long int, int, -), -6);
+  CHECK (FN3 (unsigned long int, int, *), -5);
+  CHECK (FN3 (int, unsigned long int, +), -4);
+  CHECK (FN3 (int, unsigned long int, -), -6);
+  CHECK (FN3 (int, unsigned long int, *), -5);
+
+  CHECK (FN4 (char, char, +), 2);
+  CHECK (FN4 (char, char, -), 6);
+  CHECK (FN4 (char, char, *), -8);
+  CHECK (FN4 (unsigned char, unsigned char, +), 2);
+  CHECK (FN4 (unsigned char, unsigned char, -), 6);
+  CHECK (FN4 (unsigned char, unsigned char, *), -8);
+  CHECK (FN4 (short, short, +), 2);
+  CHECK (FN4 (short, short, -), 6);
+  CHECK (FN4 (short, short, *), -8);
+  CHECK (FN4 (unsigned short, unsigned short, +), 2);
+  CHECK (FN4 (unsigned short, unsigned short, -), 6);
+  CHECK (FN4 (unsigned short, unsigned short, *), -8);
+  CHECK (FN4 (int, int, +), 2);
+  CHECK (FN4 (int, int, -), 6);
+  CHECK (FN4 (int, int, *), -8);
+  CHECK (FN4 (unsigned int, unsigned int, +), 2);
+  CHECK (FN4 (unsigned int, unsigned int, -), 6);
+  CHECK (FN4 (unsigned int, unsigned int, *), -8);
+  CHECK (FN4 (long int, long int, +), 2);
+  CHECK (FN4 (long int, long int, -), 6);
+  CHECK (FN4 (long int, long int, *), -8);
+  CHECK (FN4 (unsigned long int, unsigned long int, +), 2);
+  CHECK (FN4 (unsigned long int, unsigned long int, -), 6);
+  CHECK (FN4 (unsigned long int, unsigned long int, *), -8);
+  CHECK (FN4 (long long int, long int, +), 2);
+  CHECK (FN4 (long long int, long int, -), 6);
+  CHECK (FN4 (long long int, long int, *), -8);
+  CHECK (FN4 (unsigned long long int, unsigned long long int, +), 2);
+  CHECK (FN4 (unsigned long long int, unsigned long long int, -), 6);
+  CHECK (FN4 (unsigned long long int, unsigned long long int, *), -8);
+  CHECK (FN4 (int, unsigned char, +), 2);
+  CHECK (FN4 (int, unsigned char, -), 6);
+  CHECK (FN4 (int, unsigned char, *), -8);
+  CHECK (FN4 (unsigned char, int, +), 2);
+  CHECK (FN4 (unsigned char, int, -), 6);
+  CHECK (FN4 (unsigned char, int, *), -8);
+  CHECK (FN4 (int, long int, +), 2);
+  CHECK (FN4 (int, long int, -), 6);
+  CHECK (FN4 (int, long int, *), -8);
+  CHECK (FN4 (long int, int, +), 2);
+  CHECK (FN4 (long int, int, -), 6);
+  CHECK (FN4 (long int, int, *), -8);
+  CHECK (FN4 (unsigned int, int, +), 2);
+  CHECK (FN4 (unsigned int, int, -), 6);
+  CHECK (FN4 (unsigned int, int, *), -8);
+  CHECK (FN4 (int, unsigned int, +), 2);
+  CHECK (FN4 (int, unsigned int, -), 6);
+  CHECK (FN4 (int, unsigned int, *), -8);
+  CHECK (FN4 (unsigned long int, int, +), 2);
+  CHECK (FN4 (unsigned long int, int, -), 6);
+  CHECK (FN4 (unsigned long int, int, *), -8);
+  CHECK (FN4 (int, unsigned long int, +), 2);
+  CHECK (FN4 (int, unsigned long int, -), 6);
+  CHECK (FN4 (int, unsigned long int, *), -8);
+
+  CHECK (FN5 (char), -77);
+  CHECK (FN5 (unsigned char), -77);
+  CHECK (FN5 (short), -77);
+  CHECK (FN5 (unsigned short), -77);
+  CHECK (FN5 (int), -77);
+  CHECK (FN5 (unsigned int), -77);
+  CHECK (FN5 (long int), -77);
+  CHECK (FN5 (unsigned long int), -77);
+  CHECK (FN5 (long long int), -77);
+  CHECK (FN5 (unsigned long long int), -77);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c b/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c
new file mode 100644
index 00000000000..1f448dae3b5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c
@@ -0,0 +1,11 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unreachable" } */
+/* { dg-shouldfail "ubsan" } */
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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"))))
+main (void)
+{
+  __builtin_unreachable ();
+}
+ /* { dg-output "execution reached a __builtin_unreachable\\(\\) call" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/vla-5.c b/gcc/testsuite/c-c++-common/ubsan/vla-5.c
new file mode 100644
index 00000000000..e7f0c3a7273
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/vla-5.c
@@ -0,0 +1,14 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover=vla-bound" } */
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,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"))))
+main (void)
+{
+  int x = 1;
+  /* Check that the size of an array is evaluated only once.  */
+  int a[++x];
+  if (x != 2)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ubsan/return-8.C b/gcc/testsuite/g++.dg/ubsan/return-8.C
new file mode 100644
index 00000000000..10b3c5880be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/return-8.C
@@ -0,0 +1,29 @@ 
+// { dg-do run }
+// { dg-options "-fsanitize=return" }
+// { dg-shouldfail "ubsan" }
+
+struct S { S (); ~S (); };
+
+S::S () {}
+S::~S () {}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,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"))))
+foo (int x)
+{
+  S a;
+  {
+    S b;
+    if (x)
+      return 1;
+  }
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,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"))))
+main ()
+{
+  foo (0);
+}
+
+// { dg-output "execution reached the end of a value-returning function without returning a value" }
diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-12.C b/gcc/testsuite/g++.dg/ubsan/vptr-12.C
new file mode 100644
index 00000000000..9d4c898b4ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/vptr-12.C
@@ -0,0 +1,185 @@ 
+// { dg-do run { target { ilp32 || lp64 } } }
+// { dg-options "-fsanitize=vptr" }
+
+struct S
+{
+  S() : a(0) {}
+  ~S() {}
+  int a;
+  int f() { return 0; }
+  virtual int v() { return 0; }
+};
+
+struct T : S
+{
+  T() : b(0) {}
+  int b;
+  int g() { return 0; }
+  virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } }; // { dg-warning "direct base .S. inaccessible in .U. due to ambiguity" }
+struct V : S {};
+
+void
+foo ()
+{
+  T t;
+  (void)t.a;
+  (void)t.b;
+  (void)t.f();
+  (void)t.g();
+  (void)t.v();
+  (void)t.S::v();
+
+  U u;
+  (void)u.T::a;
+  (void)u.b;
+  (void)u.T::f();
+  (void)u.g();
+  (void)u.v();
+  (void)u.T::v();
+  (void)((T&)u).S::v();
+}
+
+T *x;
+
+__attribute__((noinline, noclone, no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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")))) int
+bar (T *p, int q)
+{
+  switch (q)
+    {
+    // These shouldn't fail:
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+      {
+	T &r = *p;
+	break;
+      }
+    case 0x21:
+    case 0x31:
+      return p->b;
+    case 0x22:
+    case 0x32:
+      return p->g ();
+    case 0x23:
+    case 0x33:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    case 0x44:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // These should:
+    case 0x11:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-12.C:75:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x12:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-12.C:82:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x13:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-12.C:89:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x34:
+      return reinterpret_cast<U*>(p)->v() - 2;
+    // { dg-output "\[^\n\r]*vptr-12.C:97:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'U'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 16 within object of type 'U'(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 8 within object of type 'U'(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^                                                 ~~~~~~~~~~~~~~~~~~~~~~~(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "                                                                vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^                        ~~~~~~~~~~~(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "                                       vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    case 0x41:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-12.C:107:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x42:
+      return p->g ();
+    // { dg-output "\[^\n\r]*vptr-12.C:114:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x43:
+      x = static_cast<T*>(reinterpret_cast<S*>(p));
+      break;
+    // { dg-output "\[^\n\r]*vptr-12.C:121:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  .. .. .. .. .. .. .. ..  \[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+    // { dg-output "              vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" }
+    case 0x51:
+      return p->b;
+    // { dg-output "\[^\n\r]*vptr-12.C:129:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" }
+    // { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+    // { dg-output " .. .. .. ..  00 00 00 00 00 00 00 00  \[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "              \\^~~~~~~~~~~~~~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target lp64 } }
+    // { dg-output "  ?.. .. .. ..  ?00 00 00 00  ?.. .. .. ..  ?\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } }
+    // { dg-output "              invalid vptr" }
+    }
+  return 0;
+}
+
+char b[sizeof (U)] __attribute__((aligned (__alignof__ (U)))) = {};
+
+__attribute__((noinline, noclone, no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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")))) void
+baz (int q)
+{
+  T *p = 0;
+  S *s = 0;
+  U *u = 0;
+  switch (q)
+    {
+    case 0x10: case 0x11: case 0x12: case 0x13:
+      s = new S;
+      bar (reinterpret_cast<T *>(s), q);
+      delete s;
+      break;
+    case 0x20: case 0x21: case 0x22: case 0x23:
+      p = new T;
+      bar (p, q);
+      delete p;
+      break;
+    case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+      u = new U;
+      bar (u, q);
+      delete u;
+      break;
+    case 0x40: case 0x41: case 0x42: case 0x43: case 0x44:
+      u = new U;
+      bar (reinterpret_cast<T *>(u), q);
+      delete u;
+      break;
+    case 0x51:
+      p = reinterpret_cast<T*>(b);
+      bar (p, q);
+      break;
+    }
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,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"))))
+main ()
+{
+  foo ();
+  for (int q = 0; q < 0x52; q++)
+    baz (q);
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/bounds-4.c b/gcc/testsuite/gcc.dg/ubsan/bounds-4.c
new file mode 100644
index 00000000000..03e0b8134f7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/bounds-4.c
@@ -0,0 +1,20 @@ 
+/* PR sanitizer/65280 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+void
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+foo (int n, int (*b)[n])
+{
+  (*b)[n] = 1;
+}
+
+int
+__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr"))))
+main ()
+{
+  int a[20];
+  foo (3, (int (*)[3]) &a);
+}
+
+/* { dg-output "index 3 out of bounds for type 'int \\\[\\\*\\\]'" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c
new file mode 100644
index 00000000000..3f2813ecbe7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c
@@ -0,0 +1,11 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -w -std=c99" } */
+
+int
+__attribute__((no_sanitize(("shift-exponent,integer-divide-by-zero,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"))))
+main (void)
+{
+  int a = -42;
+  a << 1;
+}
+/* { dg-output "left shift of negative value -42" } */
diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c
new file mode 100644
index 00000000000..d62e9124aac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c
@@ -0,0 +1,12 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift-exponent -w -std=c99" } */
+
+int
+__attribute__((no_sanitize(("shift-base,integer-divide-by-zero,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"))))
+main (void)
+{
+  int b = 43;
+  volatile int c = 129;
+  b << c;
+}
+/* { dg-output "shift exponent 129 is too large for" } */
diff --git a/gcc/tree.c b/gcc/tree.c
index 2a603866522..1420c3e4dc7 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -14264,6 +14264,23 @@  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)
+{
+  if (fn == NULL)
+    return false;
+
+  unsigned int result_flags = flag_sanitize & flag;
+  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 62cd7bb19c3..a9185a9e29d 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4217,6 +4217,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 317bf6d2dac..4832b3387a0 100644
--- a/gcc/tsan.c
+++ b/gcc/tsan.c
@@ -881,11 +881,9 @@  public:
 
   /* opt_pass methods: */
   opt_pass * clone () { return new pass_tsan (m_ctxt); }
-  virtual bool gate (function *)
+  virtual bool gate (function *fn)
 {
-  return ((flag_sanitize & SANITIZE_THREAD) != 0
-	  && !lookup_attribute ("no_sanitize_thread",
-                                DECL_ATTRIBUTES (current_function_decl)));
+  return sanitize_flags_p (SANITIZE_THREAD, fn->decl);
 }
 
   virtual unsigned int execute (function *) { return tsan_pass (); }
@@ -923,11 +921,9 @@  public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *)
+  virtual bool gate (function *fn)
     {
-      return ((flag_sanitize & SANITIZE_THREAD) != 0 && !optimize
-	      && !lookup_attribute ("no_sanitize_thread",
-				    DECL_ATTRIBUTES (current_function_decl)));
+      return (sanitize_flags_p (SANITIZE_THREAD, fn->decl) && !optimize);
     }
 
   virtual unsigned int execute (function *) { return tsan_pass (); }
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index d5422a1283d..8a6ab13da8b 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -754,7 +754,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)
     {
@@ -1178,13 +1178,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)))
@@ -1350,13 +1350,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))
@@ -1915,16 +1916,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 =
@@ -1948,15 +1939,14 @@  public:
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *)
+  virtual bool gate (function *fn)
     {
-      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), fn->decl);
     }
 
   virtual unsigned int execute (function *);
@@ -1983,11 +1973,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);
@@ -1995,14 +1985,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))
 	    {
@@ -2010,14 +2000,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 c4ebec946a6..bdfaccf74db 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.11.0