This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC][Draft patch] Introduce IntegerSanitizer in GCC.


CC'ing Jakub, Marek and Kostya, sanitizer maintainers in GCC.

On 04/07/16 14:12, Maxim Ostapenko wrote:
Hi!

Although in languages like C and C++ unsigned integer overflow reliably wraps around and well defined, sometimes it may indicate an error in code and lead to undesirable consequences and even security vulnerabilities (https://android-developers.blogspot.ru/2016/05/hardening-media-stack.html). Clang has corresponding '-fsanitize=unsigned-integer-overflow' option that may catch some of these errors (but misses some important cases such as pointers overflow), GCC has not and instruments only signed integer operations (S = S {+, -, *} S).

Although GCC has a nice framework for instrumenting even "mixed" binary operations (e.g. S = U + S) in middle end, implemented by Jakub some time ago (see expand_addsub_overflow, expand_mul_overflow etc in internal-fn.c), adding support for sanitizing unsigned and "mixed" binary operations seems to be quite problematic in terms of huge amount of false positive reports that may pop up in following cases:

1) Some code intentionally relies on overflows, e.g. when computing hash codes. Unfortunately, it seems that we cannot do anything with it on compiler side, programmer will need to do some filtering here by himself.

2) Unfortunately, it seems that C and C++ FEs don't provide suitable interfaces to get *LHS* real type when building binary expressions, so *build_binary_op* doesn't know about *LHS* real type and we can easily end up with false positive report e.g. in such code:

int
cmp (const char *s1, const char *s2)
{
  return strlen (s1) - strlen (s2);
}

because in gimple we would have something like this:

  unsigned int tmp, len1, len2;
  int result;
  .........
  tmp = len1 - len2;  <===== FP here if len1 < len2
  result = (int) tmp;
  return result;

3) GCC relies on unsigned integer overflows when constructing POINTER_PLUS expressions. We need a special treatment for this case, because it's quite common to have a negative offset from a given pointer and we don't want have false reports here.

This very draft patch (of course we would need more testcases, changelog entry etc) adds support for '-fsanitize=unsigned-integer-overflow' to GCC for PLUS, MINUS and MULT operations, extending its functionality to support pointers overflow and binary operations with different signess of theirs operands ("mixed" binary operations, say, S = U + S). It simply replaces all +, - and * by new ISAN_CHECK_{ADD, SUB, MUL} intrinsics in C and C++ FEs to be lowered later during intrinsics expansion. For example, if we have a + b expression, it would be replaced in FE by following call:

a + b -> ISAN_CHECK_ADD (a, b, a_unsiged_p, b_unsigned_p, result_unsigned_p)

where *a_unsiged_p* is 1 if *a* is unsigned and 0 otherwise. The same rule applies for *b_unsigned_p*. The *result_unsigned_p* value should reflect result type signess, but as mentioned in 2), we don't know it when building binary expression, so we can only guess it from arguments types and maybe change it (in a very hackish way) later (see *isan_maybe_change_ifn_sign_arg* function) in order to reduce number of FPs (in fact, these hacks can significantly reduce FPs ratio, ~ 30%). These values will be used in *expand_addsub_overflow* and *expand_mul_overflow* functions to compute overflow correctly.

This patch survives GCC bootstrap, I've also managed to build Firefox and Chromium with '-fsanitize=unsigned-integer-overflow' enabled. The tool found several bugs in opensource software:

1) Integer overflow when computing len parameter in "file" utility (http://bugs.gw.com/view.php?id=555) 2) Pointer overflow (nullptr - 1) in Google's skia library (https://bugs.chromium.org/p/skia/issues/detail?id=5415) 3) Unsigned integer overflow in HAL component in Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=1280514)

Is community interested in such a tool? Any feedback would be greatly appreciated, especially some points how we can figure out real result type for *LHS* of binary expressions in *build_binary_op* in order to avoid unnecessary FPs and ugly hacks in FEs.

Thanks,
-Maxim

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 5d234a5..1cdba47 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -6838,6 +6838,7 @@ builtin_mathfn_code (const_tree t)
   const_call_expr_arg_iterator iter;
 
   if (TREE_CODE (t) != CALL_EXPR
+      || !CALL_EXPR_FN (t)
       || TREE_CODE (CALL_EXPR_FN (t)) != ADDR_EXPR)
     return END_BUILTINS;
 
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 3301c31..29c7ce1 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -4248,10 +4248,18 @@ pointer_int_sum (location_t loc, enum tree_code resultcode,
 		 tree ptrop, tree intop, bool complain)
 {
   tree size_exp, ret;
+  HOST_WIDE_INT op1 = 0;
 
   /* The result is a pointer of the same type that is being added.  */
   tree result_type = TREE_TYPE (ptrop);
 
+  if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+    {
+      /* First, try to avoid FPs if INTOP is negative constant.  */
+      if (tree_fits_shwi_p (intop))
+	op1 = tree_to_shwi (intop);
+    }
+
   if (TREE_CODE (TREE_TYPE (result_type)) == VOID_TYPE)
     {
       if (complain && warn_pointer_arith)
@@ -4329,11 +4337,34 @@ pointer_int_sum (location_t loc, enum tree_code resultcode,
       intop = wide_int_to_tree (TREE_TYPE (intop), intop);
   }
 
-  /* Create the sum or difference.  */
-  if (resultcode == MINUS_EXPR)
-    intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
+  if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+    {
+      tree tmp = NULL_TREE;
+      /* First, try to avoid FPs if INTOP is negative constant.  */
+      if (op1 != 0)
+	{
+	  if (op1 < 0)
+	    {
+	      resultcode = (resultcode == PLUS_EXPR ? MINUS_EXPR : PLUS_EXPR);
+	      op1 = -op1;
+	    }
+	  tmp = build_int_cst (sizetype, op1);
+	  intop = build_binary_op (loc, MULT_EXPR, tmp,
+				   convert (TREE_TYPE (intop), size_exp), 1);
+	}
+      tmp = build_binary_op (loc, resultcode,
+			     convert (sizetype, ptrop),
+			     convert (long_integer_type_node, intop), 1);
+      ret = convert (result_type, tmp);
+    }
+  else
+    {
+       /* Create the sum or difference.  */
+       if (resultcode == MINUS_EXPR)
+	 intop = fold_build1_loc (loc, NEGATE_EXPR, sizetype, intop);
 
-  ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+       ret = fold_build_pointer_plus_loc (loc, ptrop, intop);
+    }
 
   fold_undefer_and_ignore_overflow_warnings ();
 
@@ -9759,7 +9790,9 @@ check_function_arguments_recurse (void (*callback)
 
   if (TREE_CODE (param) == CALL_EXPR)
     {
-      tree type = TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (param)));
+      tree type
+        = CALL_EXPR_FN (param) ? TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (param)))
+			       : TREE_TYPE (param);
       tree attrs;
       bool found_format_arg = false;
 
diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c
index 4022bdf..82ecc5b 100644
--- a/gcc/c-family/c-ubsan.c
+++ b/gcc/c-family/c-ubsan.c
@@ -456,3 +456,90 @@ ubsan_maybe_instrument_member_call (tree stmt, bool is_ctor)
   if (op)
     CALL_EXPR_ARG (stmt, 0) = op;
 }
+
+/* Perform the unsigned and signed integer instrumentation for binary
+   operation CODE.  LOC is the source location, RESULT_TYPE is computed type
+   for result of expression, OP0 and OP1 are corresponding operands.
+   UNS0_P and UNS1_P are bool flags indicating whether original OP0 and OP1
+   were signed or unsigned.  */
+
+tree
+isan_maybe_instrument_ui (location_t loc, enum tree_code code,
+			  tree result_type, tree op0, tree op1,
+			  int uns0_p, int uns1_p)
+{
+  tree op0type = TREE_TYPE (op0);
+  tree op1type = TREE_TYPE (op1);
+
+  tree op0_uns_p = build_int_cst (integer_type_node, uns0_p);
+  tree op1_uns_p = build_int_cst (integer_type_node, uns1_p);
+  tree res_uns_p = build_int_cst (integer_type_node,
+				  TYPE_UNSIGNED (result_type));
+
+  /* TODO: instrument even two constants.  We don't do it right now to avoid
+     nasty errors in array bounds computations such as A[1 + 2 * sizeof (int)].
+   */
+  if ((TREE_CODE (op0) == INTEGER_CST) && (TREE_CODE (op1) == INTEGER_CST))
+    return NULL_TREE;
+
+  /* TODO: instrument for different precision modes.  */
+  if ((TYPE_PRECISION (op0type) != TYPE_PRECISION (result_type))
+       || (TYPE_PRECISION (op1type) != TYPE_PRECISION (result_type)))
+    return NULL_TREE;
+
+  /* If both operands are signed, don't instrument anything here.
+     Also punt on bit-fields.  */
+  if (!INTEGRAL_TYPE_P (op0type) || !INTEGRAL_TYPE_P (op1type)
+      || (!TYPE_OVERFLOW_WRAPS (op0type) && !TYPE_OVERFLOW_WRAPS (op1type))
+      || GET_MODE_BITSIZE (TYPE_MODE (op0type)) != TYPE_PRECISION (op0type)
+      || GET_MODE_BITSIZE (TYPE_MODE (op1type)) != TYPE_PRECISION (op1type))
+    return NULL_TREE;
+
+  switch (code)
+    {
+    case MULT_EXPR:
+    case MINUS_EXPR:
+    case PLUS_EXPR:
+      /* Transform
+	 i = u {+,-,*} 5;
+	 into
+	 i = ISAN_CHECK_{ADD,SUB,MUL} (u, 5);  */
+	 return build_call_expr_internal_loc (loc, code == PLUS_EXPR
+						   ? IFN_ISAN_CHECK_ADD
+						   : code == MINUS_EXPR
+						   ? IFN_ISAN_CHECK_SUB
+						   : IFN_ISAN_CHECK_MUL,
+					     result_type, 5, op0, op1,
+					     op0_uns_p, op1_uns_p,
+					     res_uns_p);
+    default:
+      break;
+    }
+  return NULL_TREE;
+}
+
+/* Return TRUE iff FN is ISAN_CHECK_{ADD,SUB,MUL} internal function.  */
+
+bool
+isan_internal_fn_p (tree fn)
+{
+  if (!fn || TREE_CODE (fn) != CALL_EXPR)
+    return false;
+
+  enum internal_fn ifn = CALL_EXPR_IFN (fn);
+  return ifn == IFN_ISAN_CHECK_ADD || ifn == IFN_ISAN_CHECK_SUB
+	 ||ifn == IFN_ISAN_CHECK_MUL;
+}
+
+/* Change fourth (RES_UNS_P) parameter of ISAN_CHECK_{ADD,SUB,MUL} function
+   to UNS_P value.  This is an extremely hacky way to avoid false psoitive
+   reports,  because in many cases we simply cannot set RES_UNS_P value in
+   isan_maybe_instrument_ui correctly since we dont' see LHS and its type.  */
+
+void
+isan_maybe_change_ifn_sign_arg (tree *call, int uns_p)
+{
+  tree *opp = &CALL_EXPR_ARG (*call, 4);
+  tree op_new = build_int_cst (integer_type_node, uns_p);
+  *opp = op_new;
+}
diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h
index 30d4f97..a8202c0 100644
--- a/gcc/c-family/c-ubsan.h
+++ b/gcc/c-family/c-ubsan.h
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.  If not see
 
 extern tree ubsan_instrument_division (location_t, tree, tree);
 extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
+extern tree isan_maybe_instrument_ui (location_t, enum tree_code,
+				      tree, tree, tree, int, int);
 extern tree ubsan_instrument_vla (location_t, tree);
 extern tree ubsan_instrument_return (location_t);
 extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
@@ -33,5 +35,7 @@ extern void ubsan_maybe_instrument_member_call (tree, bool);
 
 /* Declare this here as well as in ubsan.h. */
 extern bool do_ubsan_in_current_function (void);
+extern bool isan_internal_fn_p (tree fn);
+extern void isan_maybe_change_ifn_sign_arg (tree *, int);
 
 #endif  /* GCC_C_UBSAN_H  */
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 7f491f1..23c899a 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "trans-mem.h"
 #include "c-family/c-pragma.h"
+#include "c-family/c-ubsan.h"
 #include "c-lang.h"
 #include "c-family/c-objc.h"
 #include "plugin.h"
@@ -263,6 +264,8 @@ struct GTY(()) c_parser {
 
 static GTY (()) c_parser *the_parser;
 
+bool processing_static_variable;
+
 /* Read in and lex a single token, storing it in *TOKEN.  */
 
 static void
@@ -1969,6 +1972,10 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 		     its initializer is parsed.  */
 		  d = start_decl (declarator, specs, true,
 				  chainon (postfix_attrs, all_prefix_attrs));
+
+		  if (d && TREE_STATIC (d))
+		    processing_static_variable = true;
+
 		  if (!d)
 		    d = error_mark_node;
 		  if (omp_declare_simd_clauses.exists ()
@@ -1978,7 +1985,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 		  start_init (d, asm_name, global_bindings_p ());
 		  init_loc = c_parser_peek_token (parser)->location;
 		  init = c_parser_initializer (parser);
+		  if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+		       && isan_internal_fn_p (init.value))
+		    isan_maybe_change_ifn_sign_arg (&init.value,
+						     TYPE_UNSIGNED (TREE_TYPE (d)));
 		  finish_init ();
+		  processing_static_variable = false;
 		}
 	      if (oacc_routine_clauses)
 		c_finish_oacc_routine (parser, d, oacc_routine_clauses,
@@ -5269,6 +5281,12 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
 	      location_t xloc = c_parser_peek_token (parser)->location;
 	      struct c_expr expr = c_parser_expression_conv (parser);
 	      mark_exp_read (expr.value);
+	      if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+		   && isan_internal_fn_p (expr.value))
+		{
+		  int uns_p = TYPE_UNSIGNED (TREE_TYPE (DECL_RESULT (current_function_decl)));
+		  isan_maybe_change_ifn_sign_arg (&expr.value, uns_p);
+		}
 	      stmt = c_finish_return (EXPR_LOC_OR_LOC (expr.value, xloc),
 				      expr.value, expr.original_type);
 	      goto expect_semicolon;
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 7c6241c..40b1286 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -72,6 +72,8 @@ int in_typeof;
    if expr.original_code == SIZEOF_EXPR.  */
 tree c_last_sizeof_arg;
 
+extern bool processing_static_variable;
+
 /* Nonzero if we might need to print a "missing braces around
    initializer" message within this initializer.  */
 static int found_missing_braces;
@@ -5618,6 +5620,13 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
 
   newrhs = rhs;
 
+  if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+       && isan_internal_fn_p (rhs))
+    {
+      tree lhs_type = lhs_origtype ? lhs_origtype : TREE_TYPE (lhs);
+      isan_maybe_change_ifn_sign_arg (&newrhs, TYPE_UNSIGNED (lhs_type));
+    }
+
   if (TREE_CODE (lhs) == C_MAYBE_CONST_EXPR)
     {
       tree inner = build_modify_expr (location, C_MAYBE_CONST_EXPR_EXPR (lhs),
@@ -5655,6 +5664,12 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
 	    }
 	  newrhs = build_binary_op (location,
 				    modifycode, lhs, newrhs, 1);
+	  if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+	       && isan_internal_fn_p (newrhs))
+	    {
+	      tree lhs_type = lhs_origtype ? lhs_origtype : TREE_TYPE (lhs);
+	      isan_maybe_change_ifn_sign_arg (&newrhs, TYPE_UNSIGNED (lhs_type));
+	    }
 
 	  /* The original type of the right hand side is no longer
 	     meaningful.  */
@@ -10637,6 +10652,10 @@ build_binary_op (location_t location, enum tree_code code,
   /* Remember whether we're doing << or >>.  */
   bool doing_shift = false;
 
+  bool doing_plus = false;
+  bool doing_minus = false;
+  bool doing_mul = false;
+
   /* Tree holding instrumentation expression.  */
   tree instrument_expr = NULL;
 
@@ -10817,7 +10836,10 @@ build_binary_op (location_t location, enum tree_code code,
 	  goto return_build_binary_op;
 	}
       else
-	common = 1;
+	{
+	  doing_plus = true;
+	  common = 1;
+	}
       break;
 
     case MINUS_EXPR:
@@ -10836,10 +10858,14 @@ build_binary_op (location_t location, enum tree_code code,
 	  goto return_build_binary_op;
 	}
       else
-	common = 1;
+	{
+	  doing_minus = true;
+	  common = 1;
+	}
       break;
 
     case MULT_EXPR:
+      doing_mul = true;
       common = 1;
       break;
 
@@ -11669,6 +11695,20 @@ build_binary_op (location_t location, enum tree_code code,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
+  if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+       && (doing_plus || doing_minus || doing_mul)
+       && !processing_static_variable)
+    {
+      op0 = c_save_expr (op0);
+      op1 = c_save_expr (op1);
+      op0 = c_fully_fold (op0, false, NULL);
+      op1 = c_fully_fold (op1, false, NULL);
+      instrument_expr = isan_maybe_instrument_ui (location, code,
+						  result_type, op0, op1,
+						  TYPE_UNSIGNED (orig_type0),
+						  TYPE_UNSIGNED (orig_type1));
+    }
+
   /* Treat expressions in initializers specially as they can't trap.  */
   if (int_const_or_overflow)
     ret = (require_constant_value
@@ -11694,9 +11734,18 @@ build_binary_op (location_t location, enum tree_code code,
   protected_set_expr_location (ret, location);
 
   if (instrument_expr != NULL)
-    ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret),
-		       instrument_expr, ret);
-
+    {
+      if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+	{
+	  /* Inherit side effects.  TODO: what another attributes should be
+	     inherited?  */
+	  TREE_SIDE_EFFECTS (instrument_expr) = TREE_SIDE_EFFECTS (ret);
+	  ret = instrument_expr;
+	}
+      else
+	ret = fold_build2 (COMPOUND_EXPR, TREE_TYPE (ret),
+			   instrument_expr, ret);
+    }
   return ret;
 }
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 587969f..d09d129 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -786,6 +786,10 @@ call_expr_flags (const_tree t)
   int flags;
   tree decl = get_callee_fndecl (t);
 
+  /* We may have UBSan internal functions here.  */
+  if (!CALL_EXPR_FN (t))
+    return ECF_CONST | ECF_LEAF | ECF_NOTHROW;
+
   if (decl)
     flags = flags_from_decl_or_type (decl);
   else if (CALL_EXPR_FN (t) == NULL_TREE)
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 31fa4b0..8cc4cda 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -36,6 +36,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "gimplify.h"
 #include "intl.h"
+#include "c-family/c-ubsan.h"
 
 /* The number of nested classes being processed.  If we are not in the
    scope of any class, this is zero.  */
@@ -7329,6 +7330,8 @@ fixed_type_or_null (tree instance, int *nonnull, int *cdtorp)
 	return RECUR (TREE_OPERAND (instance, 0));
 
     case CALL_EXPR:
+      if (isan_internal_fn_p (instance))
+	return RECUR (TREE_OPERAND (instance, 0));
       /* This is a call to a constructor, hence it's never zero.  */
       if (TREE_HAS_CONSTRUCTOR (instance))
 	{
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index ba40435..d09e649 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4657,6 +4657,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 		case IFN_UBSAN_VPTR:
 		  return true;
 
+		case IFN_ISAN_CHECK_ADD:
+		case IFN_ISAN_CHECK_SUB:
+		case IFN_ISAN_CHECK_MUL:
 		case IFN_ADD_OVERFLOW:
 		case IFN_SUB_OVERFLOW:
 		case IFN_MUL_OVERFLOW:
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d1f06fd..d764550 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "decl.h"
 #include "c-family/c-objc.h"
+#include "c-family/c-ubsan.h"
 #include "plugin.h"
 #include "tree-pretty-print.h"
 #include "parser.h"
@@ -9076,6 +9077,14 @@ cp_parser_assignment_expression (cp_parser* parser, cp_id_kind * pidk,
 	      cp_expr rhs = cp_parser_initializer_clause (parser,
 							  &non_constant_p);
 
+	      if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+		   && isan_internal_fn_p (rhs))
+		{
+		  int uns_p = TYPE_UNSIGNED (TREE_TYPE (expr));
+		  tree rhs_value = rhs.get_value();
+		  isan_maybe_change_ifn_sign_arg (&rhs_value, uns_p);
+		}
+
 	      if (BRACE_ENCLOSED_INITIALIZER_P (rhs))
 		maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
 
@@ -11807,6 +11816,15 @@ cp_parser_jump_statement (cp_parser* parser)
 	  /* If the next token is a `;', then there is no
 	     expression.  */
 	  expr = NULL_TREE;
+
+	if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+	     && isan_internal_fn_p (expr))
+	  {
+	    int uns_p = TYPE_UNSIGNED (TREE_TYPE
+				      (DECL_RESULT (current_function_decl)));
+	    isan_maybe_change_ifn_sign_arg (&expr, uns_p);
+	  }
+
 	/* Build the return-statement.  */
 	statement = finish_return_stmt (expr);
 	/* Look for the final `;'.  */
@@ -18650,6 +18668,12 @@ cp_parser_init_declarator (cp_parser* parser,
 	    finish_lambda_scope ();
 	  if (initializer == error_mark_node)
 	    cp_parser_skip_to_end_of_statement (parser);
+	  else if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+		    && isan_internal_fn_p (initializer))
+	    {
+	      int uns_p = TYPE_UNSIGNED (TREE_TYPE (decl));
+	      isan_maybe_change_ifn_sign_arg (&initializer, uns_p);
+	    }
 	}
     }
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index c5f65a7..0da042d 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -22884,6 +22884,8 @@ value_dependent_expression_p (tree expression)
 
     case CALL_EXPR:
       {
+	if (!CALL_EXPR_FN (expression))
+	  return false;
 	if (value_dependent_expression_p (CALL_EXPR_FN (expression)))
 	  return true;
 	tree fn = get_callee_fndecl (expression);
@@ -23262,7 +23264,7 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees,
 
     case CALL_EXPR:
       /* Treat calls to function concepts as dependent. */
-      if (function_concept_check_p (*tp))
+      if (CALL_EXPR_FN (*tp) && function_concept_check_p (*tp))
         return *tp;
       break;
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index fb6a16e..328693b 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4085,6 +4085,9 @@ cp_build_binary_op (location_t location,
   tree result, result_ovl;
   tree orig_type = NULL;
 
+  tree orig_type0 = TREE_TYPE (orig_op0);
+  tree orig_type1 = TREE_TYPE (orig_op1);
+
   /* Nonzero if this is an operation like MIN or MAX which can
      safely be computed in short if both args are promoted shorts.
      Also implies COMMON.
@@ -4114,6 +4117,10 @@ cp_build_binary_op (location_t location,
   /* Remember whether we're doing << or >>.  */
   bool doing_shift = false;
 
+  bool doing_plus = false;
+  bool doing_minus = false;
+  bool doing_mul = false;
+
   /* Tree holding instrumentation expression.  */
   tree instrument_expr = NULL;
 
@@ -4256,6 +4263,7 @@ cp_build_binary_op (location_t location,
       else if (!(code0 == POINTER_TYPE && code1 == INTEGER_TYPE))
 	{
 	  common = 1;
+	  doing_minus = 1;
 	  break;
 	}
       /* The pointer - int case is just like pointer + int; fall
@@ -4279,10 +4287,12 @@ cp_build_binary_op (location_t location,
 				     complain);
 	}
       common = 1;
+      doing_plus = 1;
       break;
 
     case MULT_EXPR:
       common = 1;
+      doing_mul = 1;
       break;
 
     case TRUNC_DIV_EXPR:
@@ -5178,13 +5188,37 @@ cp_build_binary_op (location_t location,
 	instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
     }
 
+  if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+       && (doing_plus || doing_minus || doing_mul)
+       && !processing_template_decl)
+    {
+      op0 = cp_save_expr (op0);
+      op1 = cp_save_expr (op1);
+      op0 = fold_non_dependent_expr (op0);
+      op1 = fold_non_dependent_expr (op1);
+      instrument_expr = isan_maybe_instrument_ui (location, code,
+						  result_type, op0, op1,
+						  TYPE_UNSIGNED (orig_type0),
+						  TYPE_UNSIGNED (orig_type1));
+    }
+
   result = build2_loc (location, resultcode, build_type, op0, op1);
   if (final_type != 0)
     result = cp_convert (final_type, result, complain);
 
   if (instrument_expr != NULL)
-    result = build2 (COMPOUND_EXPR, TREE_TYPE (result),
-		     instrument_expr, result);
+    {
+      if (flag_sanitize & SANITIZE_UI_OVERFLOW)
+	{
+	  /* Inherit side effects.  TODO: what another attributes should be
+	     inherited?  */
+	  TREE_SIDE_EFFECTS (instrument_expr) = TREE_SIDE_EFFECTS (result);
+	  result = instrument_expr;
+	}
+      else
+	result = fold_build2 (COMPOUND_EXPR, TREE_TYPE (result),
+			      instrument_expr, result);
+    }
 
   if (!processing_template_decl)
     {
@@ -7682,7 +7716,15 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
 	  rhs = rvalue (rhs);
 	  rhs = stabilize_expr (rhs, &init);
 	  newrhs = cp_build_binary_op (loc, modifycode, lhs, rhs, complain);
-	  if (newrhs == error_mark_node)
+
+	   if ((flag_sanitize & SANITIZE_UI_OVERFLOW)
+	       && isan_internal_fn_p (newrhs))
+	    {
+	      int uns_p = TYPE_UNSIGNED (TREE_TYPE (lhs));
+	      isan_maybe_change_ifn_sign_arg (&newrhs, uns_p);
+	    }
+
+	   if (newrhs == error_mark_node)
 	    {
 	      if (complain & tf_error)
 		error ("  in evaluation of %<%Q(%#T, %#T)%>", modifycode,
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 2c87c53..d7a73d3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -9991,6 +9991,14 @@ signed char a = SCHAR_MAX;
 a++;
 @end smallexample
 
+@item -fsanitize=unsigned-integer-overflow
+@opindex fsanitize=unsigned-integer-overflow
+This option enables unsigned and mixed (signed/unsigned) integer overflow
+checking.  We check that the result of @code{+}, @code{*}, and binary @code{-}
+does not overflow in the unsigned or mixed arithmetics.  These checks can
+trigger false positive warnings on code that intentionally relies on overflows,
+e.g. when computing hash codes.
+
 @item -fsanitize=bounds
 @opindex fsanitize=bounds
 This option enables instrumentation of array bounds.  Various out of bounds
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index dd57e16..7757112 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -229,6 +229,7 @@ enum sanitize_code {
   SANITIZE_OBJECT_SIZE = 1UL << 20,
   SANITIZE_VPTR = 1UL << 21,
   SANITIZE_BOUNDS_STRICT = 1UL << 22,
+  SANITIZE_UI_OVERFLOW = 1 << 23,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
@@ -237,7 +238,7 @@ enum sanitize_code {
 		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
 		       | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
-			| SANITIZE_BOUNDS_STRICT
+			| SANITIZE_BOUNDS_STRICT | SANITIZE_UI_OVERFLOW
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 3b9500d..b80b0b7 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -385,7 +385,8 @@ negate_expr_p (tree t)
   switch (TREE_CODE (t))
     {
     case INTEGER_CST:
-      if (INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type))
+      if (INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)
+	  && !TYPE_OVERFLOW_SANITIZED (type))
 	return true;
 
       /* Check that -CST will not overflow type.  */
@@ -548,8 +549,8 @@ fold_negate_expr (location_t loc, tree t)
       if (TREE_OVERFLOW (tem) == TREE_OVERFLOW (t)
 	  || (ANY_INTEGRAL_TYPE_P (type)
 	      && !TYPE_OVERFLOW_TRAPS (type)
-	      && TYPE_OVERFLOW_WRAPS (type))
-	  || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
+	      && !TYPE_OVERFLOW_SANITIZED (type))
+	  || (flag_sanitize & (SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW)) == 0)
 	return tem;
       break;
 
@@ -9797,7 +9798,11 @@ fold_binary_loc (location_t loc,
 	    return tem;
 	}
 
-      goto associate;
+      /* Associate if we don't sanitize unsigned integer overflows.  */
+      if ((flag_sanitize & SANITIZE_UI_OVERFLOW) == 0)
+	goto associate;
+
+      return NULL_TREE;
 
     case MULT_EXPR:
       if (! FLOAT_TYPE_P (type))
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 47c4d25..c84acf2 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -2450,6 +2450,13 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
       enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
       auto_vec<tree> vargs (nargs);
 
+      /* Ugh, sometimes FE can optimize conditional thus it doesn't
+         depend on ISAN_*_CHECK return value.  Don't fold the check in this
+         case, but we'll probably want introduce temporary here.  */
+      if (ifn == IFN_ISAN_CHECK_ADD || ifn == IFN_ISAN_CHECK_SUB
+	  || ifn == IFN_ISAN_CHECK_MUL)
+	return GS_ALL_DONE;
+
       for (i = 0; i < nargs; i++)
 	{
 	  gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index de850fd..c60bca1 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -506,9 +506,8 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
   int prec = GET_MODE_PRECISION (mode);
   rtx sgn = immed_wide_int_const (wi::min_value (prec, SIGNED), mode);
   bool do_xor = false;
-
-  if (is_ubsan)
-    gcc_assert (!unsr_p && !uns0_p && !uns1_p);
+  tree orig_arg0 = arg0;
+  tree orig_arg1 = arg1;
 
   if (lhs)
     {
@@ -896,8 +895,8 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
     {
       /* Expand the ubsan builtin call.  */
       push_temp_slots ();
-      fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (arg0),
-					 arg0, arg1);
+      fn = ubsan_build_overflow_builtin (code, loc, TREE_TYPE (lhs),
+					 orig_arg0, orig_arg1);
       expand_normal (fn);
       pop_temp_slots ();
       do_pending_stack_adjust ();
@@ -911,7 +910,12 @@ expand_addsub_overflow (location_t loc, tree_code code, tree lhs,
   if (lhs)
     {
       if (is_ubsan)
-	expand_ubsan_result_store (target, res);
+	{
+	  if (do_xor)
+	    res = expand_binop (mode, add_optab, res, sgn, NULL_RTX, false,
+				OPTAB_LIB_WIDEN);
+	  expand_ubsan_result_store (target, res);
+	}
       else
 	{
 	  if (do_xor)
@@ -1024,6 +1028,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
   rtx target = NULL_RTX;
   signop sign;
   enum insn_code icode;
+  tree orig_arg0 = arg0;
+  tree orig_arg1 = arg1;
 
   done_label = gen_label_rtx ();
   do_error = gen_label_rtx ();
@@ -1041,9 +1047,6 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
 	write_complex_part (target, const0_rtx, true);
     }
 
-  if (is_ubsan)
-    gcc_assert (!unsr_p && !uns0_p && !uns1_p);
-
   /* We assume both operands and result have the same precision
      here (GET_MODE_BITSIZE (mode)), S stands for signed type
      with that precision, U for unsigned type with that precision,
@@ -1124,7 +1127,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
 				   NULL, do_main_label, PROB_VERY_LIKELY);
 	  do_compare_rtx_and_jump (op1, const0_rtx, EQ, true, mode, NULL_RTX,
 				   NULL, do_main_label, PROB_VERY_LIKELY);
-	  expand_arith_set_overflow (lhs, target);
+	  if (!is_ubsan)
+	    expand_arith_set_overflow (lhs, target);
 	  emit_label (do_main_label);
 	  goto do_main;
 	default:
@@ -1255,7 +1259,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
 	     is, thus we can keep do_main code oring in overflow as is.  */
 	  do_compare_rtx_and_jump (tem, const0_rtx, EQ, true, mode, NULL_RTX,
 				   NULL, do_main_label, PROB_VERY_LIKELY);
-	  expand_arith_set_overflow (lhs, target);
+	  if (!is_ubsan)
+	    expand_arith_set_overflow (lhs, target);
 	  emit_label (do_main_label);
 	  goto do_main;
 	default:
@@ -1652,8 +1657,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
     {
       /* Expand the ubsan builtin call.  */
       push_temp_slots ();
-      fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (arg0),
-					 arg0, arg1);
+      fn = ubsan_build_overflow_builtin (MULT_EXPR, loc, TREE_TYPE (lhs),
+					 orig_arg0, orig_arg1);
       expand_normal (fn);
       pop_temp_slots ();
       do_pending_stack_adjust ();
@@ -1670,7 +1675,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
       rtx_code_label *all_done_label = gen_label_rtx ();
       do_compare_rtx_and_jump (res, const0_rtx, GE, false, mode, NULL_RTX,
 			       NULL, all_done_label, PROB_VERY_LIKELY);
-      expand_arith_set_overflow (lhs, target);
+      if (!is_ubsan)
+	expand_arith_set_overflow (lhs, target);
       emit_label (all_done_label);
     }
 
@@ -1681,7 +1687,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
       rtx_code_label *set_noovf = gen_label_rtx ();
       do_compare_rtx_and_jump (op1, const0_rtx, GE, false, mode, NULL_RTX,
 			       NULL, all_done_label, PROB_VERY_LIKELY);
-      expand_arith_set_overflow (lhs, target);
+      if (!is_ubsan)
+	expand_arith_set_overflow (lhs, target);
       do_compare_rtx_and_jump (op0, const0_rtx, EQ, true, mode, NULL_RTX,
 			       NULL, set_noovf, PROB_VERY_LIKELY);
       do_compare_rtx_and_jump (op0, constm1_rtx, NE, true, mode, NULL_RTX,
@@ -1689,7 +1696,8 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
       do_compare_rtx_and_jump (op1, res, NE, true, mode, NULL_RTX, NULL,
 			       all_done_label, PROB_VERY_UNLIKELY);
       emit_label (set_noovf);
-      write_complex_part (target, const0_rtx, true);
+      if (!is_ubsan)
+	write_complex_part (target, const0_rtx, true);
       emit_label (all_done_label);
     }
 
@@ -1702,6 +1710,56 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
     }
 }
 
+/* Expand ISAN_CHECK_ADD call STMT.  */
+
+static void
+expand_ISAN_CHECK_ADD (internal_fn, gcall *stmt)
+{
+  location_t loc = gimple_location (stmt);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg0 = gimple_call_arg (stmt, 0);
+  tree arg1 = gimple_call_arg (stmt, 1);
+  int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+  int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+  int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+  expand_addsub_overflow (loc, PLUS_EXPR, lhs, arg0, arg1,
+			  unsr_p, uns0_p, uns1_p, true);
+}
+
+/* Expand ISAN_CHECK_SUB call STMT.  */
+
+static void
+expand_ISAN_CHECK_SUB (internal_fn, gcall *stmt)
+{
+  location_t loc = gimple_location (stmt);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg0 = gimple_call_arg (stmt, 0);
+  tree arg1 = gimple_call_arg (stmt, 1);
+  int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+  int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+  int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+  if (integer_zerop (arg0))
+    expand_neg_overflow (loc, lhs, arg1, true);
+  else
+    expand_addsub_overflow (loc, MINUS_EXPR, lhs, arg0, arg1,
+			    unsr_p, uns0_p, uns1_p, true);
+}
+
+/* Expand ISAN_CHECK_MUL call STMT.  */
+
+static void
+expand_ISAN_CHECK_MUL (internal_fn, gcall *stmt)
+{
+  location_t loc = gimple_location (stmt);
+  tree lhs = gimple_call_lhs (stmt);
+  tree arg0 = gimple_call_arg (stmt, 0);
+  tree arg1 = gimple_call_arg (stmt, 1);
+  int uns0_p = tree_to_shwi (gimple_call_arg (stmt, 2));
+  int uns1_p = tree_to_shwi (gimple_call_arg (stmt, 3));
+  int unsr_p = tree_to_shwi (gimple_call_arg (stmt, 4));
+  expand_mul_overflow (loc, lhs, arg0, arg1, uns0_p, uns1_p, unsr_p, true);
+}
+
 /* Expand UBSAN_CHECK_ADD call STMT.  */
 
 static void
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index e729d85..f02b12a 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -154,6 +154,9 @@ DEF_INTERNAL_FN (UBSAN_VPTR, ECF_LEAF | ECF_NOTHROW, ".RR..")
 DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (ISAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
 DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/opts.c b/gcc/opts.c
index 7406210..02f0b47 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1455,6 +1455,7 @@ const struct sanitizer_opts_s sanitizer_opts[] =
   SANITIZER_OPT (return, SANITIZE_RETURN),
   SANITIZER_OPT (null, SANITIZE_NULL),
   SANITIZER_OPT (signed-integer-overflow, SANITIZE_SI_OVERFLOW),
+  SANITIZER_OPT (unsigned-integer-overflow, SANITIZE_UI_OVERFLOW),
   SANITIZER_OPT (bool, SANITIZE_BOOL),
   SANITIZER_OPT (enum, SANITIZE_ENUM),
   SANITIZER_OPT (float-divide-by-zero, SANITIZE_FLOAT_DIVIDE),
diff --git a/gcc/testsuite/c-c++-common/isan/liveadder.c b/gcc/testsuite/c-c++-common/isan/liveadder.c
new file mode 100644
index 0000000..d76bc22
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/liveadder.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+__extension__ typedef signed long long gint64;
+__extension__ typedef unsigned long long guint64;
+typedef signed int gint32;
+typedef unsigned int guint32;
+typedef int gint;
+
+static void add_uint32 (guint32 *out, guint32 *in, gint bytes)
+{
+  gint i;
+  for (i = 0; i < bytes / sizeof (guint32); i++)
+    out[i] = ((((guint64)out[i] + (guint64)in[i]) > 1) ? 111111111 : (((guint64)out[i] + (guint64)in[i]) < 0) ? 0 : 1111111);
+}
diff --git a/gcc/testsuite/c-c++-common/isan/miniperl.c b/gcc/testsuite/c-c++-common/isan/miniperl.c
new file mode 100644
index 0000000..1887373
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/miniperl.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -fdump-tree-gimple" } */
+
+extern int *PL_tmps_stack;
+
+void foo (int items, int tmpsbase) {
+  int i = items;
+  while (i-- > 0)
+    PL_tmps_stack[--tmpsbase] |= 3;
+  return;
+}
+
+/* { dg-final { scan-tree-dump-times "ISAN_CHECK_ADD" 1 "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/isan/overflow-add.c b/gcc/testsuite/c-c++-common/isan/overflow-add.c
new file mode 100644
index 0000000..1622635
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/overflow-add.c
@@ -0,0 +1,44 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <limits.h>
+
+#define CHECK_ADD(sign1, sign2, sign3, type1, type2, type3, val1, val2)                   \
+  do {                                                                                    \
+    volatile sign1 type1 a = val1;                                                        \
+    volatile sign2 type2 b = val2;                                                        \
+    volatile sign3 type3 c = a + b;                                                       \
+    asm volatile ("" : : "r" (c) : "memory");                                             \
+  } while (0)
+
+int main () {
+
+  CHECK_ADD(unsigned, unsigned, unsigned, int, int, int, UINT_MAX, 1);
+  CHECK_ADD(unsigned, unsigned, signed, int, int, int, UINT_MAX, 1);
+  CHECK_ADD(unsigned, signed, unsigned, int, int, int, UINT_MAX, 1);
+  CHECK_ADD(unsigned, signed, signed, int, int, int, UINT_MAX, 1);
+
+  CHECK_ADD(unsigned, unsigned, unsigned, long, long, long, ULONG_MAX, 1);
+  CHECK_ADD(unsigned, unsigned, signed, long, long, long, ULONG_MAX, 1);
+  CHECK_ADD(unsigned, signed, unsigned, long, long, long, ULONG_MAX, 1);
+  CHECK_ADD(unsigned, signed, signed, long, long, long, ULONG_MAX, 1);
+
+  CHECK_ADD(, unsigned, , char *, long, char *, (char *) ULONG_MAX, 1);
+  CHECK_ADD(, unsigned, , int *, long, int *, (int *) ULONG_MAX, 1);
+  CHECK_ADD(, unsigned, , long *, long, long *, (long *) ULONG_MAX, 1);
+
+  return 0;
+}
+
+
+/* { dg-output "unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 4 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 8 cannot be represented in type 'long unsigned int'" } */
diff --git a/gcc/testsuite/c-c++-common/isan/overflow-mul.c b/gcc/testsuite/c-c++-common/isan/overflow-mul.c
new file mode 100644
index 0000000..521c596
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/overflow-mul.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <limits.h>
+
+#define CHECK_MUL(sign1, sign2, sign3, type1, type2, type3, val1, val2)                   \
+  do {                                                                                    \
+    volatile sign1 type1 a = val1;                                                        \
+    volatile sign2 type2 b = val2;                                                        \
+    volatile sign3 type3 c = a * b;                                                       \
+  } while (0)
+
+int main () {
+
+  CHECK_MUL(unsigned, unsigned, unsigned, int, int, int, 2, UINT_MAX);
+  CHECK_MUL(unsigned, unsigned, signed, int, int, int, 2, UINT_MAX);
+  CHECK_MUL(signed, unsigned, unsigned, int, int, int, 2, UINT_MAX);
+  CHECK_MUL(signed, unsigned, signed, int, int, int, 2, UINT_MAX);
+
+  CHECK_MUL(unsigned, unsigned, unsigned, long, long, long, 2, ULONG_MAX);
+  CHECK_MUL(unsigned, unsigned, signed, long, long, long, 2, ULONG_MAX);
+  CHECK_MUL(signed, unsigned, unsigned, long, long, long, 2, ULONG_MAX);
+  CHECK_MUL(signed, unsigned, signed, long, long, long, 2, ULONG_MAX);
+
+  return 0;
+}
+
+/* { dg-output "unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 2 \\* 18446744073709551615 cannot be represented in type 'long unsigned int'" } */
diff --git a/gcc/testsuite/c-c++-common/isan/overflow-sub.c b/gcc/testsuite/c-c++-common/isan/overflow-sub.c
new file mode 100644
index 0000000..957c36b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/overflow-sub.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <limits.h>
+
+#define CHECK_SUB(sign1, sign2, sign3, type1, type2, type3, val1, val2)                   \
+  do {                                                                                    \
+    volatile sign1 type1 a = val1;                                                        \
+    volatile sign2 type2 b = val2;                                                        \
+    volatile sign3 type3 c = a - b;                                                       \
+    asm volatile ("" : : "r" (c) : "memory");                                             \
+  } while (0)
+
+int main () {
+
+  CHECK_SUB(unsigned, unsigned, unsigned, int, int, int, 1, UINT_MAX);
+  CHECK_SUB(unsigned, unsigned, signed, int, int, int, 1, UINT_MAX);
+  CHECK_SUB(signed, unsigned, unsigned, int, int, int, 1, UINT_MAX);
+  CHECK_SUB(signed, unsigned, signed, int, int, int, 1, UINT_MAX);
+
+  CHECK_SUB(unsigned, unsigned, unsigned, long, long, long, 1, ULONG_MAX);
+  CHECK_SUB(unsigned, unsigned, signed, long, long, long, 1, ULONG_MAX);
+  CHECK_SUB(signed, unsigned, unsigned, long, long, long, 1, ULONG_MAX);
+  CHECK_SUB(signed, unsigned, signed, long, long, long, 1, ULONG_MAX);
+
+  CHECK_SUB(, unsigned, , char *, long, char *, (char *) 1, 2);
+  CHECK_SUB(, unsigned, , int *, long, int *, (int *) 1, 2);
+  CHECK_SUB(, unsigned, , long *, long, long *, (long *) 1, 2);
+
+  return 0;
+}
+
+/* { dg-output "unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 2 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 8 cannot be represented in type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 16 cannot be represented in type 'long unsigned int'" } */
diff --git a/gcc/testsuite/c-c++-common/isan/static-initializer.c b/gcc/testsuite/c-c++-common/isan/static-initializer.c
new file mode 100644
index 0000000..6bca344c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/isan/static-initializer.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -fdump-tree-gimple" } */
+
+char a;
+
+void foo () {
+  static char *b = &a + 14;
+  char *c = &a + 14;
+}
+
+/* { dg-final { scan-tree-dump "ISAN_CHECK_ADD" "gimple" } } */
diff --git a/gcc/testsuite/g++.dg/isan/cleanup.C b/gcc/testsuite/g++.dg/isan/cleanup.C
new file mode 100644
index 0000000..38598f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/isan/cleanup.C
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <string>
+
+extern void bar ();
+
+void foo (std::string &s) {
+  for (int i = 0; i < 20 - s.substr(1, 6).length(); ++i)
+    bar ();
+}
diff --git a/gcc/testsuite/g++.dg/isan/constexpr.C b/gcc/testsuite/g++.dg/isan/constexpr.C
new file mode 100644
index 0000000..4896396
--- /dev/null
+++ b/gcc/testsuite/g++.dg/isan/constexpr.C
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow -std=gnu++11" } */
+
+#include <algorithm>
+
+constexpr int foo (int i) {
+  return std::__lg (i);
+}
diff --git a/gcc/testsuite/g++.dg/isan/isan.exp b/gcc/testsuite/g++.dg/isan/isan.exp
new file mode 100644
index 0000000..d4af573
--- /dev/null
+++ b/gcc/testsuite/g++.dg/isan/isan.exp
@@ -0,0 +1,34 @@
+# Copyright (C) 2013-2015 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib g++-dg.exp
+load_lib ubsan-dg.exp
+
+# Initialize `dg'.
+dg-init
+ubsan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_undefined] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.C $srcdir/c-c++-common/isan/*.c]] "" ""
+}
+
+# All done.
+ubsan_finish
+dg-finish
diff --git a/gcc/testsuite/gcc.dg/isan/isan.exp b/gcc/testsuite/gcc.dg/isan/isan.exp
new file mode 100644
index 0000000..5274afc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/isan/isan.exp
@@ -0,0 +1,36 @@
+# Copyright (C) 2013-2015 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+load_lib ubsan-dg.exp
+
+# Initialize `dg'.
+dg-init
+ubsan_init
+
+# Main loop.
+if [check_effective_target_fsanitize_undefined] {
+  gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/isan/*.c]] "" ""
+}
+
+# All done.
+ubsan_finish
+dg-finish
diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c
index 9264e0b..2d524c0 100644
--- a/gcc/tree-ssa-reassoc.c
+++ b/gcc/tree-ssa-reassoc.c
@@ -2454,6 +2454,8 @@ optimize_range_tests_diff (enum tree_code opcode, tree type,
   mask = fold_build1 (BIT_NOT_EXPR, type, tem1);
   tem1 = fold_binary (MINUS_EXPR, type,
 		      fold_convert (type, rangei->exp), lowi);
+  if (tem1 == NULL_TREE)
+    return false;
   tem1 = fold_build2 (BIT_AND_EXPR, type, tem1, mask);
   lowj = build_int_cst (type, 0);
   if (update_range_test (rangei, rangej, NULL, 1, opcode, ops, tem1,
diff --git a/gcc/tree.h b/gcc/tree.h
index 012fa54..102c956 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -848,8 +848,7 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 /* True if an overflow is to be preserved for sanitization.  */
 #define TYPE_OVERFLOW_SANITIZED(TYPE)			\
   (INTEGRAL_TYPE_P (TYPE)				\
-   && !TYPE_OVERFLOW_WRAPS (TYPE)			\
-   && (flag_sanitize & SANITIZE_SI_OVERFLOW))
+   && (flag_sanitize & (SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW)))
 
 /* True if pointer types have undefined overflow.  */
 #define POINTER_TYPE_OVERFLOW_UNDEFINED (flag_strict_overflow)
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index c5543f8..5aa3390 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -1221,26 +1221,29 @@ ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
 				 ubsan_type_descriptor (lhstype), NULL_TREE,
 				 NULL_TREE);
   enum built_in_function fn_code;
+  bool uns_p = TYPE_UNSIGNED (lhstype);
+  unsigned sanitize_code = uns_p ? SANITIZE_SI_OVERFLOW | SANITIZE_UI_OVERFLOW
+				 : SANITIZE_SI_OVERFLOW;
 
   switch (code)
     {
     case PLUS_EXPR:
-      fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+      fn_code = (flag_sanitize_recover & sanitize_code)
 		? BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW
 		: BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT;
       break;
     case MINUS_EXPR:
-      fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+      fn_code = (flag_sanitize_recover & sanitize_code)
 		? BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW
 		: BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT;
       break;
     case MULT_EXPR:
-      fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+      fn_code = (flag_sanitize_recover & sanitize_code)
 		? BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW
 		: BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT;
       break;
     case NEGATE_EXPR:
-      fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+      fn_code = (flag_sanitize_recover & sanitize_code)
 		? BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW
 		: BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT;
       break;
@@ -1938,6 +1941,7 @@ public:
   virtual bool gate (function *)
     {
       return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+			      | SANITIZE_UI_OVERFLOW
 			      | SANITIZE_BOOL | SANITIZE_ENUM
 			      | SANITIZE_ALIGNMENT
 			      | SANITIZE_NONNULL_ATTRIBUTE

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]