From patchwork Fri Nov 22 09:54:16 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 293383 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 223E12C00C3 for ; Fri, 22 Nov 2013 20:54:44 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=VFmenaca7/RsBE4ldA8qYlrJYkO0Vzj04U0YkHUqFHcsDCy2uj GuWLQZ8ank2ztn+9LY3ccXGRv5AxhsZ4SlumpZsS1fZmkPTdJhKkBPc47K+Y4yy+ mDOtCQopBZydRsbtuDrkcEVvCAK2pimmRopawPoZzCYkBfcrYqPH+vbuk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=r9t9APUrw0B1qZCMtDo1xHtr3qc=; b=MsDjhN+kpoK5b5wX1He6 d74x2EV+mtCIq9i+jk6wspMBdKt5aOVFUk6v6L+XGSruQMTAMiu6Vj61rcd6BzDU ECSdKaybrPJQVt/R7uYVA6YLP+E75kgvipTF2XXvE3uK4WzGFx+a5JusZLpfBukT ZUImviaKHj71AILVjeFdAzU= Received: (qmail 7341 invoked by alias); 22 Nov 2013 09:54:33 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 7332 invoked by uid 89); 22 Nov 2013 09:54:33 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.6 required=5.0 tests=AWL, BAYES_50, RDNS_NONE, SPF_HELO_PASS, SPF_PASS, URIBL_BLOCKED autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from Unknown (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 22 Nov 2013 09:54:29 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id rAM9sLj7025014 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 22 Nov 2013 04:54:21 -0500 Received: from redhat.com (ovpn-116-18.ams2.redhat.com [10.36.116.18]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id rAM9sG8r002998 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO); Fri, 22 Nov 2013 04:54:19 -0500 Date: Fri, 22 Nov 2013 10:54:16 +0100 From: Marek Polacek To: GCC Patches Cc: Jakub Jelinek Subject: [PATCH] Add signed integer overflow checking to ubsan Message-ID: <20131122095416.GG30062@redhat.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) Hi! Working virtually out of Pago Pago. The following is the implementation of the signed integer overflow checking for the UndefinedBehaviorSanitizer. I wrote some of the generic bits; Jakub did the i?86 handlind/optabs as well as VRP/fold bits. In case we can use the i?86 optab handler, we make use e.g. of jo instructions (jump if overflow), which is really cool. Otherwise, we fall back to generic handling: we emit jmps, labels, etc. The logic behind this actually matches what libgcc does, see e.g. __subv* and __addv* routines. As in NULL pointer checking, we first emit internal fn calls that are expanded later on in the pipeline. That is, in ubsan pass, we transform int i = a + 5; into int i = UBSAN_CHECK_ADD (a, 5); It turned out that this instrumentation sometimes doesn't play very well with -fstrict-overflow - it could result in false error of overflow for instance on multiplying two chars - we have to take the integer promotion into account. So, this was special cased in c_gimplify_expr. Other than that, some other issues/observations: 1) currently, we seem to miscompile some code with -Os. That's why I skipped -Os in some of the test. Side-note: we also skip testing with LTO; LTO currently can't handle internal fn calls right - perhaps it shouldn't emit the cgraph nodes for it at all. 2) long long int on -m32 isn't tested and we ICE on it. But -m64 seems to be fine. 3) for integer overflow checking we will want to thwart some of the folding in the C FE. E.g., I think int a = INT_MAX + 1; is folded in the FE and thus ubsan doesn't detect because it doesn't see any PLUS_EXPR. 4) I wanted to test also __int128_t, but haven't time to complete the testcase. We should test long long int. 5) Try bootstrap-ubsan. I expect the bootstrap-ubsan to find a handful of bugs. 6) Documentation. I'll post a separate fix for that. Regtested/bootstrapped on x86_64-linux. 2013-11-22 Jakub Jelinek Marek Polacek * opts.c (common_handle_option): Handle -fsanitize=signed-integer-overflow. * config/i386/i386.md (addv4, subv4, mulv4, negv3, negv3_1): Define expands. (*addv4, *subv4, *mulv4, *negv3): 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. c-family/ * c-gimplify.c (c_gimplify_expr): If doing the integer-overflow sanitization, call unsigned_type_for only when !TYPE_OVERFLOW_WRAPS. testsuite/ * 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. Marek --- gcc/opts.c.mp 2013-11-21 14:08:55.029796721 +0100 +++ gcc/opts.c 2013-11-21 14:09:00.526816708 +0100 @@ -1458,6 +1458,8 @@ common_handle_option (struct gcc_options sizeof "unreachable" - 1 }, { "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 }, { "null", SANITIZE_NULL, sizeof "null" - 1 }, + { "signed-integer-overflow", SANITIZE_SI_OVERFLOW, + sizeof "signed-integer-overflow" -1 }, { NULL, 0, 0 } }; const char *comma; --- gcc/config/i386/i386.md.mp 2013-11-21 15:28:13.967939859 +0100 +++ gcc/config/i386/i386.md 2013-11-21 19:29:42.438417911 +0100 @@ -6198,6 +6198,42 @@ [(set_attr "type" "alu") (set_attr "mode" "QI")]) +(define_mode_attr widerintmode [(QI "HI") (HI "SI") (SI "DI") (DI "TI")]) + +;; Add with jump on overflow. +(define_expand "addv4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (plus: + (sign_extend: + (match_operand:SWI 1 "register_operand")) + (sign_extend: + (match_operand:SWI 2 ""))) + (sign_extend: + (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)))] + "") + +(define_insn "*addv4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (plus: + (sign_extend: + (match_operand:SWI 1 "nonimmediate_operand" "%0,0")) + (sign_extend: + (match_operand:SWI 2 "" ","))) + (sign_extend: + (plus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "nonimmediate_operand" "=,m") + (plus:SWI (match_dup 1) (match_dup 2)))] + "ix86_binary_operator_ok (PLUS, mode, operands)" + "add{}\t{%2, %0|%0, %2}" + [(set_attr "type" "alu") + (set_attr "mode" "")]) + ;; The lea patterns for modes less than 32 bits need to be matched by ;; several insns converted to real lea by splitters. @@ -6435,6 +6471,40 @@ [(set_attr "type" "alu") (set_attr "mode" "SI")]) +;; Subtract with jump on overflow. +(define_expand "subv4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (minus: + (sign_extend: + (match_operand:SWI 1 "register_operand")) + (sign_extend: + (match_operand:SWI 2 ""))) + (sign_extend: + (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)))] + "") + +(define_insn "*subv4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (minus: + (sign_extend: + (match_operand:SWI 1 "nonimmediate_operand" "0,0")) + (sign_extend: + (match_operand:SWI 2 "" ",m"))) + (sign_extend: + (minus:SWI (match_dup 1) (match_dup 2))))) + (set (match_operand:SWI 0 "nonimmediate_operand" "=m,") + (minus:SWI (match_dup 1) (match_dup 2)))] + "ix86_binary_operator_ok (MINUS, mode, operands)" + "sub{}\t{%2, %0|%0, %2}" + [(set_attr "type" "alu") + (set_attr "mode" "")]) + (define_insn "*sub_3" [(set (reg FLAGS_REG) (compare (match_operand:SWI 1 "nonimmediate_operand" "0,0") @@ -6749,6 +6819,59 @@ (set_attr "bdver1_decode" "direct") (set_attr "mode" "QI")]) +;; Multiply with jump on overflow. +(define_expand "mulv4" + [(parallel [(set (reg:CCO FLAGS_REG) + (eq:CCO (mult: + (sign_extend: + (match_operand:SWI48 1 "register_operand")) + (sign_extend: + (match_operand:SWI48 2 ""))) + (sign_extend: + (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 "*mulv4" + [(set (reg:CCO FLAGS_REG) + (eq:CCO (mult: + (sign_extend: + (match_operand:SWI 1 "nonimmediate_operand" "%rm,rm,0")) + (sign_extend: + (match_operand:SWI 2 "" "K,,mr"))) + (sign_extend: + (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{}\t{%2, %1, %0|%0, %1, %2} + imul{}\t{%2, %1, %0|%0, %1, %2} + imul{}\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" "")]) + (define_expand "mul3" [(parallel [(set (match_operand: 0 "register_operand") (mult: @@ -8662,6 +8785,49 @@ [(set_attr "type" "negnot") (set_attr "mode" "SI")]) +;; Negate with jump on overflow. +(define_expand "negv3" + [(parallel [(set (reg:CCO FLAGS_REG) + (ne:CCO (match_operand:SWI 1 "register_operand") + (const_int 0))) + (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)))] + "" +{ + rtx minv = GEN_INT (HOST_WIDE_INT_M1U + << (GET_MODE_BITSIZE (mode) - 1)); + emit_insn (gen_negv3_1 (operands[0], operands[1], minv, operands[2])); + DONE; +}) + +(define_expand "negv3_1" + [(parallel [(set (reg:CCO FLAGS_REG) + (ne:CCO (match_operand:SWI 1 "nonimmediate_operand") + (match_operand:SWI 2 "const_int_operand"))) + (set (match_operand:SWI 0 "nonimmediate_operand") + (neg:SWI (match_dup 1)))]) + (set (pc) (if_then_else + (eq (reg:CCO FLAGS_REG) (const_int 0)) + (label_ref (match_operand 3)) + (pc)))] + "") + +(define_insn "*negv3" + [(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" "=m") + (neg:SWI (match_dup 1)))] + "ix86_unary_operator_ok (NEG, mode, operands) + && mode_signbit_p (mode, operands[2])" + "neg{}\t%0" + [(set_attr "type" "negnot") + (set_attr "mode" "")]) + ;; Changing of sign for FP values is doable using integer unit too. (define_expand "2" --- gcc/c-family/c-gimplify.c.mp 2013-11-21 19:44:50.366036968 +0100 +++ gcc/c-family/c-gimplify.c 2013-11-22 01:10:27.725635249 +0100 @@ -194,7 +194,9 @@ c_gimplify_expr (tree *expr_p, gimple_se 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); } --- gcc/sanitizer.def.mp 2013-11-21 14:08:55.030796725 +0100 +++ gcc/sanitizer.def 2013-11-21 17:45:38.701245878 +0100 @@ -305,3 +305,19 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN "__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) --- gcc/ubsan.h.mp 2013-11-21 14:08:55.032796733 +0100 +++ gcc/ubsan.h 2013-11-21 14:09:00.529816720 +0100 @@ -21,6 +21,12 @@ along with GCC; see the file COPYING3. #ifndef GCC_UBSAN_H #define GCC_UBSAN_H +/* From predict.c. */ +#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) + /* The various kinds of NULL pointer checks. */ enum ubsan_null_ckind { UBSAN_LOAD_OF, @@ -43,6 +49,7 @@ extern tree ubsan_create_data (const cha 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 */ --- gcc/gimple-fold.c.mp 2013-11-21 14:22:39.227998786 +0100 +++ gcc/gimple-fold.c 2013-11-21 17:45:38.510245061 +0100 @@ -2654,8 +2654,30 @@ gimple_fold_stmt_to_constant_1 (gimple s 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 --- gcc/ubsan.c.mp 2013-11-21 14:08:55.031796729 +0100 +++ gcc/ubsan.c 2013-11-22 09:57:48.499716221 +0100 @@ -39,9 +39,6 @@ along with GCC; see the file COPYING3. #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 { @@ -628,6 +625,98 @@ instrument_null (tree *tp, int * /*walk_ return NULL_TREE; } +/* 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 @@ -640,7 +729,6 @@ ubsan_pass (void) { for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);) { - struct walk_stmt_info wi; gimple stmt = gsi_stmt (gsi); if (is_gimple_debug (stmt)) { @@ -648,9 +736,18 @@ ubsan_pass (void) continue; } - memset (&wi, 0, sizeof (wi)); - wi.gsi = gsi; - walk_gimple_op (stmt, instrument_null, &wi); + if (flag_sanitize & SANITIZE_SI_OVERFLOW + && is_gimple_assign (stmt)) + instrument_si_overflow (gsi); + + if (flag_sanitize & SANITIZE_NULL) + { + struct walk_stmt_info wi; + memset (&wi, 0, sizeof (wi)); + wi.gsi = gsi; + walk_gimple_op (stmt, instrument_null, &wi); + } + gsi_next (&gsi); } } @@ -660,7 +757,7 @@ ubsan_pass (void) static bool gate_ubsan (void) { - return flag_sanitize & SANITIZE_NULL; + return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW); } namespace { --- gcc/flag-types.h.mp 2013-11-21 14:08:55.026796709 +0100 +++ gcc/flag-types.h 2013-11-21 14:09:00.517816668 +0100 @@ -212,8 +212,9 @@ enum sanitize_code { SANITIZE_UNREACHABLE = 1 << 4, SANITIZE_VLA = 1 << 5, SANITIZE_NULL = 1 << 6, + SANITIZE_SI_OVERFLOW = 1 << 7, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE - | SANITIZE_VLA | SANITIZE_NULL + | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_SI_OVERFLOW }; /* flag_vtable_verify initialization levels. */ --- gcc/internal-fn.c.mp 2013-11-21 14:08:55.027796713 +0100 +++ gcc/internal-fn.c 2013-11-22 10:04:19.796117746 +0100 @@ -26,6 +26,8 @@ along with GCC; see the file COPYING3. #include "expr.h" #include "optabs.h" #include "gimple.h" +#include "ubsan.h" +#include "target.h" /* The names of each internal function, indexed by function number. */ const char *const internal_fn_name_array[] = { @@ -148,6 +150,300 @@ expand_UBSAN_NULL (gimple stmt ATTRIBUTE 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); + 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); + + /* 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); + + 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); + res = target; + } + 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); + + /* 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); + + 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); + + /* 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: --- gcc/fold-const.c.mp 2013-11-21 14:22:49.239034849 +0100 +++ gcc/fold-const.c 2013-11-21 14:23:48.426249349 +0100 @@ -10330,14 +10330,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, --- gcc/internal-fn.def.mp 2013-11-21 14:08:55.028796717 +0100 +++ gcc/internal-fn.def 2013-11-21 17:45:38.703245887 +0100 @@ -45,3 +45,6 @@ DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST 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) --- gcc/tree-vrp.c.mp 2013-11-21 14:22:35.167984172 +0100 +++ gcc/tree-vrp.c 2013-11-21 17:45:38.595245432 +0100 @@ -3748,6 +3748,40 @@ extract_range_basic (value_range_t *vr, 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, --- gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c.mp 2013-11-22 00:58:49.911784181 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-mul-2.c 2013-11-22 01:35:31.796587252 +0100 @@ -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)" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c.mp 2013-11-21 14:36:31.997032821 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-add-1.c 2013-11-21 21:01:38.356269474 +0100 @@ -0,0 +1,64 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ +/* { dg-skip-if "" { *-*-* } { "-Os" } { "" } } */ + +// TODO: This is miscompiled with -Os! + +#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; +} --- gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c.mp 2013-11-21 15:20:22.148184380 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-add-2.c 2013-11-21 18:11:09.883041430 +0100 @@ -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)" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c.mp 2013-11-21 23:57:24.320188104 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-mul-1.c 2013-11-22 01:36:27.662805713 +0100 @@ -0,0 +1,50 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ +/* { dg-skip-if "" { *-*-* } { "-Os" } { "" } } */ + +// TODO: This is miscompiled with -Os! + +#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; +} --- gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c.mp 2013-11-21 18:11:45.957184230 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-sub-1.c 2013-11-21 21:13:49.582029454 +0100 @@ -0,0 +1,66 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */ +/* { dg-skip-if "" { *-*-* } { "-Os" } { "" } } */ + +// TODO: This is miscompiled with -Os! + +#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; +} --- gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c.mp 2013-11-21 18:16:18.730175229 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-sub-2.c 2013-11-21 18:48:29.099778507 +0100 @@ -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)" } */ --- gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c.mp 2013-11-22 01:44:01.284928783 +0100 +++ gcc/testsuite/c-c++-common/ubsan/overflow-negate-1.c 2013-11-22 01:45:21.526246391 +0100 @@ -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)" } */ --- gcc/optabs.def.mp 2013-11-21 14:22:44.109016365 +0100 +++ gcc/optabs.def 2013-11-21 17:45:38.692245840 +0100 @@ -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")