Implement ISan: unsigned integer overflow checker. 17/158717/1
authorMaxim Ostapenko <m.ostapenko@samsung.com>
Thu, 1 Jun 2017 13:21:11 +0000 (16:21 +0300)
committerIvan Baravy <i.baravy@samsung.com>
Thu, 2 Nov 2017 13:55:25 +0000 (16:55 +0300)
ISan in FE.
Handle all s and u cases.
Add unit and regression tests.

Change-Id: I3f60f751598d904683264372541c2d3be0076768

34 files changed:
gcc/builtins.c
gcc/c-family/c-common.c
gcc/c-family/c-ubsan.c
gcc/c-family/c-ubsan.h
gcc/c/c-parser.c
gcc/c/c-typeck.c
gcc/calls.c
gcc/cp/call.c
gcc/cp/class.c
gcc/cp/constexpr.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/typeck.c
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/fold-const.c
gcc/gimplify.c
gcc/internal-fn.c
gcc/internal-fn.def
gcc/opts.c
gcc/testsuite/c-c++-common/isan/liveadder.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/isan/miniperl.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/isan/overflow-add.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/isan/overflow-mul.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/isan/overflow-sub.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/isan/static-initializer.c [new file with mode: 0644]
gcc/testsuite/g++.dg/isan/cleanup.C [new file with mode: 0644]
gcc/testsuite/g++.dg/isan/constexpr.C [new file with mode: 0644]
gcc/testsuite/g++.dg/isan/isan.exp [new file with mode: 0644]
gcc/testsuite/gcc.dg/isan/false_positive.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/isan/isan.exp [new file with mode: 0644]
gcc/tree-ssa-reassoc.c
gcc/tree.h
gcc/ubsan.c

index bf66327..4ccd052 100644 (file)
@@ -6771,6 +6771,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;
 
index c6eecaf..22420bb 100644 (file)
@@ -4249,10 +4249,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)
@@ -4330,11 +4338,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 ();
 
@@ -9732,7 +9763,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;
 
index 4022bdf..82ecc5b 100644 (file)
@@ -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;
+}
index 30d4f97..a8202c0 100644 (file)
@@ -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  */
index c9eb8dd..35f383e 100644 (file)
@@ -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"
@@ -262,6 +263,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
@@ -1927,6 +1930,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 ()
@@ -1936,7 +1943,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,
@@ -5212,6 +5224,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;
index 2bea816..5793a4a 100644 (file)
@@ -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;
@@ -3020,6 +3022,21 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   if (nargs < 0)
     return error_mark_node;
 
+  for (int i = 0; i < nargs; ++i)
+    {
+      tree tmp = (*params)[i];
+      if (TREE_CODE (tmp) == NOP_EXPR)
+       {
+         int real_type_unsigned_p = TYPE_UNSIGNED (TREE_TYPE (tmp));
+         tree arg = TREE_OPERAND (tmp, 0);
+         if (isan_internal_fn_p (arg))
+           {
+             tree *arg_p = &TREE_OPERAND ((*params)[i], 0);
+             isan_maybe_change_ifn_sign_arg (arg_p, real_type_unsigned_p);
+           }
+       }
+    }
+
   /* Check that the function is called through a compatible prototype.
      If it is not, warn.  */
   if (CONVERT_EXPR_P (function)
@@ -5570,6 +5587,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),
@@ -5607,6 +5631,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.  */
@@ -10577,6 +10607,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;
 
@@ -10757,7 +10791,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:
@@ -10776,10 +10813,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;
 
@@ -11607,6 +11648,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
@@ -11632,9 +11687,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;
 }
 
index 8de22f3..a39fc86 100644 (file)
@@ -795,6 +795,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)
index a7d35b9..cbc3ec6 100644 (file)
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "langhooks.h"
 #include "c-family/c-objc.h"
+#include "c-family/c-ubsan.h"
 #include "internal-fn.h"
 
 /* The various kinds of conversion.  */
index 35deb1e..ceff3ef 100644 (file)
@@ -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.  */
@@ -7497,6 +7498,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))
        {
index f9c4ead..cff531e 100644 (file)
@@ -4730,6 +4730,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
                case IFN_UBSAN_BOUNDS:
                case IFN_UBSAN_VPTR:
                  return true;
+               case IFN_ISAN_CHECK_ADD:
+               case IFN_ISAN_CHECK_SUB:
+               case IFN_ISAN_CHECK_MUL:
+                 return false;
                default:
                  break;
                }
index 187fe0e..bb2d371 100644 (file)
@@ -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"
@@ -9083,6 +9084,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);
 
@@ -11834,6 +11843,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 `;'.  */
@@ -18677,6 +18695,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);
+           }
        }
     }
 
index db403c8..a98a5dc 100644 (file)
@@ -22860,6 +22860,8 @@ value_dependent_expression_p (tree expression)
 
     case CALL_EXPR:
       {
+       if (!CALL_EXPR_FN (expression))
+         return false;
        tree fn = get_callee_fndecl (expression);
        int i, nargs;
        if (!fn && value_dependent_expression_p (CALL_EXPR_FN (expression)))
@@ -23224,7 +23226,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;
 
index 029d0ab..b5a9dd0 100644 (file)
@@ -4060,6 +4060,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.
@@ -4089,6 +4092,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;
 
@@ -4231,6 +4238,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
@@ -4254,10 +4262,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:
@@ -5153,13 +5163,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)
     {
@@ -7672,6 +7706,14 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
          newrhs = cp_build_binary_op (input_location,
                                       modifycode, lhs, rhs,
                                       complain);
+
+         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)
index f2f34e8..338b326 100644 (file)
@@ -9856,6 +9856,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
index 8201676..18cf0a3 100644 (file)
@@ -253,6 +253,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
@@ -261,7 +262,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. */
index 46f1ac1..17ff640 100644 (file)
@@ -386,7 +386,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.  */
@@ -549,8 +550,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;
 
@@ -9966,7 +9967,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))
index 22e053a..ee6d2b5 100644 (file)
@@ -2371,6 +2371,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,
index f6805d0..1523a91 100644 (file)
@@ -463,9 +463,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)
     {
@@ -853,8 +852,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 ();
@@ -868,7 +867,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)
@@ -981,6 +985,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 ();
@@ -998,9 +1004,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,
@@ -1081,7 +1084,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);
-         write_complex_part (target, const1_rtx, true);
+         if (!is_ubsan)
+           write_complex_part (target, const1_rtx, true);
          emit_label (do_main_label);
          goto do_main;
        default:
@@ -1212,7 +1216,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);
-         write_complex_part (target, const1_rtx, true);
+         if (!is_ubsan)
+           write_complex_part (target, const1_rtx, true);
          emit_label (do_main_label);
          goto do_main;
        default:
@@ -1609,8 +1614,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 ();
@@ -1627,7 +1632,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);
-      write_complex_part (target, const1_rtx, true);
+      if (!is_ubsan)
+       write_complex_part (target, const1_rtx, true);
       emit_label (all_done_label);
     }
 
@@ -1638,7 +1644,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);
-      write_complex_part (target, const1_rtx, true);
+      if (!is_ubsan)
+       write_complex_part (target, const1_rtx, true);
       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,
@@ -1646,7 +1653,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);
     }
 
@@ -1659,6 +1667,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
index a62f3e8..761576d 100644 (file)
@@ -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)
index 0f9431a..99542cb 100644 (file)
@@ -1454,6 +1454,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 (file)
index 0000000..d76bc22
--- /dev/null
@@ -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 (file)
index 0000000..1887373
--- /dev/null
@@ -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 (file)
index 0000000..0003842
--- /dev/null
@@ -0,0 +1,51 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+#define CHECK_ADD(type1, type2, type3, val1, val2)      \
+  do {                                                  \
+    volatile type1 a = val1;                            \
+    volatile type2 b = val2;                            \
+    volatile type3 c = a + b;                           \
+    asm volatile ("" : : "r" (c) : "memory");           \
+  } while (0)
+
+int main () {
+
+  CHECK_ADD(uint32_t, uint32_t, uint32_t, UINT32_MAX, 1);
+  CHECK_ADD(uint32_t, uint32_t, int32_t, UINT32_MAX, 1);
+  CHECK_ADD(uint32_t, int32_t, uint32_t, UINT32_MAX, 1);
+  CHECK_ADD(uint32_t, int32_t, int32_t, UINT32_MAX, 1);
+  CHECK_ADD(uint32_t, uint32_t, uint32_t, INT32_MAX, 1);
+  CHECK_ADD(uint32_t, int32_t, int32_t, INT32_MAX, -1);
+
+  CHECK_ADD(uint64_t, uint64_t, uint64_t, UINT64_MAX, 1);
+  CHECK_ADD(uint64_t, uint64_t, int64_t, UINT64_MAX, 1);
+  CHECK_ADD(uint64_t, int64_t, uint64_t, UINT64_MAX, 1);
+  CHECK_ADD(uint64_t, int64_t, int64_t, UINT64_MAX, 1);
+  CHECK_ADD(uint64_t, uint64_t, uint64_t, INT64_MAX, 1);
+  CHECK_ADD(uint64_t, int64_t, uint64_t, INT64_MAX, -1);
+
+  CHECK_ADD(int8_t *, unsigned, int8_t *, (int8_t *) UINT64_MAX, 1);
+  CHECK_ADD(int32_t *, unsigned, int32_t *, (int32_t *) UINT64_MAX, 1);
+  CHECK_ADD(int64_t *, unsigned, int64_t *, (int64_t *) UINT64_MAX, 1);
+  CHECK_ADD(int8_t *, unsigned, int8_t *, (int8_t *) SIZE_MAX, 1);
+  CHECK_ADD(int8_t *, signed, int8_t *, (int8_t *) SIZE_MAX, -1);
+
+  return 0;
+}
+
+
+/* { dg-output "unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 4294967295 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 18446744073709551615 \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 1 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 4 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: \[0-9\]+ \\+ 8 cannot be represented in type '.*'" } */
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 (file)
index 0000000..7e29eb1
--- /dev/null
@@ -0,0 +1,37 @@
+/* { 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, int, int, int, 2, INT_MAX);
+  CHECK_MUL(signed, signed, signed, int, int, int, -2, INT_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);
+  CHECK_MUL(unsigned, unsigned, unsigned, long, long, long, 2, LONG_MAX);
+  CHECK_MUL(signed, signed, signed, long, long, long, -2, LONG_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 (file)
index 0000000..fe03df2
--- /dev/null
@@ -0,0 +1,50 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+
+#include <stdint.h>
+#include <limits.h>
+
+#define CHECK_SUB(type1, type2, type3, val1, val2)      \
+  do {                                                  \
+    volatile type1 a = val1;                            \
+    volatile type2 b = val2;                            \
+    volatile type3 c = a - b;                           \
+    asm volatile ("" : : "r" (c) : "memory");           \
+  } while (0)
+
+int main () {
+
+  CHECK_SUB(uint32_t, uint32_t, uint32_t, 1, UINT32_MAX);
+  CHECK_SUB(uint32_t, uint32_t, int32_t, 1, UINT32_MAX);
+  CHECK_SUB(int32_t, uint32_t, uint32_t, 1, UINT32_MAX);
+  CHECK_SUB(int32_t, uint32_t, int32_t, 1, UINT32_MAX);
+  CHECK_SUB(uint32_t, uint32_t, uint32_t, 1, 1);
+  CHECK_SUB(uint32_t, int32_t, uint32_t, 1, -1);
+
+  CHECK_SUB(uint64_t, uint64_t, uint64_t, 1, UINT64_MAX);
+  CHECK_SUB(uint64_t, uint64_t, int64_t, 1, UINT64_MAX);
+  CHECK_SUB(int64_t, uint64_t, uint64_t, 1, UINT64_MAX);
+  CHECK_SUB(int64_t, uint64_t, int64_t, 1, UINT64_MAX);
+  CHECK_SUB(uint64_t, uint64_t, uint64_t, 1, 1);
+  CHECK_SUB(uint64_t, int64_t, uint64_t, 1, -1);
+
+  CHECK_SUB(int8_t *, unsigned, int8_t *, (int8_t *) 1, 2);
+  CHECK_SUB(int32_t *, unsigned, int32_t *, (int32_t *) 1, 2);
+  CHECK_SUB(int64_t *, unsigned, int64_t *, (int64_t *) 1, 2);
+  CHECK_SUB(int8_t *, unsigned, int8_t *, (int8_t *) 1, 1);
+  CHECK_SUB(int8_t *, signed, int8_t *, (int8_t *) 1, -1);
+
+  return 0;
+}
+
+/* { dg-output "unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 4294967295 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 18446744073709551615 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 2 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 8 cannot be represented in type '.*'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*unsigned integer overflow: 1 \\- 16 cannot be represented in type '.*'" } */
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 (file)
index 0000000..6bca344
--- /dev/null
@@ -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 (file)
index 0000000..38598f8
--- /dev/null
@@ -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 (file)
index 0000000..4896396
--- /dev/null
@@ -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 (file)
index 0000000..d4af573
--- /dev/null
@@ -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/false_positive.c b/gcc/testsuite/gcc.dg/isan/false_positive.c
new file mode 100644 (file)
index 0000000..a2b245e
--- /dev/null
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unsigned-integer-overflow" } */
+/* { dg-set-target-env-var UBSAN_OPTIONS "halt_on_error=1" } */
+
+int foo (int c) {
+};
+
+int bar (unsigned a, unsigned b) {
+  return a - b;
+}
+
+int main() {
+  unsigned a = 1;
+  unsigned b = 2;
+
+  int c = a - b;
+  foo (a - b);
+  c = bar (a, b);
+}
diff --git a/gcc/testsuite/gcc.dg/isan/isan.exp b/gcc/testsuite/gcc.dg/isan/isan.exp
new file mode 100644 (file)
index 0000000..5274afc
--- /dev/null
@@ -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
index d23dabd..8d18cf1 100644 (file)
@@ -2345,6 +2345,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,
index cb687d2..1967097 100644 (file)
@@ -843,8 +843,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)
index 7b0a3f1..144686e 100644 (file)
@@ -1242,26 +1242,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;
@@ -1961,6 +1964,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