From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1036 invoked by alias); 15 Apr 2014 10:12:13 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 1021 invoked by uid 89); 15 Apr 2014 10:12:12 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.9 required=5.0 tests=AWL,BAYES_40,RP_MATCHES_RCVD,SPF_HELO_PASS,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 15 Apr 2014 10:12:09 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s3FAC7kL005614 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 15 Apr 2014 06:12:08 -0400 Received: from tucnak.zalov.cz (ovpn-116-44.ams2.redhat.com [10.36.116.44]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s3FAC5KY021869 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Tue, 15 Apr 2014 06:12:06 -0400 Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.14.8/8.14.7) with ESMTP id s3FAC0Pu030066; Tue, 15 Apr 2014 12:12:01 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.14.8/8.14.8/Submit) id s3FABu05030065; Tue, 15 Apr 2014 12:11:56 +0200 Date: Tue, 15 Apr 2014 10:12:00 -0000 From: Jakub Jelinek To: Richard Biener , Marek Polacek , Dodji Seketeli , Konstantin Serebryany , Tobias Burnus Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] Add support for -fno-sanitize-recover and -fsanitize-undefined-trap-on-error (PR sanitizer/60275) Message-ID: <20140415101156.GB1817@tucnak.redhat.com> Reply-To: Jakub Jelinek MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes X-SW-Source: 2014-04/txt/msg00764.txt.bz2 Hi! This patch adds two new options (compatible with clang) which allow users to choose the behavior of undefined behavior sanitization. By default as before, all undefined behaviors (except for __builtin_unreachable and missing return in C++) continue after reporting which means that you can get lots of runtime errors from a single program run and the exit code will not reflect the failure in that case. With this patch, one can use -fsanitize=undefined -fno-sanitize-recover, which will report just the first undefined behavior and then exit with non-zero code. Or one can use -fsanitize-undefined-trap-on-error, which will just __builtin_trap () on undefined behavior, not report anything and not require linking of -lubsan (useful e.g. for the kernel or embedded apps). If -fsanitize-undefined-trap-on-error, then -f{,no-}sanitize-recover is ignored, as ub traps, of course only the first undefined behavior will be "reported" (through the SIGILL/abort). Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2014-04-15 Jakub Jelinek PR sanitizer/60275 * common.opt (fsanitize-recover, fsanitize-undefined-trap-on-error): New options. * gcc.c (sanitize_spec_function): Don't return "" for "undefined" if flag_sanitize_undefined_trap_on_error. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT, BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT, BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT, BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT, BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT, BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT, BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT, BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT, BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT): New builtins. * ubsan.c (ubsan_instrument_unreachable): Return __builtin_trap () if flag_sanitize_undefined_trap_on_error. (ubsan_expand_null_ifn): Emit __builtin_trap () if flag_sanitize_undefined_trap_on_error and __ubsan_handle_type_mismatch_abort if !flag_sanitize_recover. (ubsan_expand_null_ifn, ubsan_build_overflow_builtin, instrument_bool_enum_load): Emit __builtin_trap () if flag_sanitize_undefined_trap_on_error and __builtin_handle_*_abort () if !flag_sanitize_recover. * doc/invoke.texi (-fsanitize-recover, -fsanitize-undefined-trap-on-error): Document. c-family/ * c-ubsan.c (ubsan_instrument_return): Return __builtin_trap () if flag_sanitize_undefined_trap_on_error. (ubsan_instrument_division, ubsan_instrument_shift, ubsan_instrument_vla): Likewise. Use __ubsan_handle_*_abort () if !flag_sanitize_recover. testsuite/ * g++.dg/ubsan/return-2.C: Revert 2014-03-24 changes, add -fno-sanitize-recover to dg-options. * g++.dg/ubsan/cxx11-shift-1.C: Remove c++11 target restriction, add -std=c++11 to dg-options. * g++.dg/ubsan/cxx11-shift-2.C: Likewise. * g++.dg/ubsan/cxx1y-vla.C: Remove c++1y target restriction, add -std=c++1y to dg-options. * c-c++-common/ubsan/undefined-1.c: Revert 2014-03-24 changes, add -fno-sanitize-recover to dg-options. * c-c++-common/ubsan/overflow-sub-1.c: Likewise. * c-c++-common/ubsan/vla-4.c: Likewise. * c-c++-common/ubsan/pr59503.c: Likewise. * c-c++-common/ubsan/vla-3.c: Likewise. * c-c++-common/ubsan/save-expr-1.c: Likewise. * c-c++-common/ubsan/overflow-add-1.c: Likewise. * c-c++-common/ubsan/shift-3.c: Likewise. * c-c++-common/ubsan/overflow-1.c: Likewise. * c-c++-common/ubsan/overflow-negate-2.c: Likewise. * c-c++-common/ubsan/vla-2.c: Likewise. * c-c++-common/ubsan/overflow-mul-1.c: Likewise. * c-c++-common/ubsan/pr60613-1.c: Likewise. * c-c++-common/ubsan/shift-6.c: Likewise. * c-c++-common/ubsan/overflow-mul-3.c: Likewise. * c-c++-common/ubsan/overflow-add-3.c: New test. * c-c++-common/ubsan/overflow-add-4.c: New test. * c-c++-common/ubsan/div-by-zero-6.c: New test. * c-c++-common/ubsan/div-by-zero-7.c: New test. --- gcc/common.opt.jj 2014-04-15 09:57:33.400264838 +0200 +++ gcc/common.opt 2014-04-15 10:28:10.554519376 +0200 @@ -862,6 +862,14 @@ fsanitize= Common Driver Report Joined Select what to sanitize +fsanitize-recover +Common Report Var(flag_sanitize_recover) Init(1) +After diagnosing undefined behavior attempt to continue execution + +fsanitize-undefined-trap-on-error +Common Report Var(flag_sanitize_undefined_trap_on_error) Init(0) +Use trap instead of a library function for undefined behavior sanitization + fasynchronous-unwind-tables Common Report Var(flag_asynchronous_unwind_tables) Optimization Generate unwind tables that are exact at each instruction boundary --- gcc/gcc.c.jj 2014-04-15 09:57:33.456264545 +0200 +++ gcc/gcc.c 2014-04-15 10:28:10.555519370 +0200 @@ -8170,7 +8170,8 @@ sanitize_spec_function (int argc, const if (strcmp (argv[0], "thread") == 0) return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL; if (strcmp (argv[0], "undefined") == 0) - return (flag_sanitize & SANITIZE_UNDEFINED) ? "" : NULL; + return ((flag_sanitize & SANITIZE_UNDEFINED) + && !flag_sanitize_undefined_trap_on_error) ? "" : NULL; if (strcmp (argv[0], "leak") == 0) return ((flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_LEAK | SANITIZE_THREAD)) --- gcc/sanitizer.def.jj 2014-04-15 09:57:32.819267872 +0200 +++ gcc/sanitizer.def 2014-04-15 10:28:10.556519365 +0200 @@ -335,3 +335,39 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN "__ubsan_handle_load_invalid_value", BT_FN_VOID_PTR_PTR, ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT, + "__ubsan_handle_divrem_overflow_abort", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT, + "__ubsan_handle_shift_out_of_bounds_abort", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT, + "__ubsan_handle_vla_bound_not_positive_abort", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT, + "__ubsan_handle_type_mismatch_abort", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT, + "__ubsan_handle_add_overflow_abort", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT, + "__ubsan_handle_sub_overflow_abort", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT, + "__ubsan_handle_mul_overflow_abort", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT, + "__ubsan_handle_negate_overflow_abort", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT, + "__ubsan_handle_load_invalid_value_abort", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST) --- gcc/ubsan.c.jj 2014-04-15 09:57:33.317265271 +0200 +++ gcc/ubsan.c 2014-04-15 10:28:10.556519365 +0200 @@ -516,6 +516,9 @@ ubsan_create_data (const char *name, con tree ubsan_instrument_unreachable (location_t loc) { + if (flag_sanitize_undefined_trap_on_error) + return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + initialize_sanitizer_builtins (); tree data = ubsan_create_data ("__ubsan_unreachable_data", &loc, NULL, NULL_TREE); @@ -583,16 +586,25 @@ ubsan_expand_null_ifn (gimple_stmt_itera set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb); /* Put the ubsan builtin call into the newly created BB. */ - tree fn = builtin_decl_implicit (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH); - const struct ubsan_mismatch_data m - = { build_zero_cst (pointer_sized_int_node), ckind }; - tree data = ubsan_create_data ("__ubsan_null_data", - &loc, &m, - ubsan_type_descriptor (TREE_TYPE (ptr), true), - NULL_TREE); - data = build_fold_addr_expr_loc (loc, data); - gimple g = gimple_build_call (fn, 2, data, - build_zero_cst (pointer_sized_int_node)); + gimple g; + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0); + else + { + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH + : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; + tree fn = builtin_decl_implicit (bcode); + const struct ubsan_mismatch_data m + = { build_zero_cst (pointer_sized_int_node), ckind }; + tree data = ubsan_create_data ("__ubsan_null_data", &loc, &m, + ubsan_type_descriptor (TREE_TYPE (ptr), + true), NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + g = gimple_build_call (fn, 2, data, + build_zero_cst (pointer_sized_int_node)); + } gimple_set_location (g, loc); gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb); gsi_insert_after (&gsi2, g, GSI_NEW_STMT); @@ -662,6 +674,9 @@ tree ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype, tree op0, tree op1) { + if (flag_sanitize_undefined_trap_on_error) + return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, ubsan_type_descriptor (lhstype, false), NULL_TREE); @@ -670,16 +685,24 @@ ubsan_build_overflow_builtin (tree_code switch (code) { case PLUS_EXPR: - fn_code = BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW; + fn_code = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT; break; case MINUS_EXPR: - fn_code = BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW; + fn_code = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT; break; case MULT_EXPR: - fn_code = BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW; + fn_code = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT; break; case NEGATE_EXPR: - fn_code = BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW; + fn_code = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT; break; default: gcc_unreachable (); @@ -844,17 +867,26 @@ instrument_bool_enum_load (gimple_stmt_i gimple_assign_set_rhs_with_ops (&gsi2, NOP_EXPR, urhs, NULL_TREE); update_stmt (stmt); - tree data = ubsan_create_data ("__ubsan_invalid_value_data", - &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); - data = build_fold_addr_expr_loc (loc, data); - tree fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE); - gsi2 = gsi_after_labels (then_bb); - tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs), - true, NULL_TREE, true, GSI_SAME_STMT); - g = gimple_build_call (fn, 2, data, val); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data = ubsan_create_data ("__ubsan_invalid_value_data", &loc, NULL, + ubsan_type_descriptor (type, false), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE + : BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT; + tree fn = builtin_decl_explicit (bcode); + + tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs), + true, NULL_TREE, true, + GSI_SAME_STMT); + g = gimple_build_call (fn, 2, data, val); + } gimple_set_location (g, loc); gsi_insert_before (&gsi2, g, GSI_SAME_STMT); } --- gcc/c-family/c-ubsan.c.jj 2014-04-15 09:57:33.362265036 +0200 +++ gcc/c-family/c-ubsan.c 2014-04-15 10:28:10.556519365 +0200 @@ -73,14 +73,22 @@ ubsan_instrument_division (location_t lo /* In case we have a SAVE_EXPR in a conditional context, we need to make sure it gets evaluated before the condition. */ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); - tree data = ubsan_create_data ("__ubsan_overflow_data", - &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); - data = build_fold_addr_expr_loc (loc, data); - tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW); - tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), - ubsan_encode_value (op1)); + if (flag_sanitize_undefined_trap_on_error) + tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL, + ubsan_type_descriptor (type, false), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW + : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT; + tt = builtin_decl_explicit (bcode); + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), + ubsan_encode_value (op1)); + } t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node); return t; @@ -142,19 +150,28 @@ ubsan_instrument_shift (location_t loc, /* In case we have a SAVE_EXPR in a conditional context, we need to make sure it gets evaluated before the condition. */ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t); - tree data = ubsan_create_data ("__ubsan_shift_data", - &loc, NULL, - ubsan_type_descriptor (type0, false), - ubsan_type_descriptor (type1, false), - NULL_TREE); - - data = build_fold_addr_expr_loc (loc, data); - t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt ? tt : integer_zero_node); - tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS); - tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), - ubsan_encode_value (op1)); + + if (flag_sanitize_undefined_trap_on_error) + tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data = ubsan_create_data ("__ubsan_shift_data", &loc, NULL, + ubsan_type_descriptor (type0, false), + ubsan_type_descriptor (type1, false), + NULL_TREE); + + data = build_fold_addr_expr_loc (loc, data); + + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS + : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT; + tt = builtin_decl_explicit (bcode); + tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0), + ubsan_encode_value (op1)); + } t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node); return t; @@ -169,13 +186,21 @@ ubsan_instrument_vla (location_t loc, tr tree t, tt; t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0)); - tree data = ubsan_create_data ("__ubsan_vla_data", - &loc, NULL, - ubsan_type_descriptor (type, false), - NULL_TREE); - data = build_fold_addr_expr_loc (loc, data); - tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE); - tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size)); + if (flag_sanitize_undefined_trap_on_error) + tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data = ubsan_create_data ("__ubsan_vla_data", &loc, NULL, + ubsan_type_descriptor (type, false), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE + : BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT; + tt = builtin_decl_explicit (bcode); + tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size)); + } t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node); return t; @@ -186,6 +211,8 @@ ubsan_instrument_vla (location_t loc, tr tree ubsan_instrument_return (location_t loc) { + if (flag_sanitize_undefined_trap_on_error) + return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0); /* It is possible that PCH zapped table with definitions of sanitizer builtins. Reinitialize them if needed. */ initialize_sanitizer_builtins (); --- gcc/doc/invoke.texi.jj 2014-04-15 09:57:52.000000000 +0200 +++ gcc/doc/invoke.texi 2014-04-15 12:09:17.983858622 +0200 @@ -288,7 +288,8 @@ Objective-C and Objective-C++ Dialects}. @item Debugging Options @xref{Debugging Options,,Options for Debugging Your Program or GCC}. @gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol --fsanitize=@var{style} @gol +-fsanitize=@var{style} -fsanitize-recover @gol +-fsanitize-undefined-trap-on-error @gol -fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol -fdisable-ipa-@var{pass_name} @gol -fdisable-rtl-@var{pass_name} @gol @@ -5380,6 +5381,26 @@ While @option{-ftrapv} causes traps for @option{-fsanitize=undefined} gives a diagnostic message. This currently works only for the C family of languages. +@item -fsanitize-recover +@opindex fsanitize-recover +By default @option{-fsanitize=undefined} sanitization (and its suboptions +except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}) +after reporting undefined behavior attempts to continue running the +program as if no undefined behavior happened. This means multiple undefined +behavior runtime errors can be reported in a single program run, and the exit +code of the program may indicate success even when undefined behavior +has been reported. The @option{-fno-sanitize-recover} can be used to alter +this behavior, only the first detected undefined behavior will be reported +and program will exit after that with non-zero exit code. + +@item -fsanitize-undefined-trap-on-error +@opindex fsanitize-undefined-trap-on-error +The @option{-fsanitize-undefined-trap-on-error} 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 will not be linked in, so this +is usable even for use in freestanding environments. + @item -fdump-final-insns@r{[}=@var{file}@r{]} @opindex fdump-final-insns Dump the final internal representation (RTL) to @var{file}. If the --- gcc/testsuite/g++.dg/ubsan/return-2.C.jj 2014-03-25 09:22:04.311136241 +0100 +++ gcc/testsuite/g++.dg/ubsan/return-2.C 2014-04-15 11:42:41.504233228 +0200 @@ -1,7 +1,5 @@ // { dg-do run } -// { dg-options "-fsanitize=return" } - -#include +// { dg-options "-fsanitize=return -fno-sanitize-recover" } struct S { S (); ~S (); }; @@ -22,12 +20,6 @@ foo (int x) int main () { - fputs ("UBSAN TEST START\n", stderr); - foo (1); foo (14); - - fputs ("UBSAN TEST END\n", stderr); } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C.jj 2014-03-25 09:22:04.331136134 +0100 +++ gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C 2014-04-15 11:42:44.652216672 +0200 @@ -1,18 +1,10 @@ -/* { dg-do run { target c++11 } } */ -/* { dg-options "-fsanitize=shift -w" } */ - -#include +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover -std=c++11" } */ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - int a = 1; a <<= 31; - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C.jj 2014-03-10 10:49:56.000000000 +0100 +++ gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C 2014-04-15 11:28:18.777735556 +0200 @@ -1,5 +1,5 @@ -/* { dg-do run { target c++11 } } */ -/* { dg-options "-fsanitize=shift -w" } */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c++11" } */ int main (void) --- gcc/testsuite/g++.dg/ubsan/cxx1y-vla.C.jj 2014-03-10 10:49:56.000000000 +0100 +++ gcc/testsuite/g++.dg/ubsan/cxx1y-vla.C 2014-04-15 11:28:40.570622975 +0200 @@ -1,5 +1,5 @@ -/* { dg-do run { target c++1y } } */ -/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable" } */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -std=c++1y" } */ /* { dg-shouldfail "ubsan" } */ int --- gcc/testsuite/c-c++-common/ubsan/undefined-1.c.jj 2014-03-25 09:22:05.226131391 +0100 +++ gcc/testsuite/c-c++-common/ubsan/undefined-1.c 2014-04-15 11:43:04.064114597 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=undefined" } */ - -#include +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */ int foo (int x, int y) @@ -21,13 +19,7 @@ bar (int x, int y) int main (void) { - fputs ("UBSAN TEST START\n", stderr); - foo (3, 2); bar (12, 42); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c.jj 2014-03-25 09:22:05.225131396 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c 2014-04-15 11:43:09.677085087 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover" } */ #define SCHAR_MAX __SCHAR_MAX__ #define SCHAR_MIN (-__SCHAR_MAX__ - 1) @@ -20,8 +18,6 @@ check (int i, int j) int main (void) { - fputs ("UBSAN TEST START\n", stderr); - #if __INT_MAX__ == 2147483647 /* Here, nothing should fail. */ volatile int i = -1; @@ -61,9 +57,5 @@ main (void) d--; check (d, 32767); #endif - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/vla-4.c.jj 2014-03-25 09:22:05.224131402 +0100 +++ gcc/testsuite/c-c++-common/ubsan/vla-4.c 2014-04-15 11:43:12.250071561 +0200 @@ -1,21 +1,13 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=vla-bound" } */ - -#include +/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover" } */ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - int x = 1; /* Check that the size of an array is evaluated only once. */ int a[++x]; if (x != 2) __builtin_abort (); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/pr59503.c.jj 2014-03-25 09:22:05.225131396 +0100 +++ gcc/testsuite/c-c++-common/ubsan/pr59503.c 2014-04-15 11:43:14.520059628 +0200 @@ -1,21 +1,13 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover" } */ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - long long int a = 14; long int b = 9; asm volatile ("" : "+r" (a), "+r" (b)); if ((a - b) != 5) __builtin_abort (); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/vla-3.c.jj 2014-03-25 09:22:05.224131402 +0100 +++ gcc/testsuite/c-c++-common/ubsan/vla-3.c 2014-04-15 11:43:16.707048131 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=vla-bound" } */ - -#include +/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover" } */ /* Don't instrument the arrays here. */ int @@ -13,13 +11,7 @@ foo (int n, int a[]) int main (void) { - fputs ("UBSAN TEST START\n", stderr); - int a[6] = { }; int ret = foo (3, a); - - fputs ("UBSAN TEST END\n", stderr); return ret; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/save-expr-1.c.jj 2014-03-25 09:22:05.217131440 +0100 +++ gcc/testsuite/c-c++-common/ubsan/save-expr-1.c 2014-04-15 11:43:19.240034816 +0200 @@ -1,19 +1,11 @@ /* { dg-do compile } */ -/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */ - -#include +/* { dg-options "-fsanitize=shift -Wall -Werror -O -fno-sanitize-recover" } */ static int x; int main (void) { - fputs ("UBSAN TEST START\n", stderr); - int o = 1; int y = x << o; - - fputs ("UBSAN TEST END\n", stderr); return y; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c.jj 2014-03-25 09:22:05.225131396 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c 2014-04-15 11:43:22.331018569 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover" } */ #define SCHAR_MAX __SCHAR_MAX__ #define SHRT_MAX __SHRT_MAX__ @@ -18,8 +16,6 @@ check (int i, int j) int main (void) { - fputs ("UBSAN TEST START\n", stderr); - #if __INT_MAX__ == 2147483647 /* Here, nothing should fail. */ volatile int j = INT_MAX; @@ -59,9 +55,5 @@ main (void) d++; check (d, -32768); #endif - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/shift-3.c.jj 2014-03-25 09:22:05.227131385 +0100 +++ gcc/testsuite/c-c++-common/ubsan/shift-3.c 2014-04-15 11:43:24.323008099 +0200 @@ -1,19 +1,11 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=shift -w" } */ - -#include +/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover" } */ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - unsigned int a = 1; a <<= 31; a <<= 1; - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-1.c.jj 2014-03-25 09:22:05.000000000 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-1.c 2014-04-15 11:43:30.257976905 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover" } */ #ifndef ASM1 # define ASM1(a) /* Nothing */ @@ -53,8 +51,6 @@ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - CHECK (FN1 (char, char, +), 23); CHECK (FN1 (char, char, -), 5); CHECK (FN1 (char, char, *), 126); @@ -261,9 +257,5 @@ main (void) CHECK (FN5 (unsigned long int), -77); CHECK (FN5 (long long int), -77); CHECK (FN5 (unsigned long long int), -77); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-negate-2.c.jj 2014-03-25 09:22:05.227131385 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-negate-2.c 2014-04-15 11:43:33.411960330 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover" } */ #define SCHAR_MIN (-__SCHAR_MAX__ - 1) #define SHRT_MIN (-__SHRT_MAX__ - 1) @@ -14,8 +12,6 @@ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - volatile signed char c = -SCHAR_MIN; CHECK (c, -128); @@ -37,9 +33,5 @@ main (void) volatile long long lli = LLONG_MIN; lli = -(unsigned long long) lli; CHECK (lli, -0x8000000000000000L); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/vla-2.c.jj 2014-03-25 09:22:05.218131434 +0100 +++ gcc/testsuite/c-c++-common/ubsan/vla-2.c 2014-04-15 11:43:35.537949157 +0200 @@ -1,22 +1,14 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable" } */ - -#include +/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-sanitize-recover" } */ int main (void) { - fputs ("UBSAN TEST START\n", stderr); - const int t = 0; struct s { int x; /* Don't instrument this one. */ int g[t]; }; - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c.jj 2014-03-25 09:22:05.226131391 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c 2014-04-15 11:43:38.674932672 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover" } */ #define SCHAR_MAX __SCHAR_MAX__ #define SHRT_MAX __SHRT_MAX__ @@ -18,8 +16,6 @@ check (int i, int j) int main (void) { - fputs ("UBSAN TEST START\n", stderr); - /* Test integer promotion. */ #if __SCHAR_MAX__ == 127 volatile signed char a = -2; @@ -45,9 +41,5 @@ main (void) o = m * n; check (o, INT_MIN); #endif - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/pr60613-1.c.jj 2014-03-25 09:22:05.218131434 +0100 +++ gcc/testsuite/c-c++-common/ubsan/pr60613-1.c 2014-04-15 11:43:41.800916245 +0200 @@ -1,8 +1,6 @@ /* PR sanitizer/60613 */ /* { dg-do run } */ -/* { dg-options "-fsanitize=undefined" } */ - -#include +/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */ long long y; @@ -26,16 +24,10 @@ bar (long long x) int main () { - fputs ("UBSAN TEST START\n", stderr); - y = 1; if (foo (8 - 2040) != 8 - 1) __builtin_abort (); if (bar (1) != 8 - 1) __builtin_abort (); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/shift-6.c.jj 2014-03-25 09:22:05.228131380 +0100 +++ gcc/testsuite/c-c++-common/ubsan/shift-6.c 2014-04-15 11:43:44.649901274 +0200 @@ -1,15 +1,11 @@ /* PR sanitizer/58413 */ /* { dg-do run { target int32plus } } */ -/* { dg-options "-fsanitize=shift -w" } */ - -#include +/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover" } */ int x = 7; int main (void) { - fputs ("UBSAN TEST START\n", stderr); - /* All of the following should pass. */ int A[128 >> 5] = {}; int B[128 << 5] = {}; @@ -30,9 +26,5 @@ main (void) case 128 >> (4 + 1): return 1; } - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-mul-3.c.jj 2014-03-25 09:22:05.226131391 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-mul-3.c 2014-04-15 11:43:47.150888133 +0200 @@ -1,7 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-fsanitize=signed-integer-overflow" } */ - -#include +/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover" } */ __attribute__((noinline, noclone)) long long mul (long long x, long long y) @@ -31,16 +29,10 @@ long long tab[] = { int main () { - fputs ("UBSAN TEST START\n", stderr); - unsigned int i; for (i = 0; i < sizeof (tab) / sizeof (long long); i += 3) if (mul (tab[i], tab[i + 1]) != tab[i + 2] || mul (tab[i + 1], tab[i]) != tab[i + 2]) __builtin_abort (); - - fputs ("UBSAN TEST END\n", stderr); return 0; } - -/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-add-3.c.jj 2014-04-15 10:56:15.182674640 +0200 +++ gcc/testsuite/c-c++-common/ubsan/overflow-add-3.c 2014-04-15 10:57:44.703204633 +0200 @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover" } */ +/* { dg-shouldfail "ubsan" } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) + +int +main (void) +{ + volatile int j = INT_MAX; + volatile int i = 1; + volatile int k = j + i; + return 0; +} + +/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c.jj 2014-04-15 10:58:10.268070265 +0200 +++ gcc/testsuite/c-c++-common/ubsan/overflow-add-4.c 2014-04-15 10:58:28.383975015 +0200 @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fsanitize-undefined-trap-on-error" } */ +/* { dg-shouldfail "ubsan" } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) + +int +main (void) +{ + volatile int j = INT_MAX; + volatile int i = 1; + volatile int k = j + i; + return 0; +} --- gcc/testsuite/c-c++-common/ubsan/div-by-zero-6.c.jj 2014-04-15 10:36:27.241889827 +0200 +++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-6.c 2014-04-15 11:44:36.665628047 +0200 @@ -0,0 +1,49 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero" } */ + +#include + +int x; + +__attribute__((noinline, noclone)) +void +barrier (void) +{ + asm volatile ("" : : : "memory"); + if (x) + __builtin_exit (1); +} + +int +main (void) +{ + volatile int a = 0; + volatile long long int b = 0; + volatile unsigned int c = 1; + + barrier (); fputs ("1st\n", stderr); barrier (); + a / b; + barrier (); fputs ("2nd\n", stderr); barrier (); + 0 / 0; + barrier (); fputs ("3rd\n", stderr); barrier (); + a / 0; + barrier (); fputs ("4th\n", stderr); barrier (); + 0 / b; + barrier (); fputs ("5th\n", stderr); barrier (); + 2 / --c; + barrier (); fputs ("6th\n", stderr); barrier (); + + return 0; +} + +/* { dg-output "1st(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "2nd(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "3rd(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "4th(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "5th(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "6th" } */ --- gcc/testsuite/c-c++-common/ubsan/div-by-zero-7.c.jj 2014-04-15 10:49:23.124815653 +0200 +++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-7.c 2014-04-15 11:44:45.795580108 +0200 @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero -fno-sanitize-recover" } */ +/* { dg-shouldfail "ubsan" } */ + +#include + +int x; + +__attribute__((noinline, noclone)) +void +barrier (void) +{ + asm volatile ("" : : : "memory"); + if (++x == 3) + __builtin_exit (0); +} + +int +main (void) +{ + volatile int a = 0; + volatile long long int b = 0; + volatile unsigned int c = 1; + + barrier (); fputs ("1st\n", stderr); barrier (); + a / b; + barrier (); fputs ("2nd\n", stderr); barrier (); + 0 / 0; + barrier (); fputs ("3rd\n", stderr); barrier (); + a / 0; + barrier (); fputs ("4th\n", stderr); barrier (); + 0 / b; + barrier (); fputs ("5th\n", stderr); barrier (); + 2 / --c; + barrier (); fputs ("6th\n", stderr); barrier (); + + return 0; +} + +/* { dg-output "1st(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero" } */ Jakub