When not optimizing, we can't do anything useful with unreachability in terms of code performance, so we might as well improve debugging by turning __builtin_unreachable into a trap. In the PR richi suggested introducing an -funreachable-traps flag for this, but this functionality is already implemented as -fsanitize=unreachable -fsanitize-undefined-trap-on-error, we just need to set those flags by default.
I think it also makes sense to do this when we're explicitly optimizing for the debugging experience. I then needed to make options-save handle -fsanitize and -fsanitize-undefined-trap-on-error; since -fsanitize is has custom parsing, that meant handling it explicitly in the awk scripts. I also noticed we weren't setting it in opts_set. Do we still want -funreachable-traps as an alias (or separate flag) for this behavior that doesn't mention the sanitizer? Tested x86_64-pc-linux-gnu, OK for trunk? PR c++/104642 gcc/ChangeLog: * doc/invoke.texi (-fsanitize-undefined-trap-on-error): On by default at -O0, implying -fsanitize=unreachable,return * opts.cc (finish_options): At -O0 trap on unreachable code. (common_handle_option): Set opts_set->x_flag_sanitize. * common.opt (fsanitize-undefined-trap-on-error): Add Optimization tag. * optc-save-gen.awk, opth-gen.awk: Include flag_sanitize. gcc/testsuite/ChangeLog: * g++.dg/ubsan/return-8a.C: New test. --- gcc/doc/invoke.texi | 4 ++++ gcc/common.opt | 2 +- gcc/opts.cc | 10 ++++++++++ gcc/testsuite/g++.dg/ubsan/return-8a.C | 17 +++++++++++++++++ gcc/optc-save-gen.awk | 8 ++++++-- gcc/opth-gen.awk | 3 ++- 6 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8a.C diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 174bc09e5cf..446b0691305 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -16100,6 +16100,10 @@ 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. +If @option{-fsanitize} has not been specified, this option implies +@option{-fsanitize=unreachable,return}, and is enabled by default at +@option{-O0} and @option{-Og}. + @item -fsanitize-coverage=trace-pc @opindex fsanitize-coverage=trace-pc Enable coverage-guided fuzzing code instrumentation. diff --git a/gcc/common.opt b/gcc/common.opt index 7ca0cceed82..90e3e84913b 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1109,7 +1109,7 @@ 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) +Common Driver Optimization Var(flag_sanitize_undefined_trap_on_error) Init(0) Use trap instead of a library function for undefined behavior sanitization. fasynchronous-unwind-tables diff --git a/gcc/opts.cc b/gcc/opts.cc index bf06a55456a..3699eabb599 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1122,6 +1122,15 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, opts->x_flag_no_inline = 1; } + /* At -O0 or -Og, turn __builtin_unreachable into a trap. */ + if ((!opts->x_optimize || opts->x_optimize_debug) + && !opts_set->x_flag_sanitize) + SET_OPTION_IF_UNSET (opts, opts_set, + flag_sanitize_undefined_trap_on_error, true); + if (opts->x_flag_sanitize_undefined_trap_on_error) + SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize, + SANITIZE_UNREACHABLE|SANITIZE_RETURN); + /* Pipelining of outer loops is only possible when general pipelining capabilities are requested. */ if (!opts->x_flag_sel_sched_pipelining) @@ -2613,6 +2622,7 @@ common_handle_option (struct gcc_options *opts, break; case OPT_fsanitize_: + opts_set->x_flag_sanitize = true; opts->x_flag_sanitize = parse_sanitizer_options (arg, loc, code, opts->x_flag_sanitize, value, true); diff --git a/gcc/testsuite/g++.dg/ubsan/return-8a.C b/gcc/testsuite/g++.dg/ubsan/return-8a.C new file mode 100644 index 00000000000..9b2265c4bb0 --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8a.C @@ -0,0 +1,17 @@ +// PR c++/104642 + +// At -O0 we default to +// -fsanitize=unreachable,return -fsanitize-undefined-trap-on-error +// so the below should abort at runtime. + +// { dg-do run } +// { dg-shouldfail { *-*-* } } +// { dg-additional-options "-O0" } + +bool b; + +int f() { + if (b) return 42; +} // { dg-warning "-Wreturn-type" } + +int main() { f(); } diff --git a/gcc/optc-save-gen.awk b/gcc/optc-save-gen.awk index 233d1fbb637..38c02bcc2cf 100644 --- a/gcc/optc-save-gen.awk +++ b/gcc/optc-save-gen.awk @@ -84,7 +84,7 @@ print "{"; n_opt_char = 4; n_opt_short = 0; -n_opt_int = 0; +n_opt_int = 1; n_opt_enum = 0; n_opt_string = 0; n_opt_other = 0; @@ -96,6 +96,7 @@ var_opt_range["optimize"] = "0, 255"; var_opt_range["optimize_size"] = "0, 2"; var_opt_range["optimize_debug"] = "0, 1"; var_opt_range["optimize_fast"] = "0, 1"; +var_opt_int[0] = "flag_sanitize"; # Sort by size to mimic how the structure is laid out to be friendlier to the # cache. @@ -1264,7 +1265,7 @@ for (i = 0; i < n_target_str; i++) { } print "}"; -n_opt_val = 4; +n_opt_val = 5; var_opt_val[0] = "x_optimize" var_opt_val_type[0] = "char " var_opt_hash[0] = 1; @@ -1277,6 +1278,9 @@ var_opt_hash[2] = 1; var_opt_val[3] = "x_optimize_fast" var_opt_val_type[3] = "char " var_opt_hash[3] = 1; +var_opt_val[4] = "x_flag_sanitize" +var_opt_val_type[4] = "unsigned int " +var_opt_hash[4] = 1; for (i = 0; i < n_opts; i++) { if (flag_set_p("(Optimization|PerFunction)", flags[i])) { name = var_name(flags[i]) diff --git a/gcc/opth-gen.awk b/gcc/opth-gen.awk index 8bba8ec4549..b3bedaa6da2 100644 --- a/gcc/opth-gen.awk +++ b/gcc/opth-gen.awk @@ -134,7 +134,7 @@ print "{"; n_opt_char = 4; n_opt_short = 0; -n_opt_int = 0; +n_opt_int = 1; n_opt_enum = 0; n_opt_other = 0; n_opt_explicit = 4; @@ -142,6 +142,7 @@ var_opt_char[0] = "unsigned char x_optimize"; var_opt_char[1] = "unsigned char x_optimize_size"; var_opt_char[2] = "unsigned char x_optimize_debug"; var_opt_char[3] = "unsigned char x_optimize_fast"; +var_opt_int[0] = "unsigned int x_flag_sanitize"; for (i = 0; i < n_opts; i++) { if (flag_set_p("(Optimization|PerFunction)", flags[i])) { base-commit: 13ea4a6e830da1f245136601e636dec62e74d1a7 prerequisite-patch-id: f75da3aa4e66d8b85562d5dd9ae35c5429c1ea74 -- 2.27.0