PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken branch
authorMartin Sebor <msebor@redhat.com>
Thu, 17 Jan 2019 16:33:55 +0000 (16:33 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 17 Jan 2019 16:33:55 +0000 (09:33 -0700)
gcc/ChangeLog:

PR tree-optimization/88800
* gimple-fold.c (gimple_fold_builtin_memory_op): Avoid checking
NO_WARNING bit here.  Avoid folding out-of-bounds calls.
* gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Remove
redundant argument.  Add new argument and issue diagnostics under
its control.  Detect out-of-bounds access even with warnings
disabled.
(check_bounds_or_overlap): Change return type.  Add argument.
(wrestrict_dom_walker::check_call): Adjust.
* gimple-ssa-warn-restrict.h (check_bounds_or_overlap): Add argument.
* tree-ssa-strlen.c (handle_builtin_strcpy): Adjust to change in
check_bounds_or_overlap's return value.
(handle_builtin_stxncpy): Same.
(handle_builtin_strcat): Same.

gcc/testsuite/ChangeLog:

PR tree-optimization/88800
* c-c++-common/Wrestrict.c: Adjust.
* gcc.dg/Warray-bounds-37.c: New test.
* gcc.dg/builtin-memcpy-2.c: New test.
* gcc.dg/builtin-memcpy.c: New test.

From-SVN: r268037

gcc/ChangeLog
gcc/gimple-fold.c
gcc/gimple-ssa-warn-restrict.c
gcc/gimple-ssa-warn-restrict.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wrestrict.c
gcc/testsuite/gcc.dg/Warray-bounds-37.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-memcpy-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-memcpy.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c

index 1ca3a271a9a0cae0d4b0e67238daec530af38dd5..9089aa1922b54078d7da9c0586d95a34b926b109 100644 (file)
@@ -1,3 +1,20 @@
+2019-01-17  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/88800
+       * gimple-fold.c (gimple_fold_builtin_memory_op): Avoid checking
+       NO_WARNING bit here.  Avoid folding out-of-bounds calls.
+       * gimple-ssa-warn-restrict.c (maybe_diag_offset_bounds): Remove
+       redundant argument.  Add new argument and issue diagnostics under
+       its control.  Detect out-of-bounds access even with warnings
+       disabled.
+       (check_bounds_or_overlap): Change return type.  Add argument.
+       (wrestrict_dom_walker::check_call): Adjust.
+       * gimple-ssa-warn-restrict.h (check_bounds_or_overlap): Add argument.
+       * tree-ssa-strlen.c (handle_builtin_strcpy): Adjust to change in
+       check_bounds_or_overlap's return value.
+       (handle_builtin_stxncpy): Same.
+       (handle_builtin_strcat): Same.
+
 2019-01-17  Andrew Stubbs  <ams@codesourcery.com>
            Kwok Cheung Yeung  <kcy@codesourcery.com>
            Julian Brown  <julian@codesourcery.com>
index 2d25f9eec5f45d2230c1c048e358abecfcaaaba4..500e551996a7679708d714d27082530eb5002d15 100644 (file)
@@ -697,8 +697,6 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  bool nowarn = gimple_no_warning_p (stmt);
-
   /* If the LEN parameter is a constant zero or in range where
      the only valid value is zero, return DEST.  */
   if (size_must_be_zero_p (len))
@@ -766,12 +764,16 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          unsigned ilen = tree_to_uhwi (len);
          if (pow2p_hwi (ilen))
            {
-             /* Detect invalid bounds and overlapping copies and issue
-                either -Warray-bounds or -Wrestrict.  */
-             if (!nowarn
-                 && check_bounds_or_overlap (as_a <gcall *>(stmt),
-                                             dest, src, len, len))
-               gimple_set_no_warning (stmt, true);
+             /* Detect out-of-bounds accesses without issuing warnings.
+                Avoid folding out-of-bounds copies but to avoid false
+                positives for unreachable code defer warning until after
+                DCE has worked its magic.
+                -Wrestrict is still diagnosed.  */
+             if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+                                                        dest, src, len, len,
+                                                        false, false))
+               if (warning != OPT_Wrestrict)
+                 return false;
 
              scalar_int_mode mode;
              tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
@@ -1038,10 +1040,16 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
            }
        }
 
-      /* Detect invalid bounds and overlapping copies and issue either
-        -Warray-bounds or -Wrestrict.  */
-      if (!nowarn)
-       check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+      /* Same as above, detect out-of-bounds accesses without issuing
+        warnings.  Avoid folding out-of-bounds copies but to avoid
+        false positives for unreachable code defer warning until
+        after DCE has worked its magic.
+        -Wrestrict is still diagnosed.  */
+      if (int warning = check_bounds_or_overlap (as_a <gcall *>(stmt),
+                                                dest, src, len, len,
+                                                false, false))
+       if (warning != OPT_Wrestrict)
+         return false;
 
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
index 6eb393dc8e109fbd76047886dfecd7e741b3a803..42c87190dd8cf21a7be52b9fe92c73280a4d0f80 100644 (file)
@@ -1329,6 +1329,9 @@ maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
   if (!acs.overlap ())
     return false;
 
+  if (gimple_no_warning_p (call))
+    return true;
+
   /* For convenience.  */
   const builtin_memref &dstref = *acs.dstref;
   const builtin_memref &srcref = *acs.srcref;
@@ -1568,7 +1571,7 @@ maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
   return true;
 }
 
-/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+/* Validate REF offsets in an expression passed as an argument to a CALL
    to a built-in function FUNC to make sure they are within the bounds
    of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
    Both initial values of the offsets and their final value computed by
@@ -1578,21 +1581,27 @@ maybe_diag_overlap (location_t loc, gimple *call, builtin_access &acs)
 
 static bool
 maybe_diag_offset_bounds (location_t loc, gimple *call, tree func, int strict,
-                         tree expr, const builtin_memref &ref)
+                         const builtin_memref &ref, bool do_warn)
 {
-  if (!warn_array_bounds)
+  /* Check for out-bounds pointers regardless of warning options since
+     the result is used to make codegen decisions.  */
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+  tree oobref = ref.offset_out_of_bounds (strict, ooboff);
+  if (!oobref)
     return false;
 
-  if (ref.ref && TREE_NO_WARNING (ref.ref))
+  /* Return true without issuing a warning.  */
+  if (!do_warn)
+    return true;
+
+  if (!warn_array_bounds)
     return false;
 
-  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
-  tree oobref = ref.offset_out_of_bounds (strict, ooboff);
-  if (!oobref)
+  if (ref.ref && TREE_NO_WARNING (ref.ref))
     return false;
 
-  if (EXPR_HAS_LOCATION (expr))
-    loc = EXPR_LOCATION (expr);
+  if (EXPR_HAS_LOCATION (ref.ptr))
+    loc = EXPR_LOCATION (ref.ptr);
 
   loc = expansion_point_location_if_in_system_header (loc);
 
@@ -1811,7 +1820,7 @@ wrestrict_dom_walker::check_call (gimple *call)
       || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
-  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+  if (!check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
     return;
 
   /* Avoid diagnosing the call again.  */
@@ -1823,12 +1832,14 @@ wrestrict_dom_walker::check_call (gimple *call)
 /* Attempt to detect and diagnose invalid offset bounds and (except for
    memmove) overlapping copy in a call expression EXPR from SRC to DST
    and DSTSIZE and SRCSIZE bytes, respectively.  Both DSTSIZE and
-   SRCSIZE may be NULL.  Return false when one or the other has been
-   detected and diagnosed, true otherwise.  */
+   SRCSIZE may be NULL.  DO_WARN is false to detect either problem
+   without issue a warning.  Return the OPT_Wxxx constant corresponding
+   to the warning if one has been detected and zero otherwise.  */
 
-bool
+int
 check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
-                        tree srcsize, bool bounds_only /* = false */)
+                        tree srcsize, bool bounds_only /* = false */,
+                        bool do_warn /* = true */)
 {
   location_t loc = gimple_nonartificial_location (call);
   loc = expansion_point_location_if_in_system_header (loc);
@@ -1847,11 +1858,12 @@ check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
   /* Validate offsets first to make sure they are within the bounds
      of the destination object if its size is known, or PTRDIFF_MAX
      otherwise.  */
-  if (maybe_diag_offset_bounds (loc, call, func, strict, dst, dstref)
-      || maybe_diag_offset_bounds (loc, call, func, strict, src, srcref))
+  if (maybe_diag_offset_bounds (loc, call, func, strict, dstref, do_warn)
+      || maybe_diag_offset_bounds (loc, call, func, strict, srcref, do_warn))
     {
-      gimple_set_no_warning (call, true);
-      return false;
+      if (do_warn)
+       gimple_set_no_warning (call, true);
+      return OPT_Warray_bounds;
     }
 
   bool check_overlap
@@ -1861,7 +1873,7 @@ check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
               && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
 
   if (!check_overlap)
-    return true;
+    return 0;
 
   if (operand_equal_p (dst, src, 0))
     {
@@ -1875,20 +1887,20 @@ check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
                      "%G%qD source argument is the same as destination",
                      call, func);
          gimple_set_no_warning (call, true);
-         return false;
+         return OPT_Wrestrict;
        }
 
-      return true;
+      return 0;
     }
 
   /* Return false when overlap has been detected.  */
   if (maybe_diag_overlap (loc, call, acs))
     {
       gimple_set_no_warning (call, true);
-      return false;
+      return OPT_Wrestrict;
     }
 
-  return true;
+  return 0;
 }
 
 gimple_opt_pass *
index 167d5843039acdc118d47566bce4b22a07f5b76e..47835de84c6a3b1fadfaa42f7cc5b0c7ddf8eb7e 100644 (file)
@@ -20,7 +20,7 @@
 
 #ifndef GIMPLE_SSA_WARN_RESTRICT_H
 
-extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
-                                    bool = false);
+extern int check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+                                   bool = false, bool = true);
 
 #endif /* GIMPLE_SSA_WARN_RESTRICT_H */
index 103a0f73bc73fa96b0a0ddeb2c051c693d88b6ef..31b171b628bce20dc36f11344f970b27e83f7407 100644 (file)
@@ -1,3 +1,11 @@
+2019-01-17  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/88800
+       * c-c++-common/Wrestrict.c: Adjust.
+       * gcc.dg/Warray-bounds-37.c: New test.
+       * gcc.dg/builtin-memcpy-2.c: New test.
+       * gcc.dg/builtin-memcpy.c: New test.
+
 2019-01-17  Tamar Christina  <tamar.christina@arm.com>
 
        PR target/88850
index efd72efd320abf5c1c96c463ef3461308b7c870a..d0e3c3411fea593613e45e3db3e81e0467864273 100644 (file)
@@ -636,7 +636,7 @@ void test_strcpy_cst (ptrdiff_t i)
   T ("012", a, a + 3);
   /* The following doesn't overlap but it should trigger -Wstringop-overflow
      for reading past the end.  */
-  T ("012", a, a + sizeof a);
+  T ("012", a, a + sizeof a);     /* { dg-warning "\\\[-Wstringop-overflow" "pr81437" { xfail *-*-* } } */
 
   /* The terminating nul written to d[2] overwrites s[0].  */
   T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
@@ -651,9 +651,9 @@ void test_strcpy_cst (ptrdiff_t i)
   T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
   T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
   T ("012", a + 4, a);
-  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+  /* The following doesn't overlap but it triggers -Wstringop-overflow
      for writing past the end.  */
-  T ("012", a + sizeof a, a);
+  T ("012", a + sizeof a, a);     /* { dg-warning "\\\[-Wstringop-overflow" } */
 }
 
 /* Exercise strcpy with constant or known arguments offset by a range.
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-37.c b/gcc/testsuite/gcc.dg/Warray-bounds-37.c
new file mode 100644 (file)
index 0000000..57d218a
--- /dev/null
@@ -0,0 +1,47 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+extern void* memmove (void*, const void*, __SIZE_TYPE__);
+
+struct A
+{
+  const char *s;
+  int n;
+};
+
+void f (void*);
+
+struct B
+{
+  char d[5];
+  int n;
+};
+
+__attribute__ ((always_inline)) inline void
+g (struct B *p, struct A a)
+{
+  int i = a.n;
+  if (i <= 5)
+    p->n = i;
+  else {
+    p->n = -1;
+    f (p);
+  }
+
+  if (p->n >= 0)
+    memmove (p->d, a.s, a.n);   /* { dg-bogus "\\\[-Warray-bounds" } */
+}
+
+void h (void)
+{
+  char c[8] = "";
+
+  struct A a;
+  a.s = c;
+  a.n = 8;
+
+  struct B b;
+  g (&b, a);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-memcpy-2.c b/gcc/testsuite/gcc.dg/builtin-memcpy-2.c
new file mode 100644 (file)
index 0000000..bddff93
--- /dev/null
@@ -0,0 +1,42 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   Verify that out-of-bounds memcpy calls are not folded even when
+   warnings are disabled.
+   { dg-do compile }
+   { dg-options "-O2 -w -fdump-tree-optimized" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+  memcpy (a1, p, sizeof a1 * 2);
+}
+
+void f2 (const void *p)
+{
+  memcpy (a2, p, sizeof a2 * 2);
+}
+
+void f4 (const void *p)
+{
+  memcpy (a4, p, sizeof a4 * 2);
+}
+
+void f8 (const void *p)
+{
+  memcpy (a8, p, sizeof a8 * 2);
+}
+
+void f16 (const void *p)
+{
+  memcpy (a16, p, sizeof a16 * 2);
+}
+
+void f32 (const void *p)
+{
+  memcpy (a32, p, sizeof a32 * 2);
+}
+
+/* { dg-final { scan-tree-dump-times "memcpy" 6 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-memcpy.c b/gcc/testsuite/gcc.dg/builtin-memcpy.c
new file mode 100644 (file)
index 0000000..85e2539
--- /dev/null
@@ -0,0 +1,43 @@
+/* PR tree-optimization/88800 - Spurious -Werror=array-bounds for non-taken
+   branch
+   Verify that out-of-bounds memcpy calls are not folded when warnings are
+   enabled (builtin-memcpy-2.c verifies they're not folded with warnings
+   disabled).
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+extern void* memcpy (void*, const void*, __SIZE_TYPE__);
+
+char a1[1], a2[2], a4[4], a8[8], a16[16], a32[32];
+
+void f1 (const void *p)
+{
+  memcpy (a1, p, sizeof a1 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f2 (const void *p)
+{
+  memcpy (a2, p, sizeof a2 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f4 (const void *p)
+{
+  memcpy (a4, p, sizeof a4 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f8 (const void *p)
+{
+  memcpy (a8, p, sizeof a8 * 2);    /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f16 (const void *p)
+{
+  memcpy (a16, p, sizeof a16 * 2);  /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+void f32 (const void *p)
+{
+  memcpy (a32, p, sizeof a32 * 2);  /* { dg-warning "\\\[-Warray-bounds" } */
+}
+
+/* { dg-final { scan-tree-dump-times "memcpy" 6 "optimized" } } */
index 02ed1b4c77783ed969a9a0e9eabc88e659b356f6..df569708309140915ab1f0eadecfabaacf6dbaf9 100644 (file)
@@ -1742,7 +1742,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
   if (const strinfo *chksi = olddsi ? olddsi : dsi)
     if (si
-       && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+       && check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
       {
        gimple_set_no_warning (stmt, true);
        set_no_warning = true;
@@ -2214,7 +2214,7 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
   else
     srcsize = NULL_TREE;
 
-  if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
+  if (check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
     {
       gimple_set_no_warning (stmt, true);
       return;
@@ -2512,7 +2512,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
        tree sptr = si && si->ptr ? si->ptr : src;
 
-       if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+       if (check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
          {
            gimple_set_no_warning (stmt, true);
            set_no_warning = true;
@@ -2622,7 +2622,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
       tree sptr = si && si->ptr ? si->ptr : src;
 
-      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+      if (check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
        {
          gimple_set_no_warning (stmt, true);
          set_no_warning = true;