diff options
author | mpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-04 22:47:11 +0000 |
---|---|---|
committer | mpolacek <mpolacek@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-12-04 22:47:11 +0000 |
commit | 137559b2325c900743803d115c4d8495f9da5d67 (patch) | |
tree | 20fca81276f1e6d6224300c11a4e10fa89519d41 /gcc | |
parent | 85f5275438752595289934e2bbf725d1481f77cc (diff) | |
download | gcc-137559b2325c900743803d115c4d8495f9da5d67.tar.gz |
Implement -fsanitize=signed-integer-overflow.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@205684 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
27 files changed, 1063 insertions, 28 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 25651f8130a..6c5d257ec82 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,50 @@ +2013-12-04 Jakub Jelinek <jakub@redhat.com> + Marek Polacek <polacek@redhat.com> + + * opts.c (common_handle_option): Handle + -fsanitize=signed-integer-overflow. + * config/i386/i386.md (addv<mode>4, subv<mode>4, mulv<mode>4, + negv<mode>3, negv<mode>3_1): Define expands. + (*addv<mode>4, *subv<mode>4, *mulv<mode>4, *negv<mode>3): Define + insns. + * sanitizer.def (BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW, + BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW, + BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW, + BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW): Define. + * ubsan.h (PROB_VERY_UNLIKELY, PROB_EVEN, PROB_VERY_LIKELY, + PROB_ALWAYS): Define. + (ubsan_build_overflow_builtin): Declare. + * gimple-fold.c (gimple_fold_stmt_to_constant_1): Add folding of + internal functions. + * ubsan.c (PROB_VERY_UNLIKELY): Don't define here. + (ubsan_build_overflow_builtin): New function. + (instrument_si_overflow): Likewise. + (ubsan_pass): Add signed integer overflow checking. + (gate_ubsan): Enable the pass also when SANITIZE_SI_OVERFLOW. + * flag-types.h (enum sanitize_code): Add SANITIZE_SI_OVERFLOW. + * internal-fn.c: Include ubsan.h and target.h. + (ubsan_expand_si_overflow_addsub_check): New function. + (ubsan_expand_si_overflow_neg_check): Likewise. + (ubsan_expand_si_overflow_mul_check): Likewise. + (expand_UBSAN_CHECK_ADD): Likewise. + (expand_UBSAN_CHECK_SUB): Likewise. + (expand_UBSAN_CHECK_MUL): Likewise. + * fold-const.c (fold_binary_loc): Don't fold A + (-B) -> A - B and + (-A) + B -> B - A when doing the signed integer overflow checking. + * internal-fn.def (UBSAN_CHECK_ADD, UBSAN_CHECK_SUB, UBSAN_CHECK_MUL): + Define. + * tree-vrp.c (extract_range_basic): Handle internal calls. + * optabs.def (addv4_optab, subv4_optab, mulv4_optab, negv4_optab): New + optabs. + * asan.c: Include predict.h. + (PROB_VERY_UNLIKELY, PROB_ALWAYS): Don't define here. + * predict.c: Move the PROB_* macros... + * predict.h (enum br_predictor): ...here. + (PROB_LIKELY, PROB_UNLIKELY): Define. + * trans-mem.c: Include predict.h. + (PROB_VERY_UNLIKELY, PROB_ALWAYS, PROB_VERY_LIKELY, + PROB_LIKELY, PROB_UNLIKELY): Don't define here. + 2013-12-04 Jeff Law <law@redhat.com> * expr.c (expand_assignment): Update comments. diff --git a/gcc/asan.c b/gcc/asan.c index 2245d6dd807..74140d65289 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "gimple-builder.h" #include "ubsan.h" +#include "predict.h" /* AddressSanitizer finds out-of-bounds and use-after-free bugs with <2x slowdown on average. @@ -1311,9 +1312,6 @@ report_error_func (bool is_store, int size_in_bytes) return builtin_decl_implicit (report[is_store][exact_log2 (size_in_bytes)]); } -#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1) -#define PROB_ALWAYS (REG_BR_PROB_BASE) - /* Split the current basic block and create a condition statement insertion point right before or after the statement pointed to by ITER. Return an iterator to the point at which the caller might diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 616a74209c0..35c00035e02 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2013-12-04 Jakub Jelinek <jakub@redhat.com> + Marek Polacek <polacek@redhat.com> + + * c-gimplify.c (c_gimplify_expr): If doing the integer-overflow + sanitization, call unsigned_type_for only when !TYPE_OVERFLOW_WRAPS. + 2013-11-29 H.J. Lu <hongjiu.lu@intel.com> PR c/59309 diff --git a/gcc/c-family/c-gimplify.c b/gcc/c-family/c-gimplify.c index d047c65b4ee..9f79ccb630d 100644 --- a/gcc/c-family/c-gimplify.c +++ b/gcc/c-family/c-gimplify.c @@ -199,7 +199,9 @@ c_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED, tree type = TREE_TYPE (TREE_OPERAND (*expr_p, 0)); if (INTEGRAL_TYPE_P (type) && c_promoting_integer_type_p (type)) { - if (TYPE_OVERFLOW_UNDEFINED (type)) + if (TYPE_OVERFLOW_UNDEFINED (type) + || ((flag_sanitize & SANITIZE_SI_OVERFLOW) + && !TYPE_OVERFLOW_WRAPS (type))) type = unsigned_type_for (type); return gimplify_self_mod_expr (expr_p, pre_p, post_p, 1, type); } diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 0c732c7689f..5ff66ccf1e5 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -905,8 +905,8 @@ (TI "TARGET_64BIT")]) ;; Double word integer modes as mode attribute. -(define_mode_attr DWI [(SI "DI") (DI "TI")]) -(define_mode_attr dwi [(SI "di") (DI "ti")]) +(define_mode_attr DWI [(QI "HI") (HI "SI") (SI "DI") (DI "TI")]) +(define_mode_attr dwi [(QI "hi") (HI "si") (SI "di") (DI "ti")]) ;; Half mode for double word integer modes. (define_mode_iterator DWIH [(SI "!TARGET_64BIT") @@ -6160,6 +6160,41 @@ [(set_attr "type" "alu") (set_attr "mode" "QI")]) +;; Add with jump on overflow. +(define_expand "addv<mode>4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (plus:<DWI> + (sign_extend:<DWI> + (match_operand:SWI 1 "nonimmediate_operand")) + (sign_extend:<DWI> + (match_operand:SWI 2 "<general_operand>"))) + (sign_extend:<DWI> + (plus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "register_operand") + (plus:SWI (match_dup 1) (match_dup 2)))]) + (set (pc) (if_then_else + (eq (reg:CCO FLAGS_REG) (const_int 0)) + (label_ref (match_operand 3)) + (pc)))] + "" + "ix86_fixup_binary_operands_no_copy (PLUS, <MODE>mode, operands);") + +(define_insn "*addv<mode>4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (plus:<DWI> + (sign_extend:<DWI> + (match_operand:SWI 1 "nonimmediate_operand" "%0,0")) + (sign_extend:<DWI> + (match_operand:SWI 2 "<general_operand>" "<g>,<r><i>"))) + (sign_extend:<DWI> + (plus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "nonimmediate_operand" "=<r>,<r>m") + (plus:SWI (match_dup 1) (match_dup 2)))] + "ix86_binary_operator_ok (PLUS, <MODE>mode, operands)" + "add{<imodesuffix>}\t{%2, %0|%0, %2}" + [(set_attr "type" "alu") + (set_attr "mode" "<MODE>")]) + ;; The lea patterns for modes less than 32 bits need to be matched by ;; several insns converted to real lea by splitters. @@ -6397,6 +6432,41 @@ [(set_attr "type" "alu") (set_attr "mode" "SI")]) +;; Subtract with jump on overflow. +(define_expand "subv<mode>4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (minus:<DWI> + (sign_extend:<DWI> + (match_operand:SWI 1 "nonimmediate_operand")) + (sign_extend:<DWI> + (match_operand:SWI 2 "<general_operand>"))) + (sign_extend:<DWI> + (minus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "register_operand") + (minus:SWI (match_dup 1) (match_dup 2)))]) + (set (pc) (if_then_else + (eq (reg:CCO FLAGS_REG) (const_int 0)) + (label_ref (match_operand 3)) + (pc)))] + "" + "ix86_fixup_binary_operands_no_copy (MINUS, <MODE>mode, operands);") + +(define_insn "*subv<mode>4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (minus:<DWI> + (sign_extend:<DWI> + (match_operand:SWI 1 "nonimmediate_operand" "0,0")) + (sign_extend:<DWI> + (match_operand:SWI 2 "<general_operand>" "<r><i>,<r>m"))) + (sign_extend:<DWI> + (minus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m,<r>") + (minus:SWI (match_dup 1) (match_dup 2)))] + "ix86_binary_operator_ok (MINUS, <MODE>mode, operands)" + "sub{<imodesuffix>}\t{%2, %0|%0, %2}" + [(set_attr "type" "alu") + (set_attr "mode" "<MODE>")]) + (define_insn "*sub<mode>_3" [(set (reg FLAGS_REG) (compare (match_operand:SWI 1 "nonimmediate_operand" "0,0") @@ -6711,6 +6781,58 @@ (set_attr "bdver1_decode" "direct") (set_attr "mode" "QI")]) +;; Multiply with jump on overflow. +(define_expand "mulv<mode>4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (mult:<DWI> + (sign_extend:<DWI> + (match_operand:SWI48 1 "register_operand")) + (sign_extend:<DWI> + (match_operand:SWI48 2 "<general_operand>"))) + (sign_extend:<DWI> + (mult:SWI48 (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI48 0 "register_operand") + (mult:SWI48 (match_dup 1) (match_dup 2)))]) + (set (pc) (if_then_else + (eq (reg:CCO FLAGS_REG) (const_int 0)) + (label_ref (match_operand 3)) + (pc)))]) + +(define_insn "*mulv<mode>4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (mult:<DWI> + (sign_extend:<DWI> + (match_operand:SWI 1 "nonimmediate_operand" "%rm,rm,0")) + (sign_extend:<DWI> + (match_operand:SWI 2 "<general_operand>" "K,<i>,mr"))) + (sign_extend:<DWI> + (mult:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "register_operand" "=r,r,r") + (mult:SWI (match_dup 1) (match_dup 2)))] + "!(MEM_P (operands[1]) && MEM_P (operands[2]))" + "@ + imul{<imodesuffix>}\t{%2, %1, %0|%0, %1, %2} + imul{<imodesuffix>}\t{%2, %1, %0|%0, %1, %2} + imul{<imodesuffix>}\t{%2, %0|%0, %2}" + [(set_attr "type" "imul") + (set_attr "prefix_0f" "0,0,1") + (set (attr "athlon_decode") + (cond [(eq_attr "cpu" "athlon") + (const_string "vector") + (eq_attr "alternative" "1") + (const_string "vector") + (and (eq_attr "alternative" "2") + (match_operand 1 "memory_operand")) + (const_string "vector")] + (const_string "direct"))) + (set (attr "amdfam10_decode") + (cond [(and (eq_attr "alternative" "0,1") + (match_operand 1 "memory_operand")) + (const_string "vector")] + (const_string "direct"))) + (set_attr "bdver1_decode" "direct") + (set_attr "mode" "<MODE>")]) + (define_expand "<u>mul<mode><dwi>3" [(parallel [(set (match_operand:<DWI> 0 "register_operand") (mult:<DWI> @@ -8624,6 +8746,36 @@ [(set_attr "type" "negnot") (set_attr "mode" "SI")]) +;; Negate with jump on overflow. +(define_expand "negv<mode>3" + [(parallel [(set (reg:CCO FLAGS_REG) + (ne:CCO (match_operand:SWI 1 "register_operand") + (match_dup 3))) + (set (match_operand:SWI 0 "register_operand") + (neg:SWI (match_dup 1)))]) + (set (pc) (if_then_else + (eq (reg:CCO FLAGS_REG) (const_int 0)) + (label_ref (match_operand 2)) + (pc)))] + "" +{ + operands[3] + = gen_int_mode (HOST_WIDE_INT_1U << (GET_MODE_BITSIZE (<MODE>mode) - 1), + <MODE>mode); +}) + +(define_insn "*negv<mode>3" + [(set (reg:CCO FLAGS_REG) + (ne:CCO (match_operand:SWI 1 "nonimmediate_operand" "0") + (match_operand:SWI 2 "const_int_operand"))) + (set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m") + (neg:SWI (match_dup 1)))] + "ix86_unary_operator_ok (NEG, <MODE>mode, operands) + && mode_signbit_p (<MODE>mode, operands[2])" + "neg{<imodesuffix>}\t%0" + [(set_attr "type" "negnot") + (set_attr "mode" "<MODE>")]) + ;; Changing of sign for FP values is doable using integer unit too. (define_expand "<code><mode>2" diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 5ba909766fe..bea268f9aba 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -215,8 +215,10 @@ enum sanitize_code { SANITIZE_VLA = 1 << 6, SANITIZE_NULL = 1 << 7, SANITIZE_RETURN = 1 << 8, + SANITIZE_SI_OVERFLOW = 1 << 9, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN + | SANITIZE_SI_OVERFLOW }; /* flag_vtable_verify initialization levels. */ diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5cf8ed196d7..0781bdb52c8 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -10350,14 +10350,16 @@ fold_binary_loc (location_t loc, case PLUS_EXPR: /* A + (-B) -> A - B */ - if (TREE_CODE (arg1) == NEGATE_EXPR) + if (TREE_CODE (arg1) == NEGATE_EXPR + && (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0) return fold_build2_loc (loc, MINUS_EXPR, type, fold_convert_loc (loc, type, arg0), fold_convert_loc (loc, type, TREE_OPERAND (arg1, 0))); /* (-A) + B -> B - A */ if (TREE_CODE (arg0) == NEGATE_EXPR - && reorder_operands_p (TREE_OPERAND (arg0, 0), arg1)) + && reorder_operands_p (TREE_OPERAND (arg0, 0), arg1) + && (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0) return fold_build2_loc (loc, MINUS_EXPR, type, fold_convert_loc (loc, type, arg1), fold_convert_loc (loc, type, diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 7e9ba653f32..72893cd4aec 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -2660,8 +2660,37 @@ gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree)) tree fn; if (gimple_call_internal_p (stmt)) - /* No folding yet for these functions. */ - return NULL_TREE; + { + enum tree_code subcode = ERROR_MARK; + switch (gimple_call_internal_fn (stmt)) + { + case IFN_UBSAN_CHECK_ADD: + subcode = PLUS_EXPR; + break; + case IFN_UBSAN_CHECK_SUB: + subcode = MINUS_EXPR; + break; + case IFN_UBSAN_CHECK_MUL: + subcode = MULT_EXPR; + break; + default: + return NULL_TREE; + } + tree op0 = (*valueize) (gimple_call_arg (stmt, 0)); + tree op1 = (*valueize) (gimple_call_arg (stmt, 1)); + + if (TREE_CODE (op0) != INTEGER_CST + || TREE_CODE (op1) != INTEGER_CST) + return NULL_TREE; + tree res = fold_binary_loc (loc, subcode, + TREE_TYPE (gimple_call_arg (stmt, 0)), + op0, op1); + if (res + && TREE_CODE (res) == INTEGER_CST + && !TREE_OVERFLOW (res)) + return res; + return NULL_TREE; + } fn = (*valueize) (gimple_call_fn (stmt)); if (TREE_CODE (fn) == ADDR_EXPR diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c index 9ae917ba0ac..527b5ffaf7f 100644 --- a/gcc/internal-fn.c +++ b/gcc/internal-fn.c @@ -31,6 +31,9 @@ along with GCC; see the file COPYING3. If not see #include "gimple-expr.h" #include "is-a.h" #include "gimple.h" +#include "ubsan.h" +#include "target.h" +#include "predict.h" /* The names of each internal function, indexed by function number. */ const char *const internal_fn_name_array[] = { @@ -153,6 +156,305 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED) gcc_unreachable (); } +/* Add sub/add overflow checking to the statement STMT. + CODE says whether the operation is +, or -. */ + +void +ubsan_expand_si_overflow_addsub_check (tree_code code, gimple stmt) +{ + rtx res, op0, op1; + tree lhs, fn, arg0, arg1; + rtx done_label, do_error, target = NULL_RTX; + + lhs = gimple_call_lhs (stmt); + arg0 = gimple_call_arg (stmt, 0); + arg1 = gimple_call_arg (stmt, 1); + done_label = gen_label_rtx (); + do_error = gen_label_rtx (); + fn = ubsan_build_overflow_builtin (code, gimple_location (stmt), + TREE_TYPE (arg0), arg0, arg1); + do_pending_stack_adjust (); + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg0)); + if (lhs) + target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); + + enum insn_code icode + = optab_handler (code == PLUS_EXPR ? addv4_optab : subv4_optab, mode); + if (icode != CODE_FOR_nothing) + { + struct expand_operand ops[4]; + rtx last = get_last_insn (); + + res = gen_reg_rtx (mode); + create_output_operand (&ops[0], res, mode); + create_input_operand (&ops[1], op0, mode); + create_input_operand (&ops[2], op1, mode); + create_fixed_operand (&ops[3], do_error); + if (maybe_expand_insn (icode, 4, ops)) + { + last = get_last_insn (); + if (profile_status != PROFILE_ABSENT + && JUMP_P (last) + && any_condjump_p (last) + && !find_reg_note (last, REG_BR_PROB, 0)) + add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY); + emit_jump (done_label); + } + else + { + delete_insns_since (last); + icode = CODE_FOR_nothing; + } + } + + if (icode == CODE_FOR_nothing) + { + rtx sub_check = gen_label_rtx (); + + /* Compute the operation. On RTL level, the addition is always + unsigned. */ + res = expand_binop (mode, add_optab, op0, op1, + NULL_RTX, false, OPTAB_LIB_WIDEN); + + /* If the op1 is negative, we have to use a different check. */ + emit_cmp_and_jump_insns (op1, const0_rtx, LT, NULL_RTX, mode, + false, sub_check, PROB_EVEN); + + /* Compare the result of the addition with one of the operands. */ + emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? GE : LE, + NULL_RTX, mode, false, done_label, + PROB_VERY_LIKELY); + /* If we get here, we have to print the error. */ + emit_jump (do_error); + + emit_label (sub_check); + /* We have k = a + b for b < 0 here. k <= a must hold. */ + emit_cmp_and_jump_insns (res, op0, code == PLUS_EXPR ? LE : GE, + NULL_RTX, mode, false, done_label, + PROB_VERY_LIKELY); + } + + emit_label (do_error); + /* Expand the ubsan builtin call. */ + expand_normal (fn); + do_pending_stack_adjust (); + + /* We're done. */ + emit_label (done_label); + + if (lhs) + emit_move_insn (target, res); +} + +/* Add negate overflow checking to the statement STMT. */ + +void +ubsan_expand_si_overflow_neg_check (gimple stmt) +{ + rtx res, op1; + tree lhs, fn, arg1; + rtx done_label, do_error, target = NULL_RTX; + + lhs = gimple_call_lhs (stmt); + arg1 = gimple_call_arg (stmt, 1); + done_label = gen_label_rtx (); + do_error = gen_label_rtx (); + fn = ubsan_build_overflow_builtin (NEGATE_EXPR, gimple_location (stmt), + TREE_TYPE (arg1), arg1, NULL_TREE); + + do_pending_stack_adjust (); + op1 = expand_normal (arg1); + + enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg1)); + if (lhs) + target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); + + enum insn_code icode = optab_handler (negv3_optab, mode); + if (icode != CODE_FOR_nothing) + { + struct expand_operand ops[3]; + rtx last = get_last_insn (); + + res = gen_reg_rtx (mode); + create_output_operand (&ops[0], res, mode); + create_input_operand (&ops[1], op1, mode); + create_fixed_operand (&ops[2], do_error); + if (maybe_expand_insn (icode, 3, ops)) + { + last = get_last_insn (); + if (profile_status != PROFILE_ABSENT + && JUMP_P (last) + && any_condjump_p (last) + && !find_reg_note (last, REG_BR_PROB, 0)) + add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY); + emit_jump (done_label); + } + else + { + delete_insns_since (last); + icode = CODE_FOR_nothing; + } + } + + if (icode == CODE_FOR_nothing) + { + /* Compute the operation. On RTL level, the addition is always + unsigned. */ + res = expand_unop (mode, neg_optab, op1, NULL_RTX, false); + + /* Compare the operand with the most negative value. */ + rtx minv = expand_normal (TYPE_MIN_VALUE (TREE_TYPE (arg1))); + emit_cmp_and_jump_insns (op1, minv, NE, NULL_RTX, mode, false, + done_label, PROB_VERY_LIKELY); + } + + emit_label (do_error); + /* Expand the ubsan builtin call. */ + expand_normal (fn); + do_pending_stack_adjust (); + + /* We're done. */ + emit_label (done_label); + + if (lhs) + emit_move_insn (target, res); +} + +/* Add mul overflow checking to the statement STMT. */ + +void +ubsan_expand_si_overflow_mul_check (gimple stmt) +{ + rtx res, op0, op1; + tree lhs, fn, arg0, arg1; + rtx done_label, do_error, target = NULL_RTX; + + lhs = gimple_call_lhs (stmt); + arg0 = gimple_call_arg (stmt, 0); + arg1 = gimple_call_arg (stmt, 1); + done_label = gen_label_rtx (); + do_error = gen_label_rtx (); + fn = ubsan_build_overflow_builtin (MULT_EXPR, gimple_location (stmt), + TREE_TYPE (arg0), arg0, arg1); + + do_pending_stack_adjust (); + op0 = expand_normal (arg0); + op1 = expand_normal (arg1); + + enum machine_mode mode = TYPE_MODE (TREE_TYPE (arg0)); + if (lhs) + target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); + + enum insn_code icode = optab_handler (mulv4_optab, mode); + if (icode != CODE_FOR_nothing) + { + struct expand_operand ops[4]; + rtx last = get_last_insn (); + + res = gen_reg_rtx (mode); + create_output_operand (&ops[0], res, mode); + create_input_operand (&ops[1], op0, mode); + create_input_operand (&ops[2], op1, mode); + create_fixed_operand (&ops[3], do_error); + if (maybe_expand_insn (icode, 4, ops)) + { + last = get_last_insn (); + if (profile_status != PROFILE_ABSENT + && JUMP_P (last) + && any_condjump_p (last) + && !find_reg_note (last, REG_BR_PROB, 0)) + add_int_reg_note (last, REG_BR_PROB, PROB_VERY_UNLIKELY); + emit_jump (done_label); + } + else + { + delete_insns_since (last); + icode = CODE_FOR_nothing; + } + } + + if (icode == CODE_FOR_nothing) + { + struct separate_ops ops; + ops.op0 = arg0; + ops.op1 = arg1; + ops.op2 = NULL_TREE; + ops.location = gimple_location (stmt); + if (GET_MODE_2XWIDER_MODE (mode) != VOIDmode + && targetm.scalar_mode_supported_p (GET_MODE_2XWIDER_MODE (mode))) + { + enum machine_mode wmode = GET_MODE_2XWIDER_MODE (mode); + ops.code = WIDEN_MULT_EXPR; + ops.type + = build_nonstandard_integer_type (GET_MODE_PRECISION (wmode), 0); + + res = expand_expr_real_2 (&ops, NULL_RTX, wmode, EXPAND_NORMAL); + rtx hipart = expand_shift (RSHIFT_EXPR, wmode, res, + GET_MODE_PRECISION (mode), NULL_RTX, 0); + hipart = gen_lowpart (mode, hipart); + res = gen_lowpart (mode, res); + rtx signbit = expand_shift (RSHIFT_EXPR, mode, res, + GET_MODE_PRECISION (mode) - 1, + NULL_RTX, 0); + /* RES is low half of the double width result, HIPART + the high half. There was overflow if + HIPART is different from RES < 0 ? -1 : 0. */ + emit_cmp_and_jump_insns (signbit, hipart, EQ, NULL_RTX, mode, + false, done_label, PROB_VERY_LIKELY); + } + else + { + /* For now we don't instrument this. See __mulvDI3 in libgcc2.c + for what could be done. */ + ops.code = MULT_EXPR; + ops.type = TREE_TYPE (arg0); + res = expand_expr_real_2 (&ops, NULL_RTX, mode, EXPAND_NORMAL); + emit_jump (done_label); + } + } + + emit_label (do_error); + /* Expand the ubsan builtin call. */ + expand_normal (fn); + do_pending_stack_adjust (); + + /* We're done. */ + emit_label (done_label); + + if (lhs) + emit_move_insn (target, res); +} + +/* Expand UBSAN_CHECK_ADD call STMT. */ + +static void +expand_UBSAN_CHECK_ADD (gimple stmt) +{ + ubsan_expand_si_overflow_addsub_check (PLUS_EXPR, stmt); +} + +/* Expand UBSAN_CHECK_SUB call STMT. */ + +static void +expand_UBSAN_CHECK_SUB (gimple stmt) +{ + if (integer_zerop (gimple_call_arg (stmt, 0))) + ubsan_expand_si_overflow_neg_check (stmt); + else + ubsan_expand_si_overflow_addsub_check (MINUS_EXPR, stmt); +} + +/* Expand UBSAN_CHECK_MUL call STMT. */ + +static void +expand_UBSAN_CHECK_MUL (gimple stmt) +{ + ubsan_expand_si_overflow_mul_check (stmt); +} + /* Routines to expand each internal function, indexed by function number. Each routine has the prototype: diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 7193874c811..08a514eeec2 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -45,3 +45,6 @@ DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW) diff --git a/gcc/optabs.def b/gcc/optabs.def index 6b924acf8cd..cad96d2aea5 100644 --- a/gcc/optabs.def +++ b/gcc/optabs.def @@ -187,6 +187,10 @@ OPTAB_D (movcc_optab, "mov$acc") OPTAB_D (cmov_optab, "cmov$a6") OPTAB_D (cstore_optab, "cstore$a4") OPTAB_D (ctrap_optab, "ctrap$a4") +OPTAB_D (addv4_optab, "addv$I$a4") +OPTAB_D (subv4_optab, "subv$I$a4") +OPTAB_D (mulv4_optab, "mulv$I$a4") +OPTAB_D (negv3_optab, "negv$I$a3") OPTAB_D (smul_highpart_optab, "smul$a3_highpart") OPTAB_D (umul_highpart_optab, "umul$a3_highpart") diff --git a/gcc/opts.c b/gcc/opts.c index a0a6c53128c..7e61cbce7f8 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1460,6 +1460,8 @@ common_handle_option (struct gcc_options *opts, { "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 }, { "return", SANITIZE_RETURN, sizeof "return" - 1 }, { "null", SANITIZE_NULL, sizeof "null" - 1 }, + { "signed-integer-overflow", SANITIZE_SI_OVERFLOW, + sizeof "signed-integer-overflow" -1 }, { NULL, 0, 0 } }; const char *comma; diff --git a/gcc/predict.c b/gcc/predict.c index 822343b1295..1cd3fa621ea 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -74,14 +74,6 @@ along with GCC; see the file COPYING3. If not see static sreal real_zero, real_one, real_almost_one, real_br_prob_base, real_inv_br_prob_base, real_one_half, real_bb_freq_max; -/* Random guesstimation given names. - PROV_VERY_UNLIKELY should be small enough so basic block predicted - by it gets below HOT_BB_FREQUENCY_FRACTION. */ -#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1) -#define PROB_EVEN (REG_BR_PROB_BASE / 2) -#define PROB_VERY_LIKELY (REG_BR_PROB_BASE - PROB_VERY_UNLIKELY) -#define PROB_ALWAYS (REG_BR_PROB_BASE) - static void combine_predictions_for_insn (rtx, basic_block); static void dump_prediction (FILE *, enum br_predictor, int, basic_block, int); static void predict_paths_leading_to (basic_block, enum br_predictor, enum prediction); diff --git a/gcc/predict.h b/gcc/predict.h index 83b1695c65e..23435dc2c99 100644 --- a/gcc/predict.h +++ b/gcc/predict.h @@ -20,6 +20,16 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_PREDICT_H #define GCC_PREDICT_H +/* Random guesstimation given names. + PROB_VERY_UNLIKELY should be small enough so basic block predicted + by it gets below HOT_BB_FREQUENCY_FRACTION. */ +#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1) +#define PROB_EVEN (REG_BR_PROB_BASE / 2) +#define PROB_VERY_LIKELY (REG_BR_PROB_BASE - PROB_VERY_UNLIKELY) +#define PROB_ALWAYS (REG_BR_PROB_BASE) +#define PROB_UNLIKELY (REG_BR_PROB_BASE / 5 - 1) +#define PROB_LIKELY (PROB_ALWAYS - PROB_VERY_LIKELY) + #define DEF_PREDICTOR(ENUM, NAME, HITRATE, FLAGS) ENUM, enum br_predictor { diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 5bf1e3cebe6..9c94650321e 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -315,3 +315,19 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH, "__ubsan_handle_type_mismatch", BT_FN_VOID_PTR_PTR, ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW, + "__ubsan_handle_add_overflow", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW, + "__ubsan_handle_sub_overflow", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW, + "__ubsan_handle_mul_overflow", + BT_FN_VOID_PTR_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW, + "__ubsan_handle_negate_overflow", + BT_FN_VOID_PTR_PTR, + ATTR_COLD_NOTHROW_LEAF_LIST) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f05612efb72..6703e6a87dd 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,15 @@ 2013-12-04 Marek Polacek <polacek@redhat.com> + * c-c++-common/ubsan/overflow-mul-2.c: New test. + * c-c++-common/ubsan/overflow-add-1.c: New test. + * c-c++-common/ubsan/overflow-add-2.c: New test. + * c-c++-common/ubsan/overflow-mul-1.c: New test. + * c-c++-common/ubsan/overflow-sub-1.c: New test. + * c-c++-common/ubsan/overflow-sub-2.c: New test. + * c-c++-common/ubsan/overflow-negate-1.c: New test. + +2013-12-04 Marek Polacek <polacek@redhat.com> + PR c/54113 * gcc.dg/pr54113.c: New test. diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c b/gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c new file mode 100644 index 00000000000..436082d21d9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c @@ -0,0 +1,61 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SHRT_MAX __SHRT_MAX__ +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) + +void __attribute__((noinline,noclone)) +check (int i, int j) +{ + if (i != j) + __builtin_abort (); +} + +int +main (void) +{ +#if __INT_MAX__ == 2147483647 + /* Here, nothing should fail. */ + volatile int j = INT_MAX; + volatile int i = -1; + volatile int k = j + i; + check (k, 2147483646); + k = i + j; + check (k, 2147483646); + j--; + check (j, 2147483646); + + i = 1; + j = INT_MIN; + k = i + j; + check (k, -2147483647); + k = j + i; + check (k, -2147483647); + j++; + check (j, -2147483647); +#endif + + /* Test integer promotion. */ +#if __SCHAR_MAX__ == 127 + volatile signed char a = SCHAR_MAX; + volatile signed char b = 1; + volatile signed char c = a + b; + check (c, -128); + a++; + check (a, -128); +#endif + +#if __SHRT_MAX__ == 32767 + volatile short d = SHRT_MAX; + volatile short e = 1; + volatile short f = d + e; + check (f, -32768); + d++; + check (d, -32768); +#endif + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c b/gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c new file mode 100644 index 00000000000..de2cd2d0f88 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c @@ -0,0 +1,61 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-__LONG_MAX__ - 1L) +#define LLONG_MAX __LONG_LONG_MAX__ +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1L) + +int +main (void) +{ + volatile int j = INT_MAX; + volatile int i = 1; + volatile int k = j + i; + k = i + j; + j++; + j = INT_MAX - 100; + j += (1 << 10); + + j = INT_MIN; + i = -1; + k = i + j; + k = j + i; + j = INT_MIN + 100; + j += -(1 << 10); + + volatile long int m = LONG_MAX; + volatile long int n = 1; + volatile long int o = m + n; + o = n + m; + m++; + m = LONG_MAX - 100; + m += (1 << 10); + + m = LONG_MIN; + n = -1; + o = m + n; + o = n + m; + m = LONG_MIN + 100; + m += -(1 << 10); + + return 0; +} + +/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 2147483547 \\+ 1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -2147483648 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ \[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -\[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c b/gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c new file mode 100644 index 00000000000..0f2ea59df49 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c @@ -0,0 +1,47 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SHRT_MAX __SHRT_MAX__ +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) + +void __attribute__((noinline,noclone)) +check (int i, int j) +{ + if (i != j) + __builtin_abort (); +} + +int +main (void) +{ + /* Test integer promotion. */ +#if __SCHAR_MAX__ == 127 + volatile signed char a = -2; + volatile signed char b = SCHAR_MAX; + volatile signed char c = a * b; + check (c, 2); +#endif + +#if __SHRT_MAX__ == 32767 + volatile short d = SHRT_MAX; + volatile short e = 2; + volatile short f = d * e; + check (f, -2); +#endif + +#if __INT_MAX__ == 2147483647 + volatile int m = INT_MAX; + volatile int n = 1; + volatile int o = m * n; + check (o, INT_MAX); + + m = INT_MIN; + o = m * n; + check (o, INT_MIN); +#endif + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c b/gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c new file mode 100644 index 00000000000..adcbfe1a761 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define INT_MAX __INT_MAX__ +#define LONG_MAX __LONG_MAX__ + +int +main (void) +{ + volatile int j = INT_MAX; + volatile int i = 2; + volatile int k = j * i; + k = i * j; + + volatile long int m = LONG_MAX; + volatile long int n = 2; + volatile long int o = m * n; + o = n * m; + + return 0; +} + +/* { dg-output "signed integer overflow: 2147483647 \\* 2 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\* 2 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* \[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c b/gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c new file mode 100644 index 00000000000..9baada41007 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c @@ -0,0 +1,14 @@ +/* { dg-do run { target int128 } } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define INT_MIN (-__INT_MAX__ - 1) + +int +main (void) +{ + int j = INT_MIN; + return -j; +} + +/* { dg-output "negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c b/gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c new file mode 100644 index 00000000000..3b955279ba7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c @@ -0,0 +1,63 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define SCHAR_MAX __SCHAR_MAX__ +#define SCHAR_MIN (-__SCHAR_MAX__ - 1) +#define SHRT_MAX __SHRT_MAX__ +#define SHRT_MIN (-__SHRT_MAX__ - 1) +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) + +void __attribute__((noinline,noclone)) +check (int i, int j) +{ + if (i != j) + __builtin_abort (); +} + +int +main (void) +{ +#if __INT_MAX__ == 2147483647 + /* Here, nothing should fail. */ + volatile int i = -1; + volatile int j = INT_MIN; + volatile int k = j - i; + check (k, -2147483647); + k = i - j; + check (k, 2147483647); + j++; + check (j, -2147483647); + + i = 1; + j = INT_MAX; + k = i - j; + check (k, -2147483646); + k = j - i; + check (k, 2147483646); + j--; + check (k, 2147483646); +#endif + + /* Test integer promotion. */ +#if __SCHAR_MAX__ == 127 + volatile signed char a = SCHAR_MIN; + volatile signed char b = 1; + volatile signed char c = a - b; + check (c, 127); + a--; + check (a, 127); +#endif + +#if __SHRT_MAX__ == 32767 + volatile short d = SHRT_MIN; + volatile short e = 1; + volatile short f = d - e; + check (f, 32767); + d--; + check (d, 32767); +#endif + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c b/gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c new file mode 100644 index 00000000000..e06e3f6e891 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c @@ -0,0 +1,55 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-__INT_MAX__ - 1) +#define LONG_MAX __LONG_MAX__ +#define LONG_MIN (-__LONG_MAX__ - 1L) +#define LLONG_MAX __LONG_LONG_MAX__ +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1L) + +int +main (void) +{ + volatile int j = INT_MIN; + volatile int i = 1; + volatile int k = j - i; + j--; + j = INT_MIN + 100; + j -= (1 << 10); + + j = INT_MIN; + i = -1; + k = j - -i; + + i = INT_MIN + 1000; + i -= (1 << 20); + + volatile long int l = LONG_MIN; + volatile long int m = 1; + volatile long int n = l - m; + l--; + l = LONG_MIN + 100; + l -= (1 << 10); + + l = LONG_MIN; + m = -1; + n = l - -m; + + m = LONG_MIN + 1000; + m -= (1 << 20); + + return 0; +} + +/* { dg-output "signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -2147482648 \\+ -1048576 cannot be represented in type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1048576 cannot be represented in type 'long int'(\n|\r\n|\r)" } */ diff --git a/gcc/trans-mem.c b/gcc/trans-mem.c index 31dee7678d2..b2adc3d03e9 100644 --- a/gcc/trans-mem.c +++ b/gcc/trans-mem.c @@ -55,14 +55,9 @@ #include "gimple-pretty-print.h" #include "cfgloop.h" #include "tree-ssa-address.h" +#include "predict.h" -#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1) -#define PROB_VERY_LIKELY (PROB_ALWAYS - PROB_VERY_UNLIKELY) -#define PROB_UNLIKELY (REG_BR_PROB_BASE / 5 - 1) -#define PROB_LIKELY (PROB_ALWAYS - PROB_VERY_LIKELY) -#define PROB_ALWAYS (REG_BR_PROB_BASE) - #define A_RUNINSTRUMENTEDCODE 0x0001 #define A_RUNUNINSTRUMENTEDCODE 0x0002 #define A_SAVELIVEVARIABLES 0x0004 diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 89f2ffd111c..d98b7764b40 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -3757,6 +3757,47 @@ extract_range_basic (value_range_t *vr, gimple stmt) break; } } + else if (is_gimple_call (stmt) + && gimple_call_internal_p (stmt)) + { + enum tree_code subcode = ERROR_MARK; + switch (gimple_call_internal_fn (stmt)) + { + case IFN_UBSAN_CHECK_ADD: + subcode = PLUS_EXPR; + break; + case IFN_UBSAN_CHECK_SUB: + subcode = MINUS_EXPR; + break; + case IFN_UBSAN_CHECK_MUL: + subcode = MULT_EXPR; + break; + default: + break; + } + if (subcode != ERROR_MARK) + { + bool saved_flag_wrapv = flag_wrapv; + /* Pretend the arithmetics is wrapping. If there is + any overflow, we'll complain, but will actually do + wrapping operation. */ + flag_wrapv = 1; + extract_range_from_binary_expr (vr, subcode, type, + gimple_call_arg (stmt, 0), + gimple_call_arg (stmt, 1)); + flag_wrapv = saved_flag_wrapv; + + /* If for both arguments vrp_valueize returned non-NULL, + this should have been already folded and if not, it + wasn't folded because of overflow. Avoid removing the + UBSAN_CHECK_* calls in that case. */ + if (vr->type == VR_RANGE + && (vr->min == vr->max + || operand_equal_p (vr->min, vr->max, 0))) + set_value_range_to_varying (vr); + return; + } + } if (INTEGRAL_TYPE_P (type) && gimple_stmt_nonnegative_warnv_p (stmt, &sop)) set_value_range_to_nonnegative (vr, type, diff --git a/gcc/ubsan.c b/gcc/ubsan.c index e33e62a028a..aaf74acb546 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -41,9 +41,6 @@ along with GCC; see the file COPYING3. If not see #include "ubsan.h" #include "c-family/c-common.h" -/* From trans-mem.c. */ -#define PROB_VERY_UNLIKELY (REG_BR_PROB_BASE / 2000 - 1) - /* Map from a tree to a VAR_DECL tree. */ struct GTY(()) tree_type_map { @@ -632,6 +629,98 @@ instrument_null (gimple_stmt_iterator gsi, bool is_lhs) instrument_member_call (&gsi); } +/* Build an ubsan builtin call for the signed-integer-overflow + sanitization. CODE says what kind of builtin are we building, + LOC is a location, LHSTYPE is the type of LHS, OP0 and OP1 + are operands of the binary operation. */ + +tree +ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype, + tree op0, tree op1) +{ + tree data = ubsan_create_data ("__ubsan_overflow_data", loc, NULL, + ubsan_type_descriptor (lhstype, false), + NULL_TREE); + enum built_in_function fn_code; + + switch (code) + { + case PLUS_EXPR: + fn_code = BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW; + break; + case MINUS_EXPR: + fn_code = BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW; + break; + case MULT_EXPR: + fn_code = BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW; + break; + case NEGATE_EXPR: + fn_code = BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW; + break; + default: + gcc_unreachable (); + } + tree fn = builtin_decl_explicit (fn_code); + return build_call_expr_loc (loc, fn, 2 + (code != NEGATE_EXPR), + build_fold_addr_expr_loc (loc, data), + ubsan_encode_value (op0), + op1 ? ubsan_encode_value (op1) : NULL_TREE); +} + +/* Perform the signed integer instrumentation. GSI is the iterator + pointing at statement we are trying to instrument. */ + +static void +instrument_si_overflow (gimple_stmt_iterator gsi) +{ + gimple stmt = gsi_stmt (gsi); + tree_code code = gimple_assign_rhs_code (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree lhstype = TREE_TYPE (lhs); + tree a, b; + gimple g; + + /* If this is not a signed operation, don't instrument anything here. + Also punt on bit-fields. */ + if (!INTEGRAL_TYPE_P (lhstype) + || TYPE_OVERFLOW_WRAPS (lhstype) + || GET_MODE_BITSIZE (TYPE_MODE (lhstype)) != TYPE_PRECISION (lhstype)) + return; + + switch (code) + { + case MINUS_EXPR: + case PLUS_EXPR: + case MULT_EXPR: + /* Transform + i = u {+,-,*} 5; + into + i = UBSAN_CHECK_{ADD,SUB,MUL} (u, 5); */ + a = gimple_assign_rhs1 (stmt); + b = gimple_assign_rhs2 (stmt); + g = gimple_build_call_internal (code == PLUS_EXPR + ? IFN_UBSAN_CHECK_ADD + : code == MINUS_EXPR + ? IFN_UBSAN_CHECK_SUB + : IFN_UBSAN_CHECK_MUL, 2, a, b); + gimple_call_set_lhs (g, lhs); + gsi_replace (&gsi, g, false); + break; + case NEGATE_EXPR: + /* Represent i = -u; + as + i = UBSAN_CHECK_SUB (0, u); */ + a = build_int_cst (lhstype, 0); + b = gimple_assign_rhs1 (stmt); + g = gimple_build_call_internal (IFN_UBSAN_CHECK_SUB, 2, a, b); + gimple_call_set_lhs (g, lhs); + gsi_replace (&gsi, g, false); + break; + default: + break; + } +} + /* Gate and execute functions for ubsan pass. */ static unsigned int @@ -651,6 +740,10 @@ ubsan_pass (void) continue; } + if ((flag_sanitize & SANITIZE_SI_OVERFLOW) + && is_gimple_assign (stmt)) + instrument_si_overflow (gsi); + if (flag_sanitize & SANITIZE_NULL) { if (gimple_store_p (stmt)) @@ -668,7 +761,7 @@ ubsan_pass (void) static bool gate_ubsan (void) { - return flag_sanitize & SANITIZE_NULL; + return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW); } namespace { diff --git a/gcc/ubsan.h b/gcc/ubsan.h index 666e5fe15ab..0aced4aac67 100644 --- a/gcc/ubsan.h +++ b/gcc/ubsan.h @@ -43,6 +43,7 @@ extern tree ubsan_create_data (const char *, location_t, extern tree ubsan_type_descriptor (tree, bool); extern tree ubsan_encode_value (tree); extern bool is_ubsan_builtin_p (tree); +extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree); #endif /* GCC_UBSAN_H */ |