This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix handling of -fsanitize-recover* options.
On 09/23/2016 02:48 PM, Jakub Jelinek wrote:
> On Fri, Sep 23, 2016 at 01:53:48PM +0200, Martin Liška wrote:
>> 3) -fsanitize=leak is combinable with -fsanitize=address or
>> -fsanitize=thread
>
> Is it really combinable with -fsanitize=thread? I thought only libasan
> or liblsan provides the leak checker. Anyway, I can't find where in the
> patch you change this.
You're right, I also verified clang behavior. Thus I've enhanced documentation
to mention all incompatibilities between sanitizers and new tests are introduced
for that.
>
>> --- a/gcc/flag-types.h
>> +++ b/gcc/flag-types.h
>> @@ -203,25 +203,25 @@ enum vect_cost_model {
>> /* Different instrumentation modes. */
>> enum sanitize_code {
>> /* AddressSanitizer. */
>> - SANITIZE_ADDRESS = 1 << 0,
>> - SANITIZE_USER_ADDRESS = 1 << 1,
>> - SANITIZE_KERNEL_ADDRESS = 1 << 2,
>> + SANITIZE_ADDRESS = 1UL<< 0,
>
> Formatting, space in between UL and << (many times).
Sure, fixed.
>
>> @@ -1516,11 +1518,18 @@ parse_sanitizer_options (const char *p, location_t loc, int scode,
>> error_at (loc, "-fsanitize=all option is not valid");
>> }
>> else
>> - flags |= ~(SANITIZE_USER_ADDRESS | SANITIZE_THREAD
>> - | SANITIZE_LEAK);
>> + flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
>> + | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
>
> This change will turn on -fsanitize-recove=address for -fsanitize-recover=all, right?
> That is quite a significant behavior change, isn't it? Have you checked
> what clang does here?
Yes, I've just followed what we have written in our documentation and clang does
the same.
>
>> }
>> else if (value)
>> - flags |= sanitizer_opts[i].flag;
>> + {
>> + flags |= sanitizer_opts[i].flag;
>> + /* Do not enable -fsanitize-recover=unreachable and
>> + -fsanitize-recover=return if -fsanitize-recover=undefined
>> + is selected. */
>> + if (sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
>> + flags &= ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN);
>
> This looks wrong. If you want to complain about
> -fsanitize-recover=unreachable, the above would be silent about
> -fsanitize-recover=unreachable -fsanitize-recover=undefined.
> Shouldn't it be instead
> if (sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
> flags |= SANITIZE_UNDEFINED & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN);
> else
> flags |= sanitizer_opts[i].flag;
> ?
Yes, my approach was wrong ;) your looks better. I'm adding a new test-case to cover
your sample.
Sending v2 of the patch.
Martin
>
> Jakub
>
>From a0576e31bb6c93ce9253240daf1d8c4187acd66b Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Fri, 23 Sep 2016 10:16:10 +0200
Subject: [PATCH] Fix handling of -fsanitize-recover* options.
gcc/ChangeLog:
2016-09-23 Martin Liska <mliska@suse.cz>
* common.opt: Exclude SANITIZE_UNREACHABLE and SANITIZE_RETURN
from default sanitize recover values.
* doc/invoke.texi: Fix documentation related to -fsanitize=leak,
-fsanitize=address, -fsanitize=thread and -fsanitize-recover.
* flag-types.h: Replace couple of 1 << x to 1UL << x, make it
consistent.
* opts.c (finish_options): Do a generic loop over options
that can be recovered.
(parse_sanitizer_options): Exclude SANITIZE_UNREACHABLE and
SANITIZE_RETURN.
(common_handle_option): Likewise.
* opts.h: Declare can_recover to sanitizer_opts_s.
gcc/testsuite/ChangeLog:
2016-09-26 Martin Liska <mliska@suse.cz>
* c-c++-common/ubsan/sanitize-recover-1.c: New test.
* c-c++-common/ubsan/sanitize-recover-2.c: New test.
* c-c++-common/ubsan/sanitize-recover-3.c: New test.
* c-c++-common/ubsan/sanitize-recover-4.c: New test.
* c-c++-common/ubsan/sanitize-recover-5.c: New test.
* c-c++-common/ubsan/sanitize-recover-6.c: New test.
* c-c++-common/ubsan/sanitize-recover-7.c: New test.
* c-c++-common/ubsan/sanitize-recover-8.c: New test.
* c-c++-common/ubsan/sanitize-recover-9.c: New test.
---
gcc/common.opt | 2 +-
gcc/doc/invoke.texi | 14 ++--
gcc/flag-types.h | 32 ++++----
gcc/opts.c | 91 +++++++++++++---------
gcc/opts.h | 1 +
.../c-c++-common/ubsan/sanitize-recover-1.c | 6 ++
.../c-c++-common/ubsan/sanitize-recover-2.c | 6 ++
.../c-c++-common/ubsan/sanitize-recover-3.c | 4 +
.../c-c++-common/ubsan/sanitize-recover-4.c | 4 +
.../c-c++-common/ubsan/sanitize-recover-5.c | 6 ++
.../c-c++-common/ubsan/sanitize-recover-6.c | 4 +
.../c-c++-common/ubsan/sanitize-recover-7.c | 6 ++
.../c-c++-common/ubsan/sanitize-recover-8.c | 6 ++
.../c-c++-common/ubsan/sanitize-recover-9.c | 6 ++
14 files changed, 128 insertions(+), 60 deletions(-)
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-1.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-2.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-3.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-4.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-5.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-6.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-7.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-8.c
create mode 100644 gcc/testsuite/c-c++-common/ubsan/sanitize-recover-9.c
diff --git a/gcc/common.opt b/gcc/common.opt
index 8c0885c..f35660f 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -228,7 +228,7 @@ unsigned int flag_sanitize
; What sanitizers should recover from errors
Variable
-unsigned int flag_sanitize_recover = SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS
+unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)
fsanitize-coverage=trace-pc
Common Report Var(flag_sanitize_coverage)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d474da6..207d3a2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -10154,6 +10154,7 @@ more details. The run-time behavior can be influenced using the
the available options are shown at startup of the instrumented program. See
@url{https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags}
for a list of supported options.
+The option can't be combined with @option{-fsanitize=thread}.
@item -fsanitize=kernel-address
@opindex fsanitize=kernel-address
@@ -10169,17 +10170,19 @@ details. The run-time behavior can be influenced using the @env{TSAN_OPTIONS}
environment variable; see
@url{https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags} for a list of
supported options.
+The option can't be combined with @option{-fsanitize=address}
+and @option{-fsanitize=leak}.
@item -fsanitize=leak
@opindex fsanitize=leak
Enable LeakSanitizer, a memory leak detector.
-This option only matters for linking of executables and if neither
-@option{-fsanitize=address} nor @option{-fsanitize=thread} is used. In that
-case the executable is linked against a library that overrides @code{malloc}
+This option only matters for linking of executables and
+the executable is linked against a library that overrides @code{malloc}
and other allocator functions. See
@uref{https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer} for more
details. The run-time behavior can be influenced using the
@env{LSAN_OPTIONS} environment variable.
+The option can't be combined with @option{-fsanitize=thread}.
@item -fsanitize=undefined
@opindex fsanitize=undefined
@@ -10353,6 +10356,7 @@ and program then exits with a non-zero exit code.
Currently this feature only works for @option{-fsanitize=undefined} (and its suboptions
except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
@option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
+@option{-fsanitize=bounds-strict},
@option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
for which this feature is experimental.
@@ -10369,12 +10373,12 @@ setting the @code{halt_on_error} flag in the corresponding environment variable.
Syntax without explicit @var{opts} parameter is deprecated. It is equivalent to
@smallexample
--fsanitize-recover=undefined,float-cast-overflow,float-divide-by-zero
+-fsanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
@end smallexample
@noindent
Similarly @option{-fno-sanitize-recover} is equivalent to
@smallexample
--fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero
+-fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero,bounds-strict
@end smallexample
@item -fsanitize-undefined-trap-on-error
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index dd57e16..816df6b 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -203,25 +203,25 @@ enum vect_cost_model {
/* Different instrumentation modes. */
enum sanitize_code {
/* AddressSanitizer. */
- SANITIZE_ADDRESS = 1 << 0,
- SANITIZE_USER_ADDRESS = 1 << 1,
- SANITIZE_KERNEL_ADDRESS = 1 << 2,
+ SANITIZE_ADDRESS = 1UL << 0,
+ SANITIZE_USER_ADDRESS = 1UL << 1,
+ SANITIZE_KERNEL_ADDRESS = 1UL << 2,
/* ThreadSanitizer. */
- SANITIZE_THREAD = 1 << 3,
+ SANITIZE_THREAD = 1UL << 3,
/* LeakSanitizer. */
- SANITIZE_LEAK = 1 << 4,
+ SANITIZE_LEAK = 1UL << 4,
/* UndefinedBehaviorSanitizer. */
- SANITIZE_SHIFT = 1 << 5,
- SANITIZE_DIVIDE = 1 << 6,
- SANITIZE_UNREACHABLE = 1 << 7,
- SANITIZE_VLA = 1 << 8,
- SANITIZE_NULL = 1 << 9,
- SANITIZE_RETURN = 1 << 10,
- SANITIZE_SI_OVERFLOW = 1 << 11,
- SANITIZE_BOOL = 1 << 12,
- SANITIZE_ENUM = 1 << 13,
- SANITIZE_FLOAT_DIVIDE = 1 << 14,
- SANITIZE_FLOAT_CAST = 1 << 15,
+ SANITIZE_SHIFT = 1UL << 5,
+ SANITIZE_DIVIDE = 1UL << 6,
+ SANITIZE_UNREACHABLE = 1UL << 7,
+ SANITIZE_VLA = 1UL << 8,
+ SANITIZE_NULL = 1UL << 9,
+ SANITIZE_RETURN = 1UL << 10,
+ SANITIZE_SI_OVERFLOW = 1UL << 11,
+ SANITIZE_BOOL = 1UL << 12,
+ SANITIZE_ENUM = 1UL << 13,
+ SANITIZE_FLOAT_DIVIDE = 1UL << 14,
+ SANITIZE_FLOAT_CAST = 1UL << 15,
SANITIZE_BOUNDS = 1UL << 16,
SANITIZE_ALIGNMENT = 1UL << 17,
SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18,
diff --git a/gcc/opts.c b/gcc/opts.c
index f625973..132eae7 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -941,7 +941,6 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
opts->x_debug_generate_pub_sections = 2;
/* Userspace and kernel ASan conflict with each other. */
-
if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
&& (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
error_at (loc,
@@ -949,20 +948,23 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set,
"-fsanitize=kernel-address");
/* And with TSan. */
-
if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
&& (opts->x_flag_sanitize & SANITIZE_THREAD))
error_at (loc,
"-fsanitize=address and -fsanitize=kernel-address "
"are incompatible with -fsanitize=thread");
- /* Error recovery is not allowed for LSan and TSan. */
-
- if (opts->x_flag_sanitize_recover & SANITIZE_THREAD)
- error_at (loc, "-fsanitize-recover=thread is not supported");
+ if ((opts->x_flag_sanitize & SANITIZE_LEAK)
+ && (opts->x_flag_sanitize & SANITIZE_THREAD))
+ error_at (loc,
+ "-fsanitize=leak is incompatible with -fsanitize=thread");
- if (opts->x_flag_sanitize_recover & SANITIZE_LEAK)
- error_at (loc, "-fsanitize-recover=leak is not supported");
+ /* Check error recovery for -fsanitize-recover option. */
+ for (int i = 0; sanitizer_opts[i].name != NULL; ++i)
+ if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag)
+ && !sanitizer_opts[i].can_recover)
+ error_at (loc, "-fsanitize-recover=%s is not supported",
+ sanitizer_opts[i].name);
/* When instrumenting the pointers, we don't want to remove
the null pointer checks. */
@@ -1448,33 +1450,36 @@ enable_fdo_optimizations (struct gcc_options *opts,
/* -f{,no-}sanitize{,-recover}= suboptions. */
const struct sanitizer_opts_s sanitizer_opts[] =
{
-#define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
- SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),
- SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS),
- SANITIZER_OPT (thread, SANITIZE_THREAD),
- SANITIZER_OPT (leak, SANITIZE_LEAK),
- SANITIZER_OPT (shift, SANITIZE_SHIFT),
- SANITIZER_OPT (integer-divide-by-zero, SANITIZE_DIVIDE),
- SANITIZER_OPT (undefined, SANITIZE_UNDEFINED),
- SANITIZER_OPT (unreachable, SANITIZE_UNREACHABLE),
- SANITIZER_OPT (vla-bound, SANITIZE_VLA),
- SANITIZER_OPT (return, SANITIZE_RETURN),
- SANITIZER_OPT (null, SANITIZE_NULL),
- SANITIZER_OPT (signed-integer-overflow, SANITIZE_SI_OVERFLOW),
- SANITIZER_OPT (bool, SANITIZE_BOOL),
- SANITIZER_OPT (enum, SANITIZE_ENUM),
- SANITIZER_OPT (float-divide-by-zero, SANITIZE_FLOAT_DIVIDE),
- SANITIZER_OPT (float-cast-overflow, SANITIZE_FLOAT_CAST),
- SANITIZER_OPT (bounds, SANITIZE_BOUNDS),
- SANITIZER_OPT (bounds-strict, SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT),
- SANITIZER_OPT (alignment, SANITIZE_ALIGNMENT),
- SANITIZER_OPT (nonnull-attribute, SANITIZE_NONNULL_ATTRIBUTE),
- SANITIZER_OPT (returns-nonnull-attribute, SANITIZE_RETURNS_NONNULL_ATTRIBUTE),
- SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE),
- SANITIZER_OPT (vptr, SANITIZE_VPTR),
- SANITIZER_OPT (all, ~0U),
+#define SANITIZER_OPT(name, flags, recover) \
+ { #name, flags, sizeof #name - 1, recover }
+ SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS, true),
+ SANITIZER_OPT (kernel-address, SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS,
+ true),
+ SANITIZER_OPT (thread, SANITIZE_THREAD, false),
+ SANITIZER_OPT (leak, SANITIZE_LEAK, false),
+ SANITIZER_OPT (shift, SANITIZE_SHIFT, true),
+ SANITIZER_OPT (integer-divide-by-zero, SANITIZE_DIVIDE, true),
+ SANITIZER_OPT (undefined, SANITIZE_UNDEFINED, true),
+ SANITIZER_OPT (unreachable, SANITIZE_UNREACHABLE, false),
+ SANITIZER_OPT (vla-bound, SANITIZE_VLA, true),
+ SANITIZER_OPT (return, SANITIZE_RETURN, false),
+ SANITIZER_OPT (null, SANITIZE_NULL, true),
+ SANITIZER_OPT (signed-integer-overflow, SANITIZE_SI_OVERFLOW, true),
+ SANITIZER_OPT (bool, SANITIZE_BOOL, true),
+ SANITIZER_OPT (enum, SANITIZE_ENUM, true),
+ SANITIZER_OPT (float-divide-by-zero, SANITIZE_FLOAT_DIVIDE, true),
+ SANITIZER_OPT (float-cast-overflow, SANITIZE_FLOAT_CAST, true),
+ SANITIZER_OPT (bounds, SANITIZE_BOUNDS, true),
+ SANITIZER_OPT (bounds-strict, SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT, true),
+ SANITIZER_OPT (alignment, SANITIZE_ALIGNMENT, true),
+ SANITIZER_OPT (nonnull-attribute, SANITIZE_NONNULL_ATTRIBUTE, true),
+ SANITIZER_OPT (returns-nonnull-attribute, SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+ true),
+ SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE, true),
+ SANITIZER_OPT (vptr, SANITIZE_VPTR, true),
+ SANITIZER_OPT (all, ~0U, true),
#undef SANITIZER_OPT
- { NULL, 0U, 0UL }
+ { NULL, 0U, 0UL, false }
};
/* Parse comma separated sanitizer suboptions from P for option SCODE,
@@ -1516,11 +1521,20 @@ parse_sanitizer_options (const char *p, location_t loc, int scode,
error_at (loc, "-fsanitize=all option is not valid");
}
else
- flags |= ~(SANITIZE_USER_ADDRESS | SANITIZE_THREAD
- | SANITIZE_LEAK);
+ flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
+ | SANITIZE_UNREACHABLE | SANITIZE_RETURN);
}
else if (value)
- flags |= sanitizer_opts[i].flag;
+ {
+ /* Do not enable -fsanitize-recover=unreachable and
+ -fsanitize-recover=return if -fsanitize-recover=undefined
+ is selected. */
+ if (sanitizer_opts[i].flag == SANITIZE_UNDEFINED)
+ flags |= SANITIZE_UNDEFINED
+ & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN);
+ else
+ flags |= sanitizer_opts[i].flag;
+ }
else
flags &= ~sanitizer_opts[i].flag;
found = true;
@@ -1770,7 +1784,8 @@ 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_NONDEFAULT)
+ & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN);
else
opts->x_flag_sanitize_recover
&= ~(SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT);
diff --git a/gcc/opts.h b/gcc/opts.h
index 4132432..b3e6435 100644
--- a/gcc/opts.h
+++ b/gcc/opts.h
@@ -414,6 +414,7 @@ extern const struct sanitizer_opts_s
const char *const name;
unsigned int flag;
size_t len;
+ bool can_recover;
} sanitizer_opts[];
extern void add_misspelling_candidates (auto_vec<char *> *candidates,
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-1.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-1.c
new file mode 100644
index 0000000..4d8c27e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-1.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=unreachable" } */
+
+int i;
+
+/* { dg-error "-fsanitize-recover=unreachable is not supported" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-2.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-2.c
new file mode 100644
index 0000000..e9849bd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-2.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=return" } */
+
+int i;
+
+/* { dg-error "-fsanitize-recover=return is not supported" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-3.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-3.c
new file mode 100644
index 0000000..844f3fd
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-3.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=all" } */
+
+int i;
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-4.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-4.c
new file mode 100644
index 0000000..45fa5b9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-4.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=undefined" } */
+
+int i;
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-5.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-5.c
new file mode 100644
index 0000000..9c1ed32
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-5.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=undefined,unreachable" } */
+
+int i;
+
+/* { dg-error "-fsanitize-recover=unreachable is not supported" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-6.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-6.c
new file mode 100644
index 0000000..e309e1b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-6.c
@@ -0,0 +1,4 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=undefined,unreachable -fno-sanitize-recover=unreachable" } */
+
+int i;
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-7.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-7.c
new file mode 100644
index 0000000..2e6599f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-7.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=address -fsanitize=thread" } */
+
+int i;
+
+/* { dg-error "-fsanitize=address and -fsanitize=kernel-address are incompatible with -fsanitize=thread" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-8.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-8.c
new file mode 100644
index 0000000..ecaf0e6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-8.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=leak -fsanitize=thread" } */
+
+int i;
+
+/* { dg-error "-fsanitize=leak is incompatible with -fsanitize=thread" "" { target *-*-* } 0 } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-9.c b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-9.c
new file mode 100644
index 0000000..ef80ef1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/sanitize-recover-9.c
@@ -0,0 +1,6 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize-recover=unreachable -fsanitize-recover=undefined" } */
+
+int i;
+
+/* { dg-error "-fsanitize-recover=unreachable is not supported" "" { target *-*-* } 0 } */
--
2.9.2