The problem here is that negate_expr_p returns true (as it should)
for UINT_MAX(OVF), but fold_negate_expr didn't actually fold it,
and that is a no-no; if negate_expr_p is true, fold_negate_expr must
not return NULL_TREE.  I added the following hunk for bootstrap and
regtest for better coverage:

@@ -752,6 +755,12 @@ fold_negate_expr (location_t loc, tree t)
       break;
     }
 
+#ifdef ENABLE_CHECKING
+  if (negate_expr_p (t))
+    internal_error ("fold_negate_expr should never return NULL_TREE "
+                   "if negate_expr_p is true");
+#endif
+
   return NULL_TREE;
 }

Fixed by adjusting fold_negate_expr so that it folds non-trapping
wrapping constants.  The SANITIZE_SI_OVERFLOW check is important
to not regress -Woverflow warnings (ug).

negate_expr_p hunk is to match the NEGATE_EXPR case in fold_negate_expr.
 
Bootstrapped/regtested on power8-linux, ok for trunk?

2014-11-19  Marek Polacek  <pola...@redhat.com>

        PR sanitizer/63879
        * fold-const.c (negate_expr_p) <case NEGATE_EXPR>: Return
        !TYPE_OVERFLOW_SANITIZED.
        (fold_negate_expr) <case INTEGER_CST>: Fold when overflow
        does not trap and when overflow wraps, or when SANITIZE_SI_OVERFLOW
        is 0.

        * c-c++-common/ubsan/pr63879-1.c: New test.
        * c-c++-common/ubsan/pr63879-2.c: New test.

diff --git gcc/fold-const.c gcc/fold-const.c
index f6fb8af..719e06e 100644
--- gcc/fold-const.c
+++ gcc/fold-const.c
@@ -408,9 +408,11 @@ negate_expr_p (tree t)
              && TYPE_OVERFLOW_WRAPS (type));
 
     case FIXED_CST:
-    case NEGATE_EXPR:
       return true;
 
+    case NEGATE_EXPR:
+      return !TYPE_OVERFLOW_SANITIZED (type);
+
     case REAL_CST:
       /* We want to canonicalize to positive real constants.  Pretend
          that only negative ones can be easily negated.  */
@@ -555,7 +557,8 @@ fold_negate_expr (location_t loc, tree t)
       tem = fold_negate_const (t, type);
       if (TREE_OVERFLOW (tem) == TREE_OVERFLOW (t)
          || (!TYPE_OVERFLOW_TRAPS (type)
-             && (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0))
+             && TYPE_OVERFLOW_WRAPS (type))
+         || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
        return tem;
       break;
 
diff --git gcc/testsuite/c-c++-common/ubsan/pr63879-1.c 
gcc/testsuite/c-c++-common/ubsan/pr63879-1.c
index e69de29..2035849 100644
--- gcc/testsuite/c-c++-common/ubsan/pr63879-1.c
+++ gcc/testsuite/c-c++-common/ubsan/pr63879-1.c
@@ -0,0 +1,23 @@
+/* PR sanitizer/63879 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct A
+{
+  int inode;
+} * a;
+int b, c;
+void
+fn1 ()
+{
+  int d = 0;
+  while (b)
+    {
+      if (a->inode)
+        d++;
+      a = 0;
+    }
+  c = d - 1;
+  for (; c >= 0; c--)
+    ;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/pr63879-2.c 
gcc/testsuite/c-c++-common/ubsan/pr63879-2.c
index e69de29..34eb8e7 100644
--- gcc/testsuite/c-c++-common/ubsan/pr63879-2.c
+++ gcc/testsuite/c-c++-common/ubsan/pr63879-2.c
@@ -0,0 +1,13 @@
+/* PR sanitizer/63879 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+int a;
+void
+fn1 ()
+{
+  int b = 2;
+  for (; a;)
+    while (b >= 0)
+      b--;
+}

        Marek

Reply via email to