[PATCH] Add signed integer overflow checking to ubsan

Marek Polacek polacek@redhat.com
Fri Nov 22 12:03:00 GMT 2013


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  <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.
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.

--- 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 "addv<mode>4"
+  [(parallel [(set (reg:CCO FLAGS_REG)
+		   (eq:CCO (plus:<widerintmode>
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI 1 "register_operand"))
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI 2 "<general_operand>")))
+			   (sign_extend:<widerintmode>
+			      (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 "*addv<mode>4"
+  [(set (reg:CCO FLAGS_REG)
+	(eq:CCO (plus:<widerintmode>
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 1 "nonimmediate_operand" "%0,0"))
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 2 "<general_operand>" "<g>,<r><i>")))
+		(sign_extend:<widerintmode>
+		   (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.
 
@@ -6435,6 +6471,40 @@
   [(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:<widerintmode>
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI 1 "register_operand"))
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI 2 "<general_operand>")))
+			   (sign_extend:<widerintmode>
+			      (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 "*subv<mode>4"
+  [(set (reg:CCO FLAGS_REG)
+	(eq:CCO (minus:<widerintmode>
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 1 "nonimmediate_operand" "0,0"))
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 2 "<general_operand>" "<r><i>,<r>m")))
+		(sign_extend:<widerintmode>
+		   (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")
@@ -6749,6 +6819,59 @@
    (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:<widerintmode>
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI48 1 "register_operand"))
+			      (sign_extend:<widerintmode>
+				 (match_operand:SWI48 2 "<general_operand>")))
+			   (sign_extend:<widerintmode>
+			      (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:<widerintmode>
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 1 "nonimmediate_operand" "%rm,rm,0"))
+		   (sign_extend:<widerintmode>
+		      (match_operand:SWI 2 "<general_operand>" "K,<i>,mr")))
+		(sign_extend:<widerintmode>
+		   (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>
@@ -8662,6 +8785,49 @@
   [(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")
+			   (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>mode) - 1));
+  emit_insn (gen_negv<mode>3_1 (operands[0], operands[1], minv, operands[2]));
+  DONE;
+})
+
+(define_expand "negv<mode>3_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 "*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"
--- 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")

	Marek



More information about the Gcc-patches mailing list