From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2153) id A0A5E3858C50; Sat, 18 Jun 2022 09:11:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A0A5E3858C50 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jakub Jelinek To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-1164] ubsan: Add -fsanitize-trap= support X-Act-Checkin: gcc X-Git-Author: Jakub Jelinek X-Git-Refname: refs/heads/master X-Git-Oldrev: ef662120177d39af5f88ffc622d90bb6ae0ca1d3 X-Git-Newrev: 2c7cfc7b418564a2f1f0e7a5b38dec7013ba5e18 Message-Id: <20220618091124.A0A5E3858C50@sourceware.org> Date: Sat, 18 Jun 2022 09:11:24 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 Jun 2022 09:11:24 -0000 https://gcc.gnu.org/g:2c7cfc7b418564a2f1f0e7a5b38dec7013ba5e18 commit r13-1164-g2c7cfc7b418564a2f1f0e7a5b38dec7013ba5e18 Author: Jakub Jelinek Date: Sat Jun 18 11:09:48 2022 +0200 ubsan: Add -fsanitize-trap= support On Thu, Jun 16, 2022 at 09:32:02PM +0100, Jonathan Wakely wrote: > It looks like clang has addressed this deficiency now: > > https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#usage Thanks, that is roughly what I'd implement anyway and apparently they have it already since 2015, we've added the -fsanitize-undefined-trap-on-error support back in 2014 and didn't change it since then. As a small divergence from clang, I chose -fsanitize-undefined-trap-on-error to be a (deprecated) alias for -fsanitize-trap aka -fsanitize-trap=all rather thn -fsanitize-trap=undefined which seems to be what clang does, because for a deprecated option it is IMHO more important backwards compatibility with what gcc did over the past 8 years rather than clang compatibility. Some sanitizers (e.g. asan, lsan, tsan) don't support traps, -fsanitize-trap=address etc. will be rejected (if enabled at the end of command line), -fno-sanitize-trap= can be specified even for them. This is similar behavior to -fsanitize-recover=. One complication is vptr sanitization, which can't easily trap, as the whole slow path of the checking is inside of libubsan. Previously, -fsanitize=vptr -fsanitize-undefined-trap-on-error silently ignored vptr sanitization. This patch similarly to what clang does will accept -fsanitize-trap=all or -fsanitize-trap=undefined which enable the vptr bit as trapping and again that causes silent disabling of vptr sanitization, while -fsanitize-trap=vptr is rejected (already during option processing). 2022-06-18 Jakub Jelinek gcc/ * common.opt (flag_sanitize_trap): New variable. (fsanitize-trap=, fsanitize-trap): New options. (fsanitize-undefined-trap-on-error): Change into deprecated alias for -fsanitize-trap=all. * opts.h (struct sanitizer_opts_s): Add can_trap member. * opts.cc (finish_options): Complain about unsupported -fsanitize-trap= options. (sanitizer_opts): Add can_trap values to all entries. (get_closest_sanitizer_option): Ignore -fsanitize-trap= options which have can_trap false. (parse_sanitizer_options): Add support for -fsanitize-trap=. For -fsanitize-trap=all, enable SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT. Disallow -fsanitize-trap=vptr here. (common_handle_option): Handle OPT_fsanitize_trap_ and OPT_fsanitize_trap. * sanopt.cc (maybe_optimize_ubsan_null_ifn): Check flag_sanitize_trap & SANITIZE_{NULL,ALIGNMENT} instead of flag_sanitize_undefined_trap_on_error. * gcc.cc (sanitize_spec_function): Use flag_sanitize & ~flag_sanitize_trap instead of flag_sanitize and drop use of flag_sanitize_undefined_trap_on_error in "undefined" handling. * ubsan.cc (ubsan_instrument_unreachable): Use flag_sanitize_trap & SANITIZE_??? instead of flag_sanitize_undefined_trap_on_error. (ubsan_expand_bounds_ifn, ubsan_expand_null_ifn, ubsan_expand_objsize_ifn, ubsan_expand_ptr_ifn, ubsan_build_overflow_builtin, instrument_bool_enum_load, ubsan_instrument_float_cast, instrument_nonnull_arg, instrument_nonnull_return, instrument_builtin): Likewise. * doc/invoke.texi (-fsanitize-trap=, -fsanitize-trap): Document. (-fsanitize-undefined-trap-on-error): Document as deprecated alias of -fsanitize-trap. gcc/c-family/ * c-ubsan.cc (ubsan_instrument_division, ubsan_instrument_shift): Use flag_sanitize_trap & SANITIZE_??? instead of flag_sanitize_undefined_trap_on_error. If 2 sanitizers are involved and flag_sanitize_trap differs for them, emit __builtin_trap only for the comparison where trap is requested. (ubsan_instrument_vla, ubsan_instrument_return): Use lag_sanitize_trap & SANITIZE_??? instead of flag_sanitize_undefined_trap_on_error. gcc/cp/ * cp-ubsan.cc (cp_ubsan_instrument_vptr_p): Use flag_sanitize_trap & SANITIZE_VPTR instead of flag_sanitize_undefined_trap_on_error. gcc/testsuite/ * c-c++-common/ubsan/nonnull-4.c: Use -fsanitize-trap=all instead of -fsanitize-undefined-trap-on-error. * c-c++-common/ubsan/div-by-zero-4.c: Use -fsanitize-trap=signed-integer-overflow instead of -fsanitize-undefined-trap-on-error. * c-c++-common/ubsan/overflow-add-4.c: Use -fsanitize-trap=undefined instead of -fsanitize-undefined-trap-on-error. * c-c++-common/ubsan/pr56956.c: Likewise. * c-c++-common/ubsan/pr68142.c: Likewise. * c-c++-common/ubsan/pr80932.c: Use -fno-sanitize-trap=all -fsanitize-trap=shift,undefined instead of -fsanitize-undefined-trap-on-error. * c-c++-common/ubsan/align-8.c: Use -fsanitize-trap=alignment instead of -fsanitize-undefined-trap-on-error. Diff: --- gcc/c-family/c-ubsan.cc | 116 +++++++++++++-------- gcc/common.opt | 15 ++- gcc/cp/cp-ubsan.cc | 2 +- gcc/doc/invoke.texi | 35 ++++++- gcc/gcc.cc | 5 +- gcc/opts.cc | 119 +++++++++++++++------- gcc/opts.h | 1 + gcc/sanopt.cc | 4 +- gcc/testsuite/c-c++-common/ubsan/align-8.c | 2 +- gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c | 2 +- gcc/testsuite/c-c++-common/ubsan/nonnull-4.c | 2 +- gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c | 2 +- gcc/testsuite/c-c++-common/ubsan/pr56956.c | 2 +- gcc/testsuite/c-c++-common/ubsan/pr68142.c | 2 +- gcc/testsuite/c-c++-common/ubsan/pr80932.c | 2 +- gcc/ubsan.cc | 24 +++-- 16 files changed, 226 insertions(+), 109 deletions(-) diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc index a2cd8fb3262..360ba82250c 100644 --- a/gcc/c-family/c-ubsan.cc +++ b/gcc/c-family/c-ubsan.cc @@ -83,8 +83,9 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) x = NULL_TREE; flag = SANITIZE_SI_OVERFLOW; } - else if (flag_sanitize_undefined_trap_on_error - || (((flag_sanitize_recover & SANITIZE_DIVIDE) == 0) + else if ((((flag_sanitize_trap & SANITIZE_DIVIDE) == 0) + == ((flag_sanitize_trap & SANITIZE_SI_OVERFLOW) == 0)) + && (((flag_sanitize_recover & SANITIZE_DIVIDE) == 0) == ((flag_sanitize_recover & SANITIZE_SI_OVERFLOW) == 0))) { t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, x); @@ -105,7 +106,7 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) make sure it gets evaluated before the condition. */ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t); t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op1), t); - if (flag_sanitize_undefined_trap_on_error) + if ((flag_sanitize_trap & flag) && x == NULL_TREE) tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -113,25 +114,41 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) ubsan_type_descriptor (type), NULL_TREE, NULL_TREE); data = build_fold_addr_expr_loc (loc, data); - enum built_in_function bcode - = (flag_sanitize_recover & flag) - ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW - : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; - tt = builtin_decl_explicit (bcode); - op0 = unshare_expr (op0); - op1 = unshare_expr (op1); - tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), - ubsan_encode_value (op1)); - if (x) + if (flag_sanitize_trap & flag) + tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), + 0); + else { - bcode = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW) - ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW - : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; - tree xt = builtin_decl_explicit (bcode); + enum built_in_function bcode + = (flag_sanitize_recover & flag) + ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; + tt = builtin_decl_explicit (bcode); op0 = unshare_expr (op0); op1 = unshare_expr (op1); - xt = build_call_expr_loc (loc, xt, 3, data, ubsan_encode_value (op0), + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), ubsan_encode_value (op1)); + } + if (x) + { + tree xt; + if (flag_sanitize_trap & SANITIZE_SI_OVERFLOW) + xt = build_call_expr_loc (loc, + builtin_decl_explicit (BUILT_IN_TRAP), + 0); + else + { + enum built_in_function bcode + = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW) + ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; + xt = builtin_decl_explicit (bcode); + op0 = unshare_expr (op0); + op1 = unshare_expr (op1); + xt = build_call_expr_loc (loc, xt, 3, data, + ubsan_encode_value (op0), + ubsan_encode_value (op1)); + } x = fold_build3 (COND_EXPR, void_type_node, x, xt, void_node); } } @@ -225,8 +242,9 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, } else { - if (flag_sanitize_undefined_trap_on_error - || ((!(flag_sanitize_recover & SANITIZE_SHIFT_EXPONENT)) + if (((!(flag_sanitize_trap & SANITIZE_SHIFT_EXPONENT)) + == (!(flag_sanitize_trap & SANITIZE_SHIFT_BASE))) + && ((!(flag_sanitize_recover & SANITIZE_SHIFT_EXPONENT)) == (!(flag_sanitize_recover & SANITIZE_SHIFT_BASE)))) t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt); else @@ -234,7 +252,7 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, } } - if (flag_sanitize_undefined_trap_on_error) + if ((flag_sanitize_trap & recover_kind) && else_t == void_node) tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -244,26 +262,40 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, NULL_TREE); data = build_fold_addr_expr_loc (loc, data); - enum built_in_function bcode - = (flag_sanitize_recover & recover_kind) - ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS - : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; - tt = builtin_decl_explicit (bcode); - op0 = unshare_expr (op0); - op1 = unshare_expr (op1); - tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), - ubsan_encode_value (op1)); + if (flag_sanitize_trap & recover_kind) + tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + enum built_in_function bcode + = (flag_sanitize_recover & recover_kind) + ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS + : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; + tt = builtin_decl_explicit (bcode); + op0 = unshare_expr (op0); + op1 = unshare_expr (op1); + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), + ubsan_encode_value (op1)); + } if (else_t != void_node) { - bcode = (flag_sanitize_recover & SANITIZE_SHIFT_BASE) + tree else_tt; + if (flag_sanitize_trap & SANITIZE_SHIFT_BASE) + else_tt + = build_call_expr_loc (loc, + builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + enum built_in_function bcode + = (flag_sanitize_recover & SANITIZE_SHIFT_BASE) ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; - tree else_tt = builtin_decl_explicit (bcode); - op0 = unshare_expr (op0); - op1 = unshare_expr (op1); - else_tt = build_call_expr_loc (loc, else_tt, 3, data, - ubsan_encode_value (op0), - ubsan_encode_value (op1)); + else_tt = builtin_decl_explicit (bcode); + op0 = unshare_expr (op0); + op1 = unshare_expr (op1); + else_tt = build_call_expr_loc (loc, else_tt, 3, data, + ubsan_encode_value (op0), + ubsan_encode_value (op1)); + } else_t = fold_build3 (COND_EXPR, void_type_node, else_t, else_tt, void_node); } @@ -282,7 +314,7 @@ ubsan_instrument_vla (location_t loc, tree size) tree t, tt; t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0)); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_VLA) tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -307,10 +339,10 @@ ubsan_instrument_vla (location_t loc, tree size) tree ubsan_instrument_return (location_t loc) { - if (flag_sanitize_undefined_trap_on_error) - return build_call_expr_loc - /* pass_warn_function_return checks for BUILTINS_LOCATION. */ - (BUILTINS_LOCATION, builtin_decl_explicit (BUILT_IN_TRAP), 0); + if (flag_sanitize_trap & SANITIZE_RETURN) + /* pass_warn_function_return checks for BUILTINS_LOCATION. */ + return build_call_expr_loc (BUILTINS_LOCATION, + builtin_decl_explicit (BUILT_IN_TRAP), 0); tree data = ubsan_create_data ("__ubsan_missing_return_data", 1, &loc, NULL_TREE, NULL_TREE); diff --git a/gcc/common.opt b/gcc/common.opt index 8e961f16b0e..32917aafcae 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -223,6 +223,10 @@ unsigned int flag_sanitize Variable unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS | SANITIZE_KERNEL_HWADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN) +; What sanitizers should use __builtin_trap () instead of runtime diagnostics +Variable +unsigned int flag_sanitize_trap + ; Flag whether a prefix has been added to dump_base_name Variable bool dump_base_name_prefixed = false @@ -1105,12 +1109,19 @@ fsanitize-recover Common This switch is deprecated; use -fsanitize-recover= instead. +fsanitize-trap= +Common Driver Joined +Use traps instead of diagnostics of undefined behavior sanitizers. + +fsanitize-trap +Common Driver + fsanitize-address-use-after-scope Common Driver Var(flag_sanitize_address_use_after_scope) Init(0) fsanitize-undefined-trap-on-error -Common Driver Var(flag_sanitize_undefined_trap_on_error) Init(0) -Use trap instead of a library function for undefined behavior sanitization. +Common Driver Alias(fsanitize-trap) +This switch is deprecated; use -fsanitize-trap= instead. fasynchronous-unwind-tables Common Var(flag_asynchronous_unwind_tables) Optimization diff --git a/gcc/cp/cp-ubsan.cc b/gcc/cp/cp-ubsan.cc index 2532521bfaf..edad2bd68db 100644 --- a/gcc/cp/cp-ubsan.cc +++ b/gcc/cp/cp-ubsan.cc @@ -32,7 +32,7 @@ along with GCC; see the file COPYING3. If not see static bool cp_ubsan_instrument_vptr_p (tree type) { - if (!flag_rtti || flag_sanitize_undefined_trap_on_error) + if (!flag_rtti || (flag_sanitize_trap & SANITIZE_VPTR)) return false; if (!sanitize_flags_p (SANITIZE_VPTR)) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 60b7b5a26bb..50f57877477 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -609,6 +609,7 @@ Objective-C and Objective-C++ Dialects}. -fprofile-exclude-files=@var{regex} @gol -fprofile-reproducible=@r{[}multithreaded@r{|}parallel-runs@r{|}serial@r{]} @gol -fsanitize=@var{style} -fsanitize-recover -fsanitize-recover=@var{style} @gol +-fsanitize-trap -fsanitize-trap=@var{style} @gol -fasan-shadow-offset=@var{number} -fsanitize-sections=@var{s1},@var{s2},... @gol -fsanitize-undefined-trap-on-error -fbounds-check @gol -fcf-protection=@r{[}full@r{|}branch@r{|}return@r{|}none@r{|}check@r{]} @gol @@ -16116,13 +16117,37 @@ undefined,float-cast-overflow,float-divide-by-zero,bounds-strict Enable sanitization of local variables to detect use-after-scope bugs. The option sets @option{-fstack-reuse} to @samp{none}. +@item -fsanitize-trap@r{[}=@var{opts}@r{]} +@opindex fsanitize-trap +@opindex fno-sanitize-trap +The @option{-fsanitize-trap=} option instructs the compiler to +report for sanitizers mentioned in comma-separated list of @var{opts} +undefined behavior using @code{__builtin_trap} rather than a @code{libubsan} +library routine. If this option is enabled for certain sanitizer, +it takes precedence over the @option{-fsanitizer-recover=} for that +sanitizer, @code{__builtin_trap} will be emitted and be fatal regardless +of whether recovery is enabled or disabled using @option{-fsanitize-recover=}. + +The advantage of this is that the @code{libubsan} library is not needed +and is not linked in, so this is usable even in freestanding environments. + +Currently this feature works with @option{-fsanitize=undefined} (and its suboptions +except for @option{-fsanitize=vptr}), @option{-fsanitize=float-cast-overflow}, +@option{-fsanitize=float-divide-by-zero} and +@option{-fsanitize=bounds-strict}. @code{-fsanitize-trap=all} can be also +specified, which enables it for @code{undefined} suboptions, +@option{-fsanitize=float-cast-overflow}, +@option{-fsanitize=float-divide-by-zero} and +@option{-fsanitize=bounds-strict}. +If @code{-fsanitize-trap=undefined} or @code{-fsanitize-trap=all} is used +and @code{-fsanitize=vptr} is enabled on the command line, the +instrumentation is silently ignored as the instrumentation always needs +@code{libubsan} support, @option{-fsanitize-trap=vptr} is not allowed. + @item -fsanitize-undefined-trap-on-error @opindex fsanitize-undefined-trap-on-error -The @option{-fsanitize-undefined-trap-on-error} option instructs the compiler to -report undefined behavior using @code{__builtin_trap} rather than -a @code{libubsan} library routine. The advantage of this is that the -@code{libubsan} library is not needed and is not linked in, so this -is usable even in freestanding environments. +The @option{-fsanitize-undefined-trap-on-error} option is deprecated +equivalent of @option{-fsanitize-trap=all}. @item -fsanitize-coverage=trace-pc @opindex fsanitize-coverage=trace-pc diff --git a/gcc/gcc.cc b/gcc/gcc.cc index 563f535d564..5cbb38560b2 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -10313,8 +10313,9 @@ sanitize_spec_function (int argc, const char **argv) return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL; if (strcmp (argv[0], "undefined") == 0) return ((flag_sanitize - & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT)) - && !flag_sanitize_undefined_trap_on_error) ? "" : NULL; + & ~flag_sanitize_trap + & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT))) + ? "" : NULL; if (strcmp (argv[0], "leak") == 0) return ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_LEAK | SANITIZE_THREAD)) diff --git a/gcc/opts.cc b/gcc/opts.cc index 55859f549ba..959d48d173f 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1232,6 +1232,18 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, error_at (loc, "%<-fsanitize-recover=%s%> is not supported", sanitizer_opts[i].name); + /* Check -fsanitize-trap option. */ + for (int i = 0; sanitizer_opts[i].name != NULL; ++i) + if ((opts->x_flag_sanitize_trap & sanitizer_opts[i].flag) + && !sanitizer_opts[i].can_trap + /* Allow -fsanitize-trap=all or -fsanitize-trap=undefined + to set flag_sanitize_trap & SANITIZE_VPTR bit which will + effectively disable -fsanitize=vptr, just disallow + explicit -fsanitize-trap=vptr. */ + && sanitizer_opts[i].flag != SANITIZE_VPTR) + error_at (loc, "%<-fsanitize-trap=%s%> is not supported", + sanitizer_opts[i].name); + /* When instrumenting the pointers, we don't want to remove the null pointer checks. */ if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE @@ -2020,48 +2032,50 @@ enable_fdo_optimizations (struct gcc_options *opts, /* -f{,no-}sanitize{,-recover}= suboptions. */ const struct sanitizer_opts_s sanitizer_opts[] = { -#define SANITIZER_OPT(name, flags, recover) \ - { #name, flags, sizeof #name - 1, recover } - SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true), +#define SANITIZER_OPT(name, flags, recover, trap) \ + { #name, flags, sizeof #name - 1, recover, trap } + SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true, + false), SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS), - true), + true, false), SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS), - true), + true, false), SANITIZER_OPT (kernel-hwaddress, (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS), + true, false), + SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true, false), + SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true, false), + SANITIZER_OPT (thread, SANITIZE_THREAD, false, false), + SANITIZER_OPT (leak, SANITIZE_LEAK, false, false), + SANITIZER_OPT (shift, SANITIZE_SHIFT, true, true), + SANITIZER_OPT (shift-base, SANITIZE_SHIFT_BASE, true, true), + SANITIZER_OPT (shift-exponent, SANITIZE_SHIFT_EXPONENT, true, true), + SANITIZER_OPT (integer-divide-by-zero, SANITIZE_DIVIDE, true, true), + SANITIZER_OPT (undefined, SANITIZE_UNDEFINED, true, true), + SANITIZER_OPT (unreachable, SANITIZE_UNREACHABLE, false, true), + SANITIZER_OPT (vla-bound, SANITIZE_VLA, true, true), + SANITIZER_OPT (return, SANITIZE_RETURN, false, true), + SANITIZER_OPT (null, SANITIZE_NULL, true, true), + SANITIZER_OPT (signed-integer-overflow, SANITIZE_SI_OVERFLOW, true, true), + SANITIZER_OPT (bool, SANITIZE_BOOL, true, true), + SANITIZER_OPT (enum, SANITIZE_ENUM, true, true), + SANITIZER_OPT (float-divide-by-zero, SANITIZE_FLOAT_DIVIDE, true, true), + SANITIZER_OPT (float-cast-overflow, SANITIZE_FLOAT_CAST, true, true), + SANITIZER_OPT (bounds, SANITIZE_BOUNDS, true, true), + SANITIZER_OPT (bounds-strict, SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT, true, true), - SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true), - SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true), - SANITIZER_OPT (thread, SANITIZE_THREAD, false), - SANITIZER_OPT (leak, SANITIZE_LEAK, false), - SANITIZER_OPT (shift, SANITIZE_SHIFT, true), - SANITIZER_OPT (shift-base, SANITIZE_SHIFT_BASE, true), - SANITIZER_OPT (shift-exponent, SANITIZE_SHIFT_EXPONENT, 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 (alignment, SANITIZE_ALIGNMENT, true, true), + SANITIZER_OPT (nonnull-attribute, SANITIZE_NONNULL_ATTRIBUTE, true, 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 (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true), - SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true), - SANITIZER_OPT (shadow-call-stack, SANITIZE_SHADOW_CALL_STACK, false), - SANITIZER_OPT (all, ~0U, true), + true, true), + SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE, true, true), + SANITIZER_OPT (vptr, SANITIZE_VPTR, true, false), + SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true, true), + SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true, true), + SANITIZER_OPT (shadow-call-stack, SANITIZE_SHADOW_CALL_STACK, false, false), + SANITIZER_OPT (all, ~0U, true, true), #undef SANITIZER_OPT - { NULL, 0U, 0UL, false } + { NULL, 0U, 0UL, false, false } }; /* -fzero-call-used-regs= suboptions. */ @@ -2114,7 +2128,7 @@ struct edit_distance_traits /* Given ARG, an unrecognized sanitizer option, return the best matching sanitizer option, or NULL if there isn't one. OPTS is array of candidate sanitizer options. - CODE is OPT_fsanitize_ or OPT_fsanitize_recover_. + CODE is OPT_fsanitize_, OPT_fsanitize_recover_ or OPT_fsanitize_trap_. VALUE is non-zero for the regular form of the option, zero for the "no-" form (e.g. "-fno-sanitize-recover="). */ @@ -2139,6 +2153,13 @@ get_closest_sanitizer_option (const string_fragment &arg, && value) continue; + /* For -fsanitize-trap= (and not -fno-sanitize-trap=), + don't offer the non-trapping options. */ + if (code == OPT_fsanitize_trap_ + && !opts[i].can_trap + && value) + continue; + bm.consider (opts[i].name); } return bm.get_best_meaningful_candidate (); @@ -2183,10 +2204,13 @@ parse_sanitizer_options (const char *p, location_t loc, int scode, if (complain) error_at (loc, "%<-fsanitize=all%> option is not valid"); } - else + else if (code == OPT_fsanitize_recover_) flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK | SANITIZE_UNREACHABLE | SANITIZE_RETURN | SANITIZE_SHADOW_CALL_STACK); + else /* if (code == OPT_fsanitize_trap_) */ + flags |= (SANITIZE_UNDEFINED + | SANITIZE_UNDEFINED_NONDEFAULT); } else if (value) { @@ -2197,6 +2221,10 @@ parse_sanitizer_options (const char *p, location_t loc, int scode, && sanitizer_opts[i].flag == SANITIZE_UNDEFINED) flags |= (SANITIZE_UNDEFINED & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN)); + else if (code == OPT_fsanitize_trap_ + && sanitizer_opts[i].flag == SANITIZE_VPTR) + error_at (loc, "%<-fsanitize-trap=%s%> is not supported", + sanitizer_opts[i].name); else flags |= sanitizer_opts[i].flag; } @@ -2215,6 +2243,8 @@ parse_sanitizer_options (const char *p, location_t loc, int scode, const char *suffix; if (code == OPT_fsanitize_recover_) suffix = "-recover"; + else if (code == OPT_fsanitize_trap_) + suffix = "-trap"; else suffix = ""; @@ -2647,6 +2677,12 @@ common_handle_option (struct gcc_options *opts, opts->x_flag_sanitize_recover, value, true); break; + case OPT_fsanitize_trap_: + opts->x_flag_sanitize_trap + = parse_sanitizer_options (arg, loc, code, + opts->x_flag_sanitize_trap, value, true); + break; + case OPT_fasan_shadow_offset_: /* Deferred. */ break; @@ -2665,6 +2701,15 @@ common_handle_option (struct gcc_options *opts, &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); break; + case OPT_fsanitize_trap: + if (value) + opts->x_flag_sanitize_trap + |= (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); + else + opts->x_flag_sanitize_trap + &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); + break; + case OPT_O: case OPT_Os: case OPT_Ofast: diff --git a/gcc/opts.h b/gcc/opts.h index a43ce66cffe..73a96f32350 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -473,6 +473,7 @@ extern const struct sanitizer_opts_s unsigned int flag; size_t len; bool can_recover; + bool can_trap; } sanitizer_opts[]; extern const struct zero_call_used_regs_opts_s diff --git a/gcc/sanopt.cc b/gcc/sanopt.cc index 125e0c936e7..c3187631153 100644 --- a/gcc/sanopt.cc +++ b/gcc/sanopt.cc @@ -392,11 +392,11 @@ maybe_optimize_ubsan_null_ifn (class sanopt_ctx *ctx, gimple *stmt) stmts have same location. */ else if (integer_zerop (align)) remove = (flag_sanitize_recover & SANITIZE_NULL) == 0 - || flag_sanitize_undefined_trap_on_error + || (flag_sanitize_trap & SANITIZE_NULL) != 0 || gimple_location (g) == gimple_location (stmt); else if (tree_int_cst_le (cur_align, align)) remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0 - || flag_sanitize_undefined_trap_on_error + || (flag_sanitize_trap & SANITIZE_ALIGNMENT) != 0 || gimple_location (g) == gimple_location (stmt); if (!remove && gimple_bb (g) == gimple_bb (stmt) diff --git a/gcc/testsuite/c-c++-common/ubsan/align-8.c b/gcc/testsuite/c-c++-common/ubsan/align-8.c index 5fe0e0fe931..53b2b6ceb5f 100644 --- a/gcc/testsuite/c-c++-common/ubsan/align-8.c +++ b/gcc/testsuite/c-c++-common/ubsan/align-8.c @@ -1,6 +1,6 @@ /* Limit this to known non-strict alignment targets. */ /* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */ -/* { dg-options "-O -fsanitize=alignment -fsanitize-undefined-trap-on-error -Wno-address-of-packed-member -fdump-tree-sanopt-details" } */ +/* { dg-options "-O -fsanitize=alignment -fsanitize-trap=alignment -Wno-address-of-packed-member -fdump-tree-sanopt-details" } */ /* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */ /* { dg-shouldfail "ubsan" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c index ef431c93e4e..e2cc9fe587f 100644 --- a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-4.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -fsanitize-undefined-trap-on-error -Wno-overflow" } */ +/* { dg-options "-fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow -Wno-overflow" } */ #define INT_MIN (-__INT_MAX__ - 1) diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c index b49c72e345c..65b6dfb4c81 100644 --- a/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c +++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c @@ -1,6 +1,6 @@ /* { dg-do run } */ /* { dg-shouldfail "ubsan" } */ -/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */ +/* { dg-options "-fsanitize=undefined -fsanitize-trap=all" } */ int q, r; void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h; diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c b/gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c index 3f6f6bd0895..ac9df4d001e 100644 --- a/gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fsanitize-undefined-trap-on-error" } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fsanitize-trap=undefined" } */ /* { dg-shouldfail "ubsan" } */ #define INT_MAX __INT_MAX__ diff --git a/gcc/testsuite/c-c++-common/ubsan/pr56956.c b/gcc/testsuite/c-c++-common/ubsan/pr56956.c index 996e1dd8a79..a3280c2908e 100644 --- a/gcc/testsuite/c-c++-common/ubsan/pr56956.c +++ b/gcc/testsuite/c-c++-common/ubsan/pr56956.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */ +/* { dg-options "-fsanitize=undefined -fsanitize-trap=undefined" } */ unsigned int __attribute__((noinline,noclone)) foo (unsigned int x) diff --git a/gcc/testsuite/c-c++-common/ubsan/pr68142.c b/gcc/testsuite/c-c++-common/ubsan/pr68142.c index 9498f08ae41..13b7ef6a5e3 100644 --- a/gcc/testsuite/c-c++-common/ubsan/pr68142.c +++ b/gcc/testsuite/c-c++-common/ubsan/pr68142.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */ +/* { dg-options "-fsanitize=undefined -fsanitize-trap=undefined" } */ int __attribute__((noinline,noclone)) h(int a) diff --git a/gcc/testsuite/c-c++-common/ubsan/pr80932.c b/gcc/testsuite/c-c++-common/ubsan/pr80932.c index 92903f7e0ee..3ee56c80740 100644 --- a/gcc/testsuite/c-c++-common/ubsan/pr80932.c +++ b/gcc/testsuite/c-c++-common/ubsan/pr80932.c @@ -1,6 +1,6 @@ /* PR sanitizer/80932 */ /* { dg-do run } */ -/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */ +/* { dg-options "-fsanitize=undefined -fno-sanitize-trap=all -fsanitize-trap=shift,undefined" } */ int x = 1; diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc index 6c058142c37..3aa25b534bb 100644 --- a/gcc/ubsan.cc +++ b/gcc/ubsan.cc @@ -647,7 +647,7 @@ ubsan_instrument_unreachable (gimple_stmt_iterator *gsi) gimple *g; location_t loc = gimple_location (gsi_stmt (*gsi)); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_UNREACHABLE) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -719,7 +719,7 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) /* Generate __ubsan_handle_out_of_bounds call. */ *gsi = gsi_after_labels (then_bb); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_BOUNDS) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -827,7 +827,8 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip) set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); /* Put the ubsan builtin call into the newly created BB. */ - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & ((check_align ? SANITIZE_ALIGNMENT + 0 : 0) + | (check_null ? SANITIZE_NULL + 0 : 0))) g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0); else { @@ -997,7 +998,7 @@ ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi) } /* Generate __ubsan_handle_type_mismatch call. */ - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_OBJECT_SIZE) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -1143,7 +1144,7 @@ ubsan_expand_ptr_ifn (gimple_stmt_iterator *gsip) } /* Put the ubsan builtin call into the newly created BB. */ - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_POINTER_OVERFLOW) g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0); else { @@ -1518,7 +1519,7 @@ tree ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype, tree op0, tree op1, tree *datap) { - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_SI_OVERFLOW) return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); tree data; @@ -1741,7 +1742,8 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi) } gsi2 = gsi_after_labels (then_bb); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & (TREE_CODE (type) == BOOLEAN_TYPE + ? SANITIZE_BOOL : SANITIZE_ENUM)) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -1904,7 +1906,7 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr) if (integer_zerop (t)) return NULL_TREE; - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_FLOAT_CAST) fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -1974,7 +1976,7 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi) gsi_insert_after (gsi, g, GSI_NEW_STMT); *gsi = gsi_after_labels (then_bb); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_NONNULL_ATTRIBUTE) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -2030,7 +2032,7 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi) gsi_insert_after (gsi, g, GSI_NEW_STMT); *gsi = gsi_after_labels (then_bb); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_RETURNS_NONNULL_ATTRIBUTE) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else { @@ -2279,7 +2281,7 @@ instrument_builtin (gimple_stmt_iterator *gsi) gsi_insert_after (gsi, g, GSI_NEW_STMT); *gsi = gsi_after_labels (then_bb); - if (flag_sanitize_undefined_trap_on_error) + if (flag_sanitize_trap & SANITIZE_BUILTIN) g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); else {