PR middle-end/91977 - missing -Wstringop-overflow on memcpy into a pointer plus offset
authorMartin Sebor <msebor@redhat.com>
Fri, 4 Oct 2019 21:29:41 +0000 (21:29 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Fri, 4 Oct 2019 21:29:41 +0000 (15:29 -0600)
gcc/ChangeLog:

PR middle-end/91977
* tree-ssa-strlen.c (count_nonzero_bytes): Handle assignments with
MEM_REF right operand.  Avoid failing for MEM_REF assignments from
uninitialized objects.

gcc/testsuite/ChangeLog:

PR middle-end/91977
* gcc.dg/Wstringop-overflow-18.c: New test.

From-SVN: r276603

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/Warray-bounds-18.c
gcc/testsuite/gcc.dg/Wstringop-overflow-18.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c

index 6a1c17c0465a06859af415187b84605bb160c73c..18b4f89bb2816ec0a9626bbe540f6038301bac1a 100644 (file)
@@ -1,3 +1,10 @@
+2019-10-04  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91977
+       * tree-ssa-strlen.c (count_nonzero_bytes): Handle assignments with
+       MEM_REF right operand.  Avoid failing for MEM_REF assignments from
+       uninitialized objects.
+
 2019-10-04  Martin Sebor  <msebor@redhat.com>
 
        * builtins.c (compute_objsize): Add an argument.
index 9a5adf787dd4ebf9014fff2c6251f9121f376832..d8987cabc3dc6f0379f7e57d17f5f059383eae34 100644 (file)
@@ -1,3 +1,8 @@
+2019-10-04  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91977
+       * gcc.dg/Wstringop-overflow-18.c: New test.
+
 2019-10-04  Martin Sebor  <msebor@redhat.com>
 
        * gcc.dg/Wstringop-overflow-17.c: New test.
index 888fb80e7b9120885621e0ba05e017ffd1874129..092363aea31c8537a23423acb2e4f84e6341149b 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -Warray-bounds" } */
+/* { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
 
 typedef struct
 {
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-18.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-18.c
new file mode 100644 (file)
index 0000000..7866b58
--- /dev/null
@@ -0,0 +1,239 @@
+/* PR middle-end/91977 - missing -Wstringop-overflow on memcpy into
+   a pointer plus offset
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#define NOIPA          __attribute__ ((noipa))
+#define CONCAT(a, b)   a ## b
+#define CAT(a, b)      CONCAT (a, b)
+
+#define S3 "123"
+#define S4 "1234"
+
+char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8];
+char b1[1], b2[2], b3[3], b4[4], b5[5], b6[6], b7[7], b8[8];
+
+#define T(dst, src, off, n)                                    \
+  NOIPA void CAT (test_on_line_, __LINE__) (const void *s)     \
+  {                                                            \
+    __builtin_memcpy (dst + off, src, n);                      \
+  } typedef void dummy_type
+
+T (a4, s, 0, 1);
+T (a4, s, 1, 1);
+T (a4, s, 2, 1);
+T (a4, s, 3, 1);
+T (a4, s, 4, 1);    // { dg-warning "writing 1 byte into a region of size 0" }
+
+T (a4, s, 0, 2);
+T (a4, s, 1, 2);
+T (a4, s, 2, 2);
+T (a4, s, 3, 2);    // { dg-warning "writing 2 bytes into a region of size 1" }
+T (a4, s, 4, 2);    // { dg-warning "writing 2 bytes into a region of size 0" }
+
+T (a4, s, 0, 3);
+T (a4, s, 1, 3);
+T (a4, s, 2, 3);    // { dg-warning "writing 3 bytes into a region of size 2" }
+T (a4, s, 3, 3);    // { dg-warning "writing 3 bytes into a region of size 1" }
+T (a4, s, 4, 3);    // { dg-warning "writing 3 bytes into a region of size 0" }
+
+T (a4, s, 0, 4);
+T (a4, s, 1, 4);    // { dg-warning "writing 4 bytes into a region of size 3" }
+T (a4, s, 2, 4);    // { dg-warning "writing 4 bytes into a region of size 2" }
+T (a4, s, 3, 4);    // { dg-warning "writing 4 bytes into a region of size 1" }
+T (a4, s, 4, 4);    // { dg-warning "writing 4 bytes into a region of size 0" }
+
+T (a7, s, 3, 3);
+T (a7, s, 4, 3);
+T (a7, s, 5, 3);    // { dg-warning "writing 3 bytes into a region of size 2" }
+T (a7, s, 6, 3);    // { dg-warning "writing 3 bytes into a region of size 1" }
+T (a7, s, 7, 3);    // { dg-warning "writing 3 bytes into a region of size 0" }
+
+T (a7, s, 3, 4);
+T (a7, s, 4, 4);    // { dg-warning "writing 4 bytes into a region of size 3" }
+T (a7, s, 5, 4);    // { dg-warning "writing 4 bytes into a region of size 2" }
+T (a7, s, 6, 4);    // { dg-warning "writing 4 bytes into a region of size 1" }
+T (a7, s, 7, 4);    // { dg-warning "writing 4 bytes into a region of size 0" }
+
+T (a7, s, 1, 6);
+T (a7, s, 2, 6);    // { dg-warning "writing 6 bytes into a region of size 5" }
+T (a7, s, 3, 6);    // { dg-warning "writing 6 bytes into a region of size 4" }
+T (a7, s, 4, 6);    // { dg-warning "writing 6 bytes into a region of size 3" }
+T (a7, s, 5, 6);    // { dg-warning "writing 6 bytes into a region of size 2" }
+T (a7, s, 6, 6);    // { dg-warning "writing 6 bytes into a region of size 1" }
+T (a7, s, 7, 6);    // { dg-warning "writing 6 bytes into a region of size 0" }
+
+T (a8, s, 1, 7);
+T (a8, s, 2, 7);    // { dg-warning "writing 7 bytes into a region of size 6" }
+T (a8, s, 3, 7);    // { dg-warning "writing 7 bytes into a region of size 5" }
+T (a8, s, 4, 7);    // { dg-warning "writing 7 bytes into a region of size 4" }
+T (a8, s, 5, 7);    // { dg-warning "writing 7 bytes into a region of size 3" }
+T (a8, s, 6, 7);    // { dg-warning "writing 7 bytes into a region of size 2" }
+T (a8, s, 7, 7);    // { dg-warning "writing 7 bytes into a region of size 1" }
+T (a8, s, 8, 7);    // { dg-warning "writing 7 bytes into a region of size 0" }
+
+#undef T
+#define T(dst, src, off, n)                                    \
+  NOIPA void CAT (test_on_line_, __LINE__) (const void *s)     \
+  {                                                            \
+    char *d = dst + off;                                       \
+    __builtin_memcpy (d, src, n);                              \
+  } typedef void dummy_type
+
+T (a4, s, 0, 1);
+T (a4, s, 1, 1);
+T (a4, s, 2, 1);
+T (a4, s, 3, 1);
+T (a4, s, 4, 1);    // { dg-warning "writing 1 byte into a region of size 0" }
+
+T (a4, s, 0, 2);
+T (a4, s, 1, 2);
+T (a4, s, 2, 2);
+T (a4, s, 3, 2);    // { dg-warning "writing 2 bytes into a region of size 1" }
+T (a4, s, 4, 2);    // { dg-warning "writing 2 bytes into a region of size 0" }
+
+T (a4, s, 0, 3);
+T (a4, s, 1, 3);
+T (a4, s, 2, 3);    // { dg-warning "writing 3 bytes into a region of size 2" }
+T (a4, s, 3, 3);    // { dg-warning "writing 3 bytes into a region of size 1" }
+T (a4, s, 4, 3);    // { dg-warning "writing 3 bytes into a region of size 0" }
+
+T (a4, s, 0, 4);
+T (a4, s, 1, 4);    // { dg-warning "writing 4 bytes into a region of size 3" }
+T (a4, s, 2, 4);    // { dg-warning "writing 4 bytes into a region of size 2" }
+T (a4, s, 3, 4);    // { dg-warning "writing 4 bytes into a region of size 1" }
+T (a4, s, 4, 4);    // { dg-warning "writing 4 bytes into a region of size 0" }
+
+T (a7, s, 3, 3);
+T (a7, s, 4, 3);
+T (a7, s, 5, 3);    // { dg-warning "writing 3 bytes into a region of size 2" }
+T (a7, s, 6, 3);    // { dg-warning "writing 3 bytes into a region of size 1" }
+T (a7, s, 7, 3);    // { dg-warning "writing 3 bytes into a region of size 0" }
+
+T (a7, s, 3, 4);
+T (a7, s, 4, 4);    // { dg-warning "writing 4 bytes into a region of size 3" }
+T (a7, s, 5, 4);    // { dg-warning "writing 4 bytes into a region of size 2" }
+T (a7, s, 6, 4);    // { dg-warning "writing 4 bytes into a region of size 1" }
+T (a7, s, 7, 4);    // { dg-warning "writing 4 bytes into a region of size 0" }
+
+T (a7, s, 1, 6);
+T (a7, s, 2, 6);    // { dg-warning "writing 6 bytes into a region of size 5" }
+T (a7, s, 3, 6);    // { dg-warning "writing 6 bytes into a region of size 4" }
+T (a7, s, 4, 6);    // { dg-warning "writing 6 bytes into a region of size 3" }
+T (a7, s, 5, 6);    // { dg-warning "writing 6 bytes into a region of size 2" }
+T (a7, s, 6, 6);    // { dg-warning "writing 6 bytes into a region of size 1" }
+T (a7, s, 7, 6);    // { dg-warning "writing 6 bytes into a region of size 0" }
+
+#undef T
+#define T(dst, src, init, off, n)                      \
+  NOIPA void CAT (test_on_line_, __LINE__) (void)      \
+  {                                                    \
+    __builtin_strcpy (src, init);                      \
+    char *d = dst + off;                               \
+    __builtin_memcpy (d, src, n);                      \
+  } typedef void dummy_type
+
+
+T (a6, b6, S4, 0, 4);
+T (a6, b6, S4, 1, 4);
+T (a6, b6, S4, 2, 4);
+T (a6, b6, S4, 3, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a6, b6, S4, 4, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a6, b6, S4, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a6, b6, S4, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a7, b7, S4, 0, 4);
+T (a7, b7, S4, 1, 4);
+T (a7, b7, S4, 2, 4);
+T (a7, b7, S4, 3, 4);
+T (a7, b7, S4, 4, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a7, b7, S4, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a7, b7, S4, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a7, b7, S4, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a8, b4, S3, 0, 4);
+T (a8, b4, S3, 1, 4);
+T (a8, b4, S3, 2, 4);
+T (a8, b4, S3, 3, 4);
+T (a8, b4, S3, 4, 4);
+T (a8, b4, S3, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a8, b4, S3, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a8, b4, S3, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a8, b4, S3, 8, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a8, b8, S3, 0, 4);
+T (a8, b8, S3, 1, 4);
+T (a8, b8, S3, 2, 4);
+T (a8, b8, S3, 3, 4);
+T (a8, b8, S3, 4, 4);
+T (a8, b8, S3, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a8, b8, S3, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a8, b8, S3, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a8, b8, S3, 8, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a8, b8, S4, 0, 4);
+T (a8, b8, S4, 1, 4);
+T (a8, b8, S4, 2, 4);
+T (a8, b8, S4, 3, 4);
+T (a8, b8, S4, 4, 4);
+T (a8, b8, S4, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a8, b8, S4, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a8, b8, S4, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a8, b8, S4, 8, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a8, b8, S4, 0, 5);
+T (a8, b8, S4, 1, 5);
+T (a8, b8, S4, 2, 5);
+T (a8, b8, S4, 3, 5);
+T (a8, b8, S4, 4, 5);   // { dg-warning "writing 5 bytes into a region of size 4" } */
+T (a8, b8, S4, 5, 5);   // { dg-warning "writing 5 bytes into a region of size 3" } */
+T (a8, b8, S4, 6, 5);   // { dg-warning "writing 5 bytes into a region of size 2" } */
+T (a8, b8, S4, 7, 5);   // { dg-warning "writing 5 bytes into a region of size 1" } */
+T (a8, b8, S4, 8, 5);   // { dg-warning "writing 5 bytes into a region of size 0" } */
+
+T (a8, b8, S4, 0, 6);
+T (a8, b8, S4, 1, 6);
+T (a8, b8, S4, 2, 6);
+T (a8, b8, S4, 3, 6);   // { dg-warning "writing 6 bytes into a region of size 5" } */
+T (a8, b8, S4, 4, 6);   // { dg-warning "writing 6 bytes into a region of size 4" } */
+T (a8, b8, S4, 5, 6);   // { dg-warning "writing 6 bytes into a region of size 3" } */
+T (a8, b8, S4, 6, 6);   // { dg-warning "writing 6 bytes into a region of size 2" } */
+T (a8, b8, S4, 7, 6);   // { dg-warning "writing 6 bytes into a region of size 1" } */
+T (a8, b8, S4, 8, 6);   // { dg-warning "writing 6 bytes into a region of size 0" } */
+
+
+#undef T
+#define T(dst, init, off, n)                           \
+  NOIPA void CAT (test_on_line_, __LINE__) (char *src) \
+  {                                                    \
+    __builtin_strcpy (src, init);                      \
+    char *d = dst + off;                               \
+    __builtin_memcpy (d, src, n);                      \
+  } typedef void dummy_type
+
+T (a6, S4, 0, 4);
+T (a6, S4, 1, 4);
+T (a6, S4, 2, 4);
+T (a6, S4, 3, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a6, S4, 4, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a6, S4, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a6, S4, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a7, S4, 0, 4);
+T (a7, S4, 1, 4);
+T (a7, S4, 2, 4);
+T (a7, S4, 3, 4);
+T (a7, S4, 4, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a7, S4, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a7, S4, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a7, S4, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
+
+T (a8, S3, 0, 4);
+T (a8, S3, 1, 4);
+T (a8, S3, 2, 4);
+T (a8, S3, 3, 4);
+T (a8, S3, 4, 4);
+T (a8, S3, 5, 4);   // { dg-warning "writing 4 bytes into a region of size 3" } */
+T (a8, S3, 6, 4);   // { dg-warning "writing 4 bytes into a region of size 2" } */
+T (a8, S3, 7, 4);   // { dg-warning "writing 4 bytes into a region of size 1" } */
+T (a8, S3, 8, 4);   // { dg-warning "writing 4 bytes into a region of size 0" } */
index c82bc7c481555bee76f5f021a6c0f7941b8f11db..d68df19aa821d55c6455ce1c961513be56a1ac2a 100644 (file)
@@ -3801,40 +3801,44 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
       tree type = TREE_TYPE (exp);
       if (TREE_CODE (type) == INTEGER_TYPE
          && TYPE_MODE (type) == TYPE_MODE (char_type_node)
-         && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node))
+         && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node)
+         && tree_expr_nonzero_p (exp))
        {
-         /* Determine if the character EXP is known to be non-zero
-            (even if its exact value is not known) and if so, recurse
-            once to set the range, etc.  */
-         if (tree_expr_nonzero_p (exp))
-           return count_nonzero_bytes (build_int_cst (type, 1),
-                                       offset, nbytes, lenrange,
-                                       nulterm, allnul, allnonnul, snlim);
-         /* Don't know whether EXP is or isn't nonzero.  */
-         return false;
+         /* If the character EXP is known to be non-zero (even if its
+            exact value is not known) recurse once to set the range
+            for an arbitrary constant.  */
+         exp = build_int_cst (type, 1);
+         return count_nonzero_bytes (exp, offset, 1, lenrange,
+                                     nulterm, allnul, allnonnul, snlim);
        }
 
       gimple *stmt = SSA_NAME_DEF_STMT (exp);
-      if (gimple_code (stmt) != GIMPLE_PHI)
-       return false;
-
-      /* Avoid processing an SSA_NAME that has already been visited
-        or if an SSA_NAME limit has been reached.  Indicate success
-        if the former and failure if the latter.  */
-      if (int res = snlim.next_ssa_name (exp))
-       return res > 0;
-
-      /* Determine the minimum and maximum from the PHI arguments.  */
-      unsigned int n = gimple_phi_num_args (stmt);
-      for (unsigned i = 0; i != n; i++)
+      if (gimple_assign_single_p (stmt))
        {
-         tree def = gimple_phi_arg_def (stmt, i);
-         if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm,
-                                   allnul, allnonnul, snlim))
+         exp = gimple_assign_rhs1 (stmt);
+         if (TREE_CODE (exp) != MEM_REF)
            return false;
        }
+      else if (gimple_code (stmt) == GIMPLE_PHI)
+       {
+         /* Avoid processing an SSA_NAME that has already been visited
+            or if an SSA_NAME limit has been reached.  Indicate success
+            if the former and failure if the latter.  */
+         if (int res = snlim.next_ssa_name (exp))
+           return res > 0;
+
+         /* Determine the minimum and maximum from the PHI arguments.  */
+         unsigned int n = gimple_phi_num_args (stmt);
+         for (unsigned i = 0; i != n; i++)
+           {
+             tree def = gimple_phi_arg_def (stmt, i);
+             if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm,
+                                       allnul, allnonnul, snlim))
+               return false;
+           }
 
-      return true;
+         return true;
+       }
     }
 
   if (TREE_CODE (exp) == MEM_REF)
@@ -3897,14 +3901,25 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
       prep = reinterpret_cast <char *>(buf);
       /* Try to extract the representation of the constant object
         or expression starting from the offset.  */
-      nbytes = native_encode_expr (exp, buf, sizeof buf, offset);
-      if (!nbytes)
-       return false;
+      unsigned repsize = native_encode_expr (exp, buf, sizeof buf, offset);
+      if (repsize < nbytes)
+       {
+         /* This should only happen when REPSIZE is zero because EXP
+            doesn't denote an object with a known initializer, except
+            perhaps when the reference reads past its end.  */
+         lenrange[0] = 0;
+         prep = NULL;
+       }
+      else
+       nbytes = repsize;
     }
 
+  if (!nbytes)
+    return false;
+
   /* Compute the number of leading nonzero bytes in the representation
      and update the minimum and maximum.  */
-  unsigned n = strnlen (prep, nbytes);
+  unsigned n = prep ? strnlen (prep, nbytes) : nbytes;
 
   if (n < lenrange[0])
     lenrange[0] = n;