PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing array plus...
authorMartin Sebor <msebor@redhat.com>
Wed, 10 Jun 2020 18:00:08 +0000 (12:00 -0600)
committerMartin Sebor <msebor@redhat.com>
Wed, 10 Jun 2020 18:02:12 +0000 (12:02 -0600)
Also resolves:
PR middle-end/92939 - missing -Wstringop-overflow on negative index from the end of array

gcc/ChangeLog:

PR middle-end/95353
PR middle-end/92939
* builtins.c (inform_access): New function.
(check_access): Call it.  Add argument.
(addr_decl_size): Remove.
(get_range): New function.
(compute_objsize): New overload.  Only use compute_builtin_object_size
with raw memory function.
(check_memop_access): Pass new argument to compute_objsize and
check_access.
(expand_builtin_memchr, expand_builtin_strcat): Same.
(expand_builtin_strcpy, expand_builtin_stpcpy_1): Same.
(expand_builtin_stpncpy, check_strncat_sizes): Same.
(expand_builtin_strncat, expand_builtin_strncpy): Same.
(expand_builtin_memcmp): Same.
* builtins.h (check_nul_terminated_array): Declare extern.
(check_access): Add argument.
(struct access_ref, struct access_data): New structs.
* gimple-ssa-warn-restrict.c (clamp_offset): New helper.
(builtin_access::overlap): Call it.
* tree-object-size.c (decl_init_size): Declare extern.
(addr_object_size): Correct offset computation.
* tree-object-size.h (decl_init_size): Declare.
* tree-ssa-strlen.c (handle_integral_assign): Remove a call
to maybe_warn_overflow when assigning to an SSA_NAME.

gcc/testsuite/ChangeLog:

PR middle-end/95353
PR middle-end/92939
* c-c++-common/Wstringop-truncation.c: Remove an xfail.
* gcc.dg/Warray-bounds-46.c: Remove a bogus warning.
* gcc.dg/Wrestrict-9.c: Disable -Wstringop-overflow.
* gcc.dg/Wstringop-overflow-12.c: Remove xfails.
* gcc.dg/Wstringop-overflow-28.c: Same.
* gcc.dg/builtin-stringop-chk-4.c: Same.
* gcc.dg/builtin-stringop-chk-5.c: Same.
* gcc.dg/builtin-stringop-chk-8.c: Same.
* gcc.dg/strlenopt-74.c: Avoid buffer overflow.
* gcc.dg/Wstringop-overflow-34.c: New test.
* gcc.dg/Wstringop-overflow-35.c: New test.
* gcc.dg/Wstringop-overflow-36.c: New test.
* gcc.dg/Wstringop-overflow-37.c: New test.
* gcc.dg/Wstringop-overflow-38.c: New test.

20 files changed:
gcc/builtins.c
gcc/builtins.h
gcc/gimple-ssa-warn-restrict.c
gcc/testsuite/c-c++-common/Wstringop-truncation.c
gcc/testsuite/gcc.dg/Warray-bounds-46.c
gcc/testsuite/gcc.dg/Wrestrict-9.c
gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
gcc/testsuite/gcc.dg/Wstringop-overflow-34.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-35.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-36.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-37.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-38.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
gcc/testsuite/gcc.dg/strlenopt-74.c
gcc/tree-object-size.c
gcc/tree-object-size.h
gcc/tree-ssa-strlen.c

index 667d36c940ba3faca6730908abbbaa2173c00897..caab188e81c429fcc419689fe5c5befe2f4b2d38 100644 (file)
@@ -3394,6 +3394,130 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
   return warned;
 }
 
+/* Issue an inform message describing the target of an access REF.
+   WRITE is set for a write access and clear for a read access.  */
+
+static void
+inform_access (const access_ref &ref, bool write)
+{
+  if (!ref.ref)
+    return;
+
+  /* Convert offset range and avoid including a zero range since it isn't
+     necessarily meaningful.  */
+  long long minoff = 0, maxoff = 0;
+  if (wi::fits_shwi_p (ref.offrng[0])
+      && wi::fits_shwi_p (ref.offrng[1]))
+    {
+      minoff = ref.offrng[0].to_shwi ();
+      maxoff = ref.offrng[1].to_shwi ();
+    }
+
+  /* Convert size range and always include it since all sizes are
+     meaningful. */
+  unsigned long long minsize = 0, maxsize = 0;
+  if (wi::fits_shwi_p (ref.sizrng[0])
+      && wi::fits_shwi_p (ref.sizrng[1]))
+    {
+      minsize = ref.sizrng[0].to_shwi ();
+      maxsize = ref.sizrng[1].to_shwi ();
+    }
+
+  char sizestr[80];
+  location_t loc;
+  tree allocfn = NULL_TREE;
+  if (TREE_CODE (ref.ref) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (ref.ref);
+      gcc_assert (is_gimple_call (stmt));
+      loc = gimple_location (stmt);
+      allocfn = gimple_call_fndecl (stmt);
+      if (!allocfn)
+       /* Handle calls through pointers to functions.  */
+       allocfn = gimple_call_fn (stmt);
+
+      /* SIZRNG doesn't necessarily have the same range as the allocation
+        size determined by gimple_call_alloc_size ().  */
+
+      if (minsize == maxsize)
+       sprintf (sizestr, "%llu", minsize);
+      else
+       sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+
+    }
+  else
+    loc = DECL_SOURCE_LOCATION (ref.ref);
+
+  if (write)
+    {
+      if (DECL_P (ref.ref))
+       {
+         if (minoff == maxoff)
+           {
+             if (minoff == 0)
+               inform (loc, "destination object %qD", ref.ref);
+             else
+               inform (loc, "at offset %lli into destination object %qD",
+                       minoff, ref.ref);
+           }
+         else
+           inform (loc, "at offset [%lli, %lli] into destination object %qD",
+                   minoff, maxoff, ref.ref);
+         return;
+       }
+
+      if (minoff == maxoff)
+       {
+         if (minoff == 0)
+           inform (loc, "destination object of size %s allocated by %qE",
+                   sizestr, allocfn);
+         else
+           inform (loc,
+                   "at offset %lli into destination object of size %s "
+                   "allocated by %qE", minoff, sizestr, allocfn);
+       }
+      else
+       inform (loc,
+               "at offset [%lli, %lli] into destination object of size %s "
+               "allocated by %qE",
+               minoff, maxoff, sizestr, allocfn);
+
+      return;
+    }
+
+  if (DECL_P (ref.ref))
+    {
+      if (minoff == maxoff)
+       {
+         if (minoff == 0)
+           inform (loc, "source object %qD", ref.ref);
+         else
+           inform (loc, "at offset %lli into source object %qD",
+                   minoff, ref.ref);
+       }
+      else
+       inform (loc, "at offset [%lli, %lli] into source object %qD",
+               minoff, maxoff, ref.ref);
+      return;
+    }
+
+  if (minoff == maxoff)
+    {
+      if (minoff == 0)
+       inform (loc, "source object of size %s allocated by %qE",
+               sizestr, allocfn);
+      else
+       inform (loc,
+               "at offset %lli into source object of size %s "
+               "allocated by %qE", minoff, sizestr, allocfn);
+    }
+  else
+    inform (loc,
+           "at offset [%lli, %lli] into source object of size %s "
+           "allocated by %qE",
+           minoff, maxoff, sizestr, allocfn);
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
    the operation does not lead to buffer overflow or read past the end.
@@ -3423,13 +3547,16 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
    ACCESS is true for accesses, false for simple size checks in calls
    to functions that neither read from nor write to the region.
 
+   When nonnull, PAD points to a more detailed description of the access.
+
    If the call is successfully verified as safe return true, otherwise
    return false.  */
 
 bool
 check_access (tree exp, tree, tree, tree dstwrite,
              tree maxread, tree srcstr, tree dstsize,
-             bool access /* = true */)
+             bool access /* = true */,
+             const access_data *pad /* = NULL */)
 {
   int opt = OPT_Wstringop_overflow_;
 
@@ -3633,7 +3760,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
                                    exp, range[0], range[1],
                                    dstsize));
          if (warned)
-           TREE_NO_WARNING (exp) = true;
+           {
+             TREE_NO_WARNING (exp) = true;
+             if (pad)
+               inform_access (pad->dst, true);
+           }
 
          /* Return error when an overflow has been detected.  */
          return false;
@@ -3741,8 +3872,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
       loc = expansion_point_location_if_in_system_header (loc);
 
       if (warn_for_access (loc, func, exp, opt, range, slen, access))
-       TREE_NO_WARNING (exp) = true;
-
+       {
+         TREE_NO_WARNING (exp) = true;
+         if (pad)
+           inform_access (pad->src, false);
+       }
       return false;
     }
 
@@ -3841,185 +3975,135 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   return wide_int_to_tree (sizetype, rng1[1]);
 }
 
-/* Helper for compute_objsize.  Returns the constant size of the DEST
-   if it refers to a variable or field and sets *PDECL to the DECL and
-   *POFF to zero.  Otherwise returns null for other nodes.  */
+/* Wrapper around the wide_int overload of get_range.  Returns the same
+   result but accepts offset_int instead.  */
 
-static tree
-addr_decl_size (tree dest, tree *pdecl, tree *poff)
+static bool
+get_range (tree x, signop sgn, offset_int r[2],
+          const vr_values *rvals /* = NULL */)
 {
-  if (TREE_CODE (dest) == ADDR_EXPR)
-    dest = TREE_OPERAND (dest, 0);
-
-  if (DECL_P (dest))
-    {
-      *pdecl = dest;
-      *poff = integer_zero_node;
-      if (tree size = DECL_SIZE_UNIT (dest))
-       return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
-    }
-
-  if (TREE_CODE (dest) == COMPONENT_REF)
-    {
-      *pdecl = TREE_OPERAND (dest, 1);
-      *poff = integer_zero_node;
-      /* Only return constant sizes for now while callers depend on it.  */
-      if (tree size = component_ref_size (dest))
-       return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
-    }
+  wide_int wr[2];
+  if (!get_range (x, wr, rvals))
+    return false;
 
-  return NULL_TREE;
+  r[0] = offset_int::from (wr[0], sgn);
+  r[1] = offset_int::from (wr[1], sgn);
+  return true;
 }
 
-/* Helper to compute the size of the object referenced by the DEST
+/* Helper to compute the size of the object referenced by the PTR
    expression which must have pointer type, using Object Size type
    OSTYPE (only the least significant 2 bits are used).
-   Returns an estimate of the size of the object represented as
-   a sizetype constant if successful or NULL when the size cannot
-   be determined.
-   When the referenced object involves a non-constant offset in some
-   range the returned value represents the largest size given the
-   smallest non-negative offset in the range.
-   If nonnull, sets *PDECL to the decl of the referenced subobject
-   if it can be determined, or to null otherwise.  Likewise, when
-   POFF is nonnull *POFF is set to the offset into *PDECL.
+   On success, sets PREF->REF to the DECL of the referenced object
+   if it's unique, otherwise to null, PREF->OFFRNG to the range of
+   offsets into it, and PREF->SIZRNG to the range of sizes of
+   the object(s).
+   VISITED is used to avoid visiting the same PHI operand multiple
+   times, and, when nonnull, RVALS to determine range information.
+   Returns true on success, false when the size cannot be determined.
 
    The function is intended for diagnostics and should not be used
    to influence code generation or optimization.  */
 
-tree
-compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
-                tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
+static bool
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+                bitmap *visited, const vr_values *rvals /* = NULL */)
 {
-  tree dummy_decl = NULL_TREE;
-  if (!pdecl)
-    pdecl = &dummy_decl;
+  if (ostype == 0)
+    {
+      /* Use BOS only for raw memory functions like memcpy to get
+        the size of the largest enclosing object.  */
+      tree off = NULL_TREE;
+      unsigned HOST_WIDE_INT size;
+      if (compute_builtin_object_size (ptr, ostype, &size, &pref->ref, &off))
+       {
+         if (off)
+           {
+             offset_int offset = wi::to_offset (off);
+             pref->offrng[0] += offset;
+             pref->offrng[1] += offset;
+
+             /* compute_builtin_object_size() returns the remaining
+                size in PTR.  Add the offset to it to get the full
+                size.  */
+             pref->sizrng[0] = pref->sizrng[1] = size + offset;
+           }
+         else
+           pref->sizrng[0] = pref->sizrng[1] = size;
+         return true;
+       }
+    }
 
-  tree dummy_off = NULL_TREE;
-  if (!poff)
-    poff = &dummy_off;
+  const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
+  if (addr)
+    ptr = TREE_OPERAND (ptr, 0);
 
-  /* Only the two least significant bits are meaningful.  */
-  ostype &= 3;
+  if (DECL_P (ptr))
+    {
+      /* Bail if the reference is to the pointer itself (as opposed
+        to what it points to).  */
+      if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
+       return false;
+
+      tree size = decl_init_size (ptr, false);
+      if (!size || TREE_CODE (size) != INTEGER_CST)
+       return false;
 
-  if (ostype)
-    /* Except for overly permissive calls to memcpy and other raw
-       memory functions with zero OSTYPE, detect the size from simple
-       DECLs first to more reliably than compute_builtin_object_size
-       set *PDECL and *POFF.  */
-    if (tree size = addr_decl_size (dest, pdecl, poff))
-      return size;
+      pref->ref = ptr;
+      pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+      return true;
+    }
 
-  unsigned HOST_WIDE_INT size;
-  if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
-    return build_int_cst (sizetype, size);
+  const tree_code code = TREE_CODE (ptr);
 
-  if (TREE_CODE (dest) == SSA_NAME)
+  if (code == COMPONENT_REF)
     {
-      gimple *stmt = SSA_NAME_DEF_STMT (dest);
-      if (is_gimple_call (stmt))
+      if (ostype == 0)
        {
-         /* If STMT is a call to an allocation function get the size
-            from its argument(s).  If successful, also set *PDECL to
-            DEST for the caller to include in diagnostics.  */
-         if (tree size = gimple_call_alloc_size (stmt))
-           {
-             *pdecl = dest;
-             *poff = integer_zero_node;
-             return size;
-           }
-         return NULL_TREE;
+         /* For raw memory functions like memcpy bail if the size
+            of the enclosing object cannot be determined.  */
+         access_ref tmpref;
+         tree ref = TREE_OPERAND (ptr, 0);
+         if (!compute_objsize (ref, ostype, &tmpref, visited, rvals)
+             || !tmpref.ref)
+           return false;
        }
 
-      if (!is_gimple_assign (stmt))
-       return NULL_TREE;
-
-      dest = gimple_assign_rhs1 (stmt);
+      tree field = TREE_OPERAND (ptr, 1);
+      /* Bail if the reference is to the pointer itself (as opposed
+        to what it points to).  */
+      if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
+       return false;
 
-      tree_code code = gimple_assign_rhs_code (stmt);
-      if (code == POINTER_PLUS_EXPR)
+      pref->ref = field;
+      /* Only return constant sizes for now while callers depend
+        on it.  INT0LEN is true for interior zero-length arrays.  */
+      bool int0len = false;
+      tree size = component_ref_size (ptr, &int0len);
+      if (int0len)
        {
-         /* compute_builtin_object_size fails for addresses with
-            non-constant offsets.  Try to determine the range of
-            such an offset here and use it to adjust the constant
-            size.  */
-         tree off = gimple_assign_rhs2 (stmt);
-         if (TREE_CODE (off) == INTEGER_CST)
-           {
-             if (tree size = compute_objsize (dest, ostype, pdecl, poff))
-               {
-                 wide_int wioff = wi::to_wide (off);
-                 wide_int wisiz = wi::to_wide (size);
-
-                 /* Ignore negative offsets for now.  For others,
-                    use the lower bound as the most optimistic
-                    estimate of the (remaining) size.  */
-                 if (wi::neg_p (wioff))
-                   ;
-                 else
-                   {
-                     if (*poff)
-                       {
-                         *poff = fold_convert (ptrdiff_type_node, *poff);
-                         off = fold_convert (ptrdiff_type_node, *poff);
-                         *poff = size_binop (PLUS_EXPR, *poff, off);
-                       }
-                     else
-                       *poff = off;
-                     if (wi::ltu_p (wioff, wisiz))
-                       return wide_int_to_tree (TREE_TYPE (size),
-                                                wi::sub (wisiz, wioff));
-                     return size_zero_node;
-                   }
-               }
-           }
-         else if (TREE_CODE (off) == SSA_NAME
-                  && INTEGRAL_TYPE_P (TREE_TYPE (off)))
-           {
-             wide_int min, max;
-             enum value_range_kind rng = get_range_info (off, &min, &max);
-
-             if (rng == VR_RANGE)
-               if (tree size = compute_objsize (dest, ostype, pdecl, poff))
-                 {
-                   wide_int wisiz = wi::to_wide (size);
-
-                   /* Ignore negative offsets for now.  For others,
-                      use the lower bound as the most optimistic
-                      estimate of the (remaining)size.  */
-                   if (wi::neg_p (min) || wi::neg_p (max))
-                     ;
-                   else
-                     {
-                       /* FIXME: For now, since the offset is non-constant,
-                          clear *POFF to keep it from being "misused."
-                          Eventually *POFF will need to become a range that
-                          can be properly added to the outer offset if it
-                          too is one.  */
-                       *poff = NULL_TREE;
-                       if (wi::ltu_p (min, wisiz))
-                         return wide_int_to_tree (TREE_TYPE (size),
-                                                  wi::sub (wisiz, min));
-                       return size_zero_node;
-                     }
-                 }
-           }
+         pref->sizrng[0] = pref->sizrng[1] = 0;
+         return true;
        }
-      else if (code != ADDR_EXPR)
-       return NULL_TREE;
-    }
 
-  /* Unless computing the largest size (for memcpy and other raw memory
-     functions), try to determine the size of the object from its type.  */
-  if (!ostype)
-    return NULL_TREE;
+      if (!size || TREE_CODE (size) != INTEGER_CST)
+       return false;
+
+      pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+      return true;
+    }
 
-  if (TREE_CODE (dest) == ARRAY_REF
-      || TREE_CODE (dest) == MEM_REF)
+  if (code == ARRAY_REF || code == MEM_REF)
     {
-      tree ref = TREE_OPERAND (dest, 0);
+      tree ref = TREE_OPERAND (ptr, 0);
       tree reftype = TREE_TYPE (ref);
-      if (TREE_CODE (dest) == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
+      if (code == ARRAY_REF
+         && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
+       /* Avoid arrays of pointers.  FIXME: Hande pointers to arrays
+          of known bound.  */
+       return false;
+
+      if (code == MEM_REF && TREE_CODE (reftype) == POINTER_TYPE)
        {
          /* Give up for MEM_REFs of vector types; those may be synthesized
             from multiple assignments to consecutive data members.  See PR
@@ -4028,158 +4112,175 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
             MEM_REFs at the time they're created.  */
          reftype = TREE_TYPE (reftype);
          if (TREE_CODE (reftype) == VECTOR_TYPE)
-           return NULL_TREE;
+           return false;
        }
-      tree off = TREE_OPERAND (dest, 1);
-      if (tree size = compute_objsize (ref, ostype, pdecl, poff))
-       {
-         /* If the declaration of the destination object is known
-            to have zero size, return zero.  */
-         if (integer_zerop (size)
-             && *pdecl && DECL_P (*pdecl)
-             && *poff && integer_zerop (*poff))
-           return size_zero_node;
-
-         /* A valid offset into a declared object cannot be negative.
-            A zero size with a zero "inner" offset is still zero size
-            regardless of the "other" offset OFF.  */
-         if (*poff
-             && ((integer_zerop (*poff) && integer_zerop (size))
-                 || (TREE_CODE (*poff) == INTEGER_CST
-                     && tree_int_cst_sgn (*poff) < 0)))
-           return size_zero_node;
-
-         wide_int offrng[2];
-         if (!get_range (off, offrng, rvals))
-           return NULL_TREE;
 
-         /* Convert to the same precision to keep wide_int from "helpfully"
-            crashing whenever it sees other arguments.  */
-         const unsigned sizprec = TYPE_PRECISION (sizetype);
-         offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
-         offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
+      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+       return false;
 
-         /* Adjust SIZE either up or down by the sum of *POFF and OFF
-            above.  */
-         if (TREE_CODE (dest) == ARRAY_REF)
-           {
-             tree lowbnd = array_ref_low_bound (dest);
-             if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
-               {
-                 /* Adjust the offset by the low bound of the array
-                    domain (normally zero but 1 in Fortran).  */
-                 unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
-                 offrng[0] -= lb;
-                 offrng[1] -= lb;
-               }
+      offset_int orng[2];
+      tree off = TREE_OPERAND (ptr, 1);
+      if (!get_range (off, SIGNED, orng, rvals))
+       /* Fail unless the size of the object is zero.  */
+       return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
 
-             /* Convert the array index into a byte offset.  */
-             tree eltype = TREE_TYPE (dest);
-             tree tpsize = TYPE_SIZE_UNIT (eltype);
-             if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
-               {
-                 wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
-                 offrng[0] *= wsz;
-                 offrng[1] *= wsz;
-               }
-             else
-               return NULL_TREE;
+      if (TREE_CODE (ptr) == ARRAY_REF)
+       {
+         /* Convert the array index range determined above to a byte
+            offset.  */
+         tree lowbnd = array_ref_low_bound (ptr);
+         if (!integer_zerop (lowbnd) && tree_fits_uhwi_p (lowbnd))
+           {
+             /* Adjust the index by the low bound of the array domain
+                (normally zero but 1 in Fortran).  */
+             unsigned HOST_WIDE_INT lb = tree_to_uhwi (lowbnd);
+             orng[0] -= lb;
+             orng[1] -= lb;
            }
 
-         wide_int wisize = wi::to_wide (size);
+         tree eltype = TREE_TYPE (ptr);
+         tree tpsize = TYPE_SIZE_UNIT (eltype);
+         if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
+           return false;
+
+         offset_int sz = wi::to_offset (tpsize);
+         orng[0] *= sz;
+         orng[1] *= sz;
 
-         if (!*poff)
+         if (TREE_CODE (eltype) == ARRAY_TYPE)
            {
-             /* If the "inner" offset is unknown and the "outer" offset
-                is either negative or less than SIZE, return the size
-                minus the offset.  This may be overly optimistic in
-                the first case if the inner offset happens to be less
-                than the absolute value of the outer offset.  */
-             if (wi::neg_p (offrng[0]))
-               return size;
-             if (wi::ltu_p (offrng[0], wisize))
-               return build_int_cst (sizetype, (wisize - offrng[0]).to_uhwi ());
-             return size_zero_node;
+             pref->sizrng[0] = pref->offrng[0] + orng[0] + sz;
+             pref->sizrng[1] = pref->offrng[1] + orng[1] + sz;
            }
+       }
+
+      pref->offrng[0] += orng[0];
+      pref->offrng[1] += orng[1];
 
-         /* Convert to the same precision to keep wide_int from "helpfuly"
-            crashing whenever it sees other argumments.  */
-         offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
-         offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
+      return true;
+    }
 
-         tree dstoff = *poff;
-         if (integer_zerop (*poff))
-           *poff = off;
-         else if (!integer_zerop (off))
+  if (TREE_CODE (ptr) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+      if (is_gimple_call (stmt))
+       {
+         /* If STMT is a call to an allocation function get the size
+            from its argument(s).  If successful, also set *PDECL to
+            PTR for the caller to include in diagnostics.  */
+         wide_int wr[2];
+         if (gimple_call_alloc_size (stmt, wr, rvals))
            {
-             *poff = fold_convert (ptrdiff_type_node, *poff);
-             off = fold_convert (ptrdiff_type_node, off);
-             *poff = size_binop (PLUS_EXPR, *poff, off);
+             pref->ref = ptr;
+             pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+             pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+             return true;
            }
+         return false;
+       }
 
-         if (!wi::neg_p (offrng[0]))
-           {
-             if (TREE_CODE (size) != INTEGER_CST)
-               return NULL_TREE;
+      /* TODO: Handle PHI.  */
 
-             /* Return the difference between the size and the offset
-                or zero if the offset is greater.  */
-             wide_int wisize = wi::to_wide (size, sizprec);
-             if (wi::ltu_p (wisize, offrng[0]))
-               return size_zero_node;
+      if (!is_gimple_assign (stmt))
+       return false;
 
-             return wide_int_to_tree (sizetype, wisize - offrng[0]);
-           }
+      ptr = gimple_assign_rhs1 (stmt);
+
+      tree_code code = gimple_assign_rhs_code (stmt);
+      if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
+       /* Avoid conversions from non-pointers.  */
+       return false;
 
-         wide_int dstoffrng[2];
-         if (TREE_CODE (dstoff) == INTEGER_CST)
-           dstoffrng[0] = dstoffrng[1] = wi::to_wide (dstoff);
-         else if (TREE_CODE (dstoff) == SSA_NAME)
+      if (code == POINTER_PLUS_EXPR)
+       {
+         /* If the the offset in the expression can be determined use
+            it to adjust the overall offset.  Otherwise, set the overall
+            offset to the maximum.  */
+         offset_int orng[2];
+         tree off = gimple_assign_rhs2 (stmt);
+         if (!get_range (off, SIGNED, orng, rvals)
+             || !wi::les_p (orng[0], orng[1]))
            {
-             enum value_range_kind rng
-               = get_range_info (dstoff, dstoffrng, dstoffrng + 1);
-             if (rng != VR_RANGE)
-               return NULL_TREE;
+             orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
+             orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
            }
-         else
-           return NULL_TREE;
-
-         dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
-         dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
 
-         if (!wi::neg_p (dstoffrng[0]))
-           wisize += dstoffrng[0];
-
-         offrng[1] += dstoffrng[1];
-         if (wi::neg_p (offrng[1]))
-           return size_zero_node;
-
-         return wide_int_to_tree (sizetype, wisize);
+         pref->offrng[0] += orng[0];
+         pref->offrng[1] += orng[1];
        }
+      else if (code != ADDR_EXPR)
+       return false;
 
-      return NULL_TREE;
+      return compute_objsize (ptr, ostype, pref, visited, rvals);
     }
 
-  /* Try simple DECLs not handled above.  */
-  if (tree size = addr_decl_size (dest, pdecl, poff))
-    return size;
-
-  tree type = TREE_TYPE (dest);
-  if (TREE_CODE (type) == POINTER_TYPE)
-    type = TREE_TYPE (type);
-
+  tree type = TREE_TYPE (ptr);
   type = TYPE_MAIN_VARIANT (type);
-  if (TREE_CODE (dest) == ADDR_EXPR)
-    dest = TREE_OPERAND (dest, 0);
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    ptr = TREE_OPERAND (ptr, 0);
 
   if (TREE_CODE (type) == ARRAY_TYPE
-      && !array_at_struct_end_p (dest))
+      && !array_at_struct_end_p (ptr))
     {
       if (tree size = TYPE_SIZE_UNIT (type))
-       return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
+       return get_range (size, UNSIGNED, pref->sizrng, rvals);
     }
 
-  return NULL_TREE;
+  return false;
+}
+
+/* Convenience wrapper around the above.  */
+
+static tree
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+                const vr_values *rvals = NULL)
+{
+  bitmap visited = NULL;
+
+  bool success
+    = compute_objsize (ptr, ostype, pref, &visited, rvals);
+
+  if (visited)
+    BITMAP_FREE (visited);
+
+  if (!success)
+    return NULL_TREE;
+
+  if (pref->offrng[0] < 0)
+    {
+      if (pref->offrng[1] < 0)
+       return size_zero_node;
+
+      pref->offrng[0] = 0;
+    }
+
+  if (pref->sizrng[1] < pref->offrng[0])
+    return size_zero_node;
+
+  return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]);
+}
+
+/* Transitional wrapper around the above.  The function should be removed
+   once callers transition to one of the two above.  */
+
+tree
+compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
+                tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
+{
+  /* Set the initial offsets to zero and size to negative to indicate
+     none has been computed yet.  */
+  access_ref ref;
+  tree size = compute_objsize (ptr, ostype, &ref, rvals);
+  if (!size)
+    return NULL_TREE;
+
+  if (pdecl)
+    *pdecl = ref.ref;
+
+  if (poff)
+    *poff = wide_int_to_tree (ptrdiff_type_node, ref.offrng[ref.offrng[0] < 0]);
+
+  return size;
 }
 
 /* Helper to determine and check the sizes of the source and the destination
@@ -4196,11 +4297,12 @@ check_memop_access (tree exp, tree dest, tree src, tree size)
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
      type specified by the option.  */
-  tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0);
+  access_data data;
+  tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, 0, &data.dst);
 
   return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
-                      srcsize, dstsize);
+                      srcsize, dstsize, true, &data);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -4220,9 +4322,11 @@ expand_builtin_memchr (tree exp, rtx)
      of the object.  */
   if (warn_stringop_overflow)
     {
-      tree size = compute_objsize (arg1, 0);
+      access_data data;
+      tree size = compute_objsize (arg1, 0, &data.src);
       check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
-                   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
+                   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE,
+                   true, &data);
     }
 
   return NULL_RTX;
@@ -4497,10 +4601,11 @@ expand_builtin_strcat (tree exp)
      just diagnose cases when the souce string is longer than
      the destination object.  */
 
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+  access_data data;
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
-               destsize);
+               destsize, true, &data);
 
   return NULL_RTX;
 }
@@ -4521,9 +4626,11 @@ expand_builtin_strcpy (tree exp, rtx target)
 
   if (warn_stringop_overflow)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      access_data data;
+      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
+                                      &data.dst);
       check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-                   src, destsize);
+                   src, destsize, true, &data);
     }
 
   if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target))
@@ -4579,9 +4686,11 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
 
   if (warn_stringop_overflow)
     {
-      tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
+      access_data data;
+      tree destsize = compute_objsize (dst, warn_stringop_overflow - 1,
+                                      &data.dst);
       check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
-                   src, destsize);
+                   src, destsize, true, &data);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -4690,10 +4799,12 @@ expand_builtin_stpncpy (tree exp, rtx)
   if (!check_nul_terminated_array (exp, src, len))
     return NULL_RTX;
 
+  access_data data;
   /* The size of the destination object.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
-  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize,
+               true, &data);
 
   return NULL_RTX;
 }
@@ -4733,12 +4844,13 @@ check_strncat_sizes (tree exp, tree objsize)
   /* Try to verify that the destination is big enough for the shortest
      string.  */
 
+  access_data data;
   if (!objsize && warn_stringop_overflow)
     {
       /* If it hasn't been provided by __strncat_chk, try to determine
         the size of the destination object into which the source is
         being copied.  */
-      objsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      objsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
     }
 
   /* Add one for the terminating nul.  */
@@ -4769,10 +4881,10 @@ check_strncat_sizes (tree exp, tree objsize)
          && tree_int_cst_lt (maxread, srclen)))
     srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_access will also
+  /* The number of bytes to write is LEN but check_access will alsoa
      check SRCLEN if LEN's value isn't known.  */
   return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
-                      objsize);
+                      objsize, true, &data);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -4810,10 +4922,11 @@ expand_builtin_strncat (tree exp, rtx)
       maxlen = lendata.maxbound;
     }
 
+  access_data data;
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
 
   /* Add one for the terminating nul.  */
   tree srclen = (maxlen
@@ -4843,8 +4956,8 @@ expand_builtin_strncat (tree exp, rtx)
          && tree_int_cst_lt (maxread, srclen)))
     srclen = maxread;
 
-  /* The number of bytes to write is SRCLEN.  */
-  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize,
+               true, &data);
 
   return NULL_RTX;
 }
@@ -4873,13 +4986,14 @@ expand_builtin_strncpy (tree exp, rtx target)
 
   if (warn_stringop_overflow)
     {
-      tree destsize = compute_objsize (dest,
-                                      warn_stringop_overflow - 1);
+      access_data data;
+      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1,
+                                      &data.dst);
 
       /* The number of bytes to write is LEN but check_access will also
         check SLEN if LEN's value isn't known.  */
       check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
-                   destsize);
+                   destsize, true, &data);
     }
 
   /* We must be passed a constant len and src parameter.  */
@@ -5192,16 +5306,18 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
 
   /* Diagnose calls where the specified length exceeds the size of either
      object.  */
-  tree size = compute_objsize (arg1, 0);
+  access_data data;
+  tree size = compute_objsize (arg1, 0, &data.src);
   no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
                              len, /*maxread=*/NULL_TREE, size,
-                             /*objsize=*/NULL_TREE);
+                             /*objsize=*/NULL_TREE, true, &data);
   if (no_overflow)
     {
-      size = compute_objsize (arg2, 0);
+      access_data data;
+      size = compute_objsize (arg2, 0, &data.src);
       no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE,
                                  len,  /*maxread=*/NULL_TREE, size,
-                                 /*objsize=*/NULL_TREE);
+                                 /*objsize=*/NULL_TREE, true, &data);
     }
 
   /* If the specified length exceeds the size of either object, 
index 73e85d66e9b7d165b5d871cc28131efec505772a..8b812ceb2c4e9660519b12004b0ce4171a378562 100644 (file)
@@ -152,12 +152,39 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
-bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
+extern bool check_nul_terminated_array (tree, tree, tree = NULL_TREE);
 extern void warn_string_no_nul (location_t, const char *, tree, tree);
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
 extern bool builtin_with_linkage_p (tree);
-extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
-                         bool = true);
 
+/* Describes a reference to an object used in an access.  */
+struct access_ref
+{
+  access_ref (): ref ()
+  {
+    /* Set to valid.  */
+    offrng[0] = offrng[1] = 0;
+    /* Invalidate.   */
+    sizrng[0] = sizrng[1] = -1;
+  }
+
+  /* Reference to the object.  */
+  tree ref;
+
+  /* Range of offsets into and sizes of the object(s).  */
+  offset_int offrng[2];
+  offset_int sizrng[2];
+};
+
+/* Describes a pair of references used in an access by built-in
+   functions like memcpy.  */
+struct access_data
+{
+  /* Destination and source of the access.  */
+  access_ref dst, src;
+};
+
+extern bool check_access (tree, tree, tree, tree, tree, tree, tree,
+                         bool = true, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
index 19d2ec09aa51e64541d4183a6c95b3a255fdd58b..512fc138528e97107844eccf39e885c15d45ce1c 100644 (file)
@@ -1274,6 +1274,27 @@ builtin_access::strcpy_overlap ()
   return generic_overlap ();
 }
 
+/* For a BASE of array type, clamp REFOFF to at most [0, BASE_SIZE]
+   if known, or [0, MAXOBJSIZE] otherwise.  */
+
+static void
+clamp_offset (tree base, offset_int refoff[2], offset_int maxobjsize)
+{
+  if (!base || TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE)
+    return;
+
+  if (refoff[0] < 0 && refoff[1] >= 0)
+    refoff[0] = 0;
+
+  if (refoff[1] < refoff[0])
+    {
+      offset_int maxsize =  maxobjsize;
+      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (base)))
+       maxsize = wi::to_offset (size);
+
+      refoff[1] = wi::umin (refoff[1], maxsize);
+    }
+}
 
 /* Return true if DSTREF and SRCREF describe accesses that either overlap
    one another or that, in order not to overlap, would imply that the size
@@ -1312,35 +1333,12 @@ builtin_access::overlap ()
 
   /* If the base object is an array adjust the bounds of the offset
      to be non-negative and within the bounds of the array if possible.  */
-  if (dstref->base
-      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
-    {
-      if (acs.dstoff[0] < 0 && acs.dstoff[1] >= 0)
-       acs.dstoff[0] = 0;
-
-      if (acs.dstoff[1] < acs.dstoff[0])
-       {
-         if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dstref->base)))
-           acs.dstoff[1] = wi::umin (acs.dstoff[1], wi::to_offset (size));
-         else
-           acs.dstoff[1] = wi::umin (acs.dstoff[1], maxobjsize);
-       }
-    }
+  clamp_offset (dstref->base, acs.dstoff, maxobjsize);
 
   acs.srcoff[0] = srcref->offrange[0];
   acs.srcoff[1] = srcref->offrange[1];
 
-  if (srcref->base
-      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
-    {
-      if (acs.srcoff[0] < 0 && acs.srcoff[1] >= 0)
-       acs.srcoff[0] = 0;
-
-      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (srcref->base)))
-       acs.srcoff[1] = wi::umin (acs.srcoff[1], wi::to_offset (size));
-      else if (acs.srcoff[1] < acs.srcoff[0])
-       acs.srcoff[1] = wi::umin (acs.srcoff[1], maxobjsize);
-    }
+  clamp_offset (srcref->base, acs.srcoff, maxobjsize);
 
   /* When the upper bound of the offset is less than the lower bound
      the former is the result of a negative offset being represented
index 5e43405fde8340b64393d1d4248de838851da36d..f29eee29e8544381ebf803397fefafff72e4acfa 100644 (file)
@@ -269,7 +269,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s)
   CPY (dst7, s, 7);                 /* { dg-warning "specified bound 7 equals destination size" } */
   CPY (dst7, s, sizeof dst7);       /* { dg-warning "specified bound 7 equals destination size" } */
 
-  CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */
+  CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" } */
   CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */
 
   /* Verify that copies that nul-terminate are not diagnosed.  */
index 9078c6f5998093fadf715893365a080ad17b9a98..3f1c6c715ea3e3b8caccc976f9cd48bed0fe9967 100644 (file)
@@ -66,8 +66,10 @@ void strcpy_global (void)
   T (gma.a17, 17);        // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
 
   SA (__builtin_offsetof (struct MA17, ax) == 157);
-
-  T (gma.ax, 0);          // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
+  // GCC allows static initialization of flexible array members of
+  // non-local objects.  Verify that writing into one that may be
+  // initialized in another translation unit isn't diagnosed.  */
+  T (gma.ax, 0);          // { dg-bogus "\\\[-Warray-bounds" }
 }
 
 
index 5ad87401ed9969292279abd471f59ffd12699c0c..878fc811bc6269642236a5faf79f8189ab5a8042 100644 (file)
@@ -1,7 +1,7 @@
 /* PR tree-optimization/84095 - false-positive -Wrestrict warnings for
    strcpy within array
    { dg-do compile }
-   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+   { dg-options "-O2 -Wrestrict -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
 
 typedef __SIZE_TYPE__ size_t;
 
index 5a05f5cf7cf7beddee93467b4318450366a87e61..1e67b5fd928533e968e8cb1a871fa4024b1ac9bf 100644 (file)
@@ -29,15 +29,15 @@ void test_memcpy_array_cst_range_off (const void *s)
   T (d + UR (1, 2), 6);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
   T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
 
-  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " } */
   T (d + SR (-2, -1), 1);
   T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
-  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " } */
 
   d = ga7 + 7;
   T (d + SR (-7, -6), 1);
   T (d + SR (-7, -1), 1);
-  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " } */
 
   T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " } */
 }
@@ -67,15 +67,15 @@ void test_memset_array_unsigned_off (void)
   T (d + UR (1, 2), 6);       /* { dg-warning ".memset. writing 6 bytes into a region of size 5 overflows the destination" } */
   T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
 
-  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-3, -2), 1);     /* { dg-warning "writing 1 byte into a region of size 0 " } */
   T (d + SR (-2, -1), 1);
   T (d + SR (-2, -1), 2);     /* { dg-warning "writing 2 bytes into a region of size 7 " "pr89428" { xfail *-*-* } } */
-  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 9);     /* { dg-warning "writing 9 bytes into a region of size 7 " } */
 
   d = ga7 + 7;
   T (d + SR (-7, -6), 1);
   T (d + SR (-7, -1), 1);
-  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " "pr85350" { xfail *-*-* } } */
+  T (d + SR (-2, -1), 3);     /* { dg-warning "writing 3 bytes into a region of size 2 " } */
 
   T (d + UR (1, 2), 1);       /* { dg-warning "writing 1 byte into a region of size 0 " } */
 }
@@ -110,8 +110,8 @@ void test_memcpy_array_signed_off (const void *s)
 
   T (d + SR (-7, 7), 7);
   T (d + SR (-1, 1), 7);
-  T (d + SR (-1, 1), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
-  T (d + SR (-1, 2), 9);      /* { dg-warning "writing 9 bytes into a region of size " "pr89428" { xfail *-*-* } } */
+  T (d + SR (-1, 1), 9);      /* { dg-warning "writing 9 bytes into a region of size " } */
+  T (d + SR (-1, 2), 9);      /* { dg-warning "writing 9 bytes into a region of size " } */
   T (d + SR (1, 2), 1);
   T (d + SR (1, 2), 5);
 
index 8844b9f5e7107352dc233b92d66b769153ef9887..be7f51ad3a5626a6f1b0499ba2c50023e7e9ca5c 100644 (file)
@@ -224,7 +224,7 @@ void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
   q += n;      //  N=1     N=2
   q[-1] = 0;   // p[0]    p[1]
   q[-2] = 1;   // p[-1]   p[0]
-  q[-3] = 2;   // p[-2]   p[-1]   // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" { xfail *-*-* } }
+  q[-3] = 2;   // p[-2]   p[-1]   // { dg-warning "\\\[-Wstringop-overflow" "pr92939: negative offset from end" }
 
   /* The following isn't diagnosed because the warning doesn't recognize
      the index below as necessarily having the same value as the size
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
new file mode 100644 (file)
index 0000000..fd43f3a
--- /dev/null
@@ -0,0 +1,252 @@
+/* PR middle-end/95353 - spurious -Wstringop-overflow writing to a trailing
+   array plus offset
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+struct S0 { char n, a[0]; };
+
+
+void s0_nowarn_cstidx (struct S0 *p)
+{
+  char *q = p->a;
+  q[1] = __LINE__;
+  q[9] = __LINE__;
+}
+
+void s0_nowarn_cstoff_cstidx (struct S0 *p)
+{
+  char *q = p->a + 1;
+  q[1] = __LINE__;
+  q[9] = __LINE__;
+}
+
+void s0_nowarn_varoff_cstdix (struct S0 *p, int i)
+{
+  char *q = p->a + i;
+  q[1] = __LINE__;            // { dg-bogus "\\\[-Wstringop-overflow" }
+  q[9] = __LINE__;            // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void s0_nowarn_cstoff_varidx (struct S0 *p, int i)
+{
+  char *q = p->a + 1;
+  q[i] = __LINE__;
+}
+
+void s0_nowarn_varoff_varidx (struct S0 *p, int i, int j)
+{
+  char *q = p->a + i;
+  q[j] = __LINE__;            // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+
+/* Accesses past the end of a trailing array with one element is
+   discouraged but still reluctantly not diagnosed.  This should
+   change.  */
+
+struct S1 { char n, a[1]; };
+
+
+void s1_nowarn_cstidx (struct S1 *p)
+{
+  char *q = p->a;
+  q[1] = __LINE__;
+  q[9] = __LINE__;
+}
+
+void s1_nowarn_cstoff_cstidx (struct S1 *p)
+{
+  char *q = p->a + 1;
+  q[1] = __LINE__;
+  q[9] = __LINE__;
+}
+
+void s1_nowarn_varoff_cstdix (struct S1 *p, int i)
+{
+  char *q = p->a + i;
+  q[1] = __LINE__;            // { dg-bogus "\\\[-Wstringop-overflow" }
+  q[9] = __LINE__;            // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void s1_nowarn_cstoff_varidx (struct S1 *p, int i)
+{
+  char *q = p->a + 1;
+  q[i] = __LINE__;
+}
+
+void s1_nowarn_varoff_varidx (struct S1 *p, int i, int j)
+{
+  char *q = p->a + i;
+  q[j] = __LINE__;
+}
+
+
+/* Accesses past the end of a trailing array with more than one
+   element should be diagnosed but aren't yet because the MEM_REF
+   makes the out-of-bounds accesses indistinguishable from valid
+   ones to subsequent elements of the array pointed by P.  */
+
+struct S2 { char n, a[2]; };
+
+
+void s2_warn_cstidx (struct S2 *p)
+{
+  char *q = p->a;
+
+  /* The following invalid store is represented as
+       MEM[(char *)p_1(D) + 3B] = __LINE__;
+     which is indistinguishable from the valid
+       q = &p[1].n; q[0] = __LINE__;
+  */
+  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void s2_warn_cstoff_cstidx (struct S2 *p)
+{
+  char *q = p->a + 1;
+  q[1] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+}
+
+void s2_warn_varoff_cstdix (struct S2 *p, int i)
+{
+  char *q = p->a + i;
+  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+}
+
+void s2_warn_cstoff_varidx (struct S2 *p, int i)
+{
+  char *q = p->a + 1;
+  q[i] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+}
+
+void s2_warn_varoff_varidx (struct S2 *p, int i, int j)
+{
+  char *q = p->a + i;
+  q[j] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+}
+
+
+/* Verify that none of these triggers a bogus warning (not tested
+   elsewhere but triggered during bootstrap).  */
+
+void s2_nowarn_varidx_int (struct S2 *p, int i)
+{
+  extern struct S2 s2;
+  extern struct S2 s2a[];
+
+  s2.a[i - 1] = __LINE__;
+  s2.a[i] = __LINE__;
+  s2.a[i + 1] = __LINE__;
+
+  s2a[i].a[i - 1] = __LINE__;
+  s2a[i].a[i] = __LINE__;
+  s2a[i].a[i + 1] = __LINE__;
+
+  p[i].a[i - 1] = __LINE__;
+  p[i].a[i] = __LINE__;
+  p[i].a[i + 1] = __LINE__;
+
+  char *q = p->a;
+  q[i - 1] = __LINE__;
+  q[i] = __LINE__;
+  q[i + 1] = __LINE__;
+}
+
+/* Same as above but with a size_t index in range [1, SIZE_MAX].  */
+
+void* s2_nowarn_varidx_size (struct S2 *p, size_t i, size_t j)
+{
+  extern struct S2 s2;
+  extern struct S2 s2a[];
+  struct S2 *ps2 = __builtin_malloc (3 * sizeof *ps2);
+
+  s2.a[i - 1] = __LINE__;
+  s2.a[i] = __LINE__;
+  s2.a[i + 1] = __LINE__;
+
+  s2a[i].a[i - 1] = __LINE__;
+  s2a[i].a[i] = __LINE__;
+  s2a[i].a[i + 1] = __LINE__;
+
+  p[i].a[i - 1] = __LINE__;
+  p[i].a[i] = __LINE__;
+  p[i].a[i + 1] = __LINE__;
+
+  ps2->a[i - 1] = __LINE__;
+  ps2->a[i] = __LINE__;
+  ps2->a[i + 1] = __LINE__;
+
+  char *q = p->a;
+  q[i - 1] = __LINE__;
+  q[i] = __LINE__;
+  q[i + 1] = __LINE__;
+
+  if (j == 0)
+    return ps2;
+
+  s2.a[j - 1] = __LINE__;
+  s2.a[j] = __LINE__;
+  s2.a[j + 1] = __LINE__;
+
+  s2a[j].a[j - 1] = __LINE__;
+  s2a[j].a[j] = __LINE__;
+  s2a[j].a[j + 1] = __LINE__;
+
+  p[j].a[j - 1] = __LINE__;
+  p[j].a[j] = __LINE__;
+  p[j].a[j + 1] = __LINE__;
+
+  ps2->a[j - 1] = __LINE__;
+  ps2->a[j] = __LINE__;
+  ps2->a[j + 1] = __LINE__;
+
+  q = p->a;
+  q[j - 1] = __LINE__;
+  q[j] = __LINE__;
+  q[j + 1] = __LINE__;
+
+  return ps2;
+}
+
+/* Verify that accesses to an interior zero-length array are diagnosed.  */
+
+struct Si0 { char c, a[0], d; };
+
+void si0_warn_cstidx (struct Si0 *p)
+{
+  // These are indistinguishable from valid accesses to p->d:
+  //   MEM[(char *)p_1(D) + 1B] = 0;
+  char *q = p->a;
+  q[1] = __LINE__;            // { dg-warning "writing 1 byte into a region of size 0"  "pr?????" { xfail *-*-* } }
+  q[9] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_cstoff_cstidx (struct Si0 *p)
+{
+  // Like those above, these too are indistinguishable from valid accesses
+  // to p->d.
+  char *q = p->a + 1;
+  q[1] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+  q[9] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_varoff_cstdix (struct Si0 *p, int i)
+{
+  char *q = p->a + i;
+  q[1] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" }
+  q[9] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+void si0_warn_cstoff_varidx (struct Si0 *p, int i)
+{
+  char *q = p->a + 1;
+  q[i] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* } }
+}
+
+void si0_warn_varoff_varidx (struct Si0 *p, int i, int j)
+{
+  char *q = p->a + i;
+  q[j] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-35.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-35.c
new file mode 100644 (file)
index 0000000..612137e
--- /dev/null
@@ -0,0 +1,76 @@
+/* Verify that calls to strcpy to write to an element of an array of pointers
+   are not diagnosed (due to mistakenly using the size of the array as that
+   of the destination).
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef char A1[1];
+typedef char A2[2];
+typedef char A3[3];
+typedef char A4[4];
+typedef char A5[5];
+typedef char A6[6];
+typedef char A7[7];
+typedef char A8[8];
+typedef char A9[9];
+typedef char A10[10];
+
+A1* pa1[3];
+A2* pa2[3];
+A3* pa3[3];
+A4* pa4[3];
+A5* pa5[3];
+A6* pa6[3];
+A7* pa7[3];
+A8* pa8[3];
+A9* pa9[3];
+A10* pa10[3];
+
+void nowarn_a1_1 (int i)
+{
+  __builtin_strcpy (*pa1[0], "");
+  __builtin_strcpy (*pa1[1], "");
+  __builtin_strcpy (*pa1[i], "");
+}
+
+void nowarn_a2_2 (int i)
+{
+  __builtin_strcpy (*pa2[0], "1");
+  __builtin_strcpy (*pa2[1], "2");
+  __builtin_strcpy (*pa2[i], "3");
+}
+
+void nowarn_a3_3 (int i)
+{
+  __builtin_strcpy (*pa3[0], "12");
+  __builtin_strcpy (*pa3[1], "23");
+  __builtin_strcpy (*pa3[i], "34");
+}
+
+void nowarn_a4_4 (int i)
+{
+  __builtin_strcpy (*pa4[0], "123");
+  __builtin_strcpy (*pa4[1], "234");
+  __builtin_strcpy (*pa4[i], "345");
+}
+
+void nowarn_a5_5 (int i)
+{
+  __builtin_strcpy (*pa5[0], "1234");
+  __builtin_strcpy (*pa5[1], "2345");
+  __builtin_strcpy (*pa5[i], "3456");
+}
+
+void nowarn_a6_6 (int i)
+{
+  __builtin_strcpy (*pa6[0], "12345");
+  __builtin_strcpy (*pa6[1], "23456");
+  __builtin_strcpy (*pa6[1], "34567");
+}
+
+void nowarn_a10_10 (int i)
+{
+  __builtin_strcpy (*pa10[0], "0123456789");
+  __builtin_strcpy (*pa10[1], "1234567890");
+  __builtin_strcpy (*pa10[i], "2345678909");
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-36.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-36.c
new file mode 100644 (file)
index 0000000..3f0874d
--- /dev/null
@@ -0,0 +1,24 @@
+/* Verify that casts between pointers and integers don't trigger false
+   positives.  Test derived from Glibc's _dl_allocate_tls_storage() in
+   dl-tls.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+size_t a;
+size_t s;
+
+void* _dl_allocate_tls_storage (void)
+{
+  void *p = __builtin_malloc (s + a + sizeof (void *));
+
+  char *q = (char *)(__builtin_constant_p (a) && (((a - 1) & a) == 0)
+             ? ((((uintptr_t)p) + a - 1) & ~(a - 1))
+             : (((((uintptr_t)p) + (a - 1)) / a) * a));
+
+  char *r = q + s - sizeof (int[4]);
+  __builtin_memset (r, '\0', sizeof (int[4]));
+  return r;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
new file mode 100644 (file)
index 0000000..339f904
--- /dev/null
@@ -0,0 +1,229 @@
+/* Verify that -Wstringop-overflow detects writing past the end of each
+   individual element of a multidimensional array.
+  { dg-do compile }
+  { dg-options "-O2 -Wall -Wno-array-bounds -Wno-stringop-truncation" } */
+
+#define CONCAT(x, y)    x ## y
+#define CAT(name, line) CONCAT (name, line)
+#define UNIQ_NAME(name) CAT (name, __LINE__)
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* malloc (size_t);
+extern char* strncpy (char*, const char*, size_t);
+
+extern char a2_2_8[2][2][8];
+
+void nowarn_a2_2_8 (const char *s)
+{
+  // The following trigger -Wstringop-truncation.
+  strncpy (a2_2_8[0][0], s, 8);
+  strncpy (a2_2_8[0][1], s, 8);
+  strncpy (a2_2_8[1][0], s, 8);
+  strncpy (a2_2_8[1][1], s, 8);
+}
+
+void warn_a2_2_8 (const char *s)
+{
+  strncpy (a2_2_8[0][0], s, 9);         // { dg-warning "writing 9 bytes into a region of size 8 " }
+  strncpy (a2_2_8[0][1], s, 9);         // { dg-warning "writing 9 bytes into a region of size 8 " }
+  strncpy (a2_2_8[1][0], s, 9);         // { dg-warning "writing 9 bytes into a region of size 8 " }
+  strncpy (a2_2_8[1][1], s, 9);         // { dg-warning "writing 9 bytes into a region of size 8 " }
+}
+
+
+extern char a2_3_5[2][3][5];
+
+void nowarn_a2_3_5 (const char *s)
+{
+  // The following trigger -Wstringop-truncation.
+  strncpy (a2_3_5[0][0], s, 5);
+  strncpy (a2_3_5[0][1], s, 5);
+  strncpy (a2_3_5[0][2], s, 5);
+  strncpy (a2_3_5[1][0], s, 5);
+  strncpy (a2_3_5[1][1], s, 5);
+  strncpy (a2_3_5[1][2], s, 5);
+}
+
+void warn_a2_3_5 (const char *s)
+{
+  strncpy (a2_3_5[0][0], s, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+  strncpy (a2_3_5[0][1], s, 7);         // { dg-warning "writing 7 bytes into a region of size 5 " }
+  strncpy (a2_3_5[1][0], s, 8);         // { dg-warning "writing 8 bytes into a region of size 5 " }
+  strncpy (a2_3_5[1][1], s, 9);         // { dg-warning "writing 9 bytes into a region of size 5 " }
+}
+
+
+void* nowarn_malloc_3_5 (const char *s, unsigned n)
+{
+  if (n < 3 || 5 < n)
+    n = 3;
+  char *p = (char*)malloc (n);
+  strncpy (p + 1, s, 4);
+  return p;
+}
+
+void* warn_malloc_3_5 (const char *s, unsigned n)
+{
+  if (n < 3 || 5 < n)
+    n = 3;
+  char *p = (char*)malloc (n);          // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" }
+  // The size below should be a range like the one above.
+  strncpy (p + 1, s, 5);                // { dg-warning "writing 5 bytes into a region of size 4 " }
+  return p;
+}
+
+
+typedef __attribute__ ((alloc_size (1, 2))) void* UsrAlloc (int, int);
+
+void* nowarn_use_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
+{
+  if (n < 3 || 5 < n)
+    n = 3;
+  char *p = (char*)usr_alloc (n, 3);
+  strncpy (p + 1, s, 14);
+  return p;
+}
+
+void* warn_usr_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
+{
+  if (n < 3 || 5 < n)
+    n = 3;
+  char *p = (char*)usr_alloc (n, 3);    // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" }
+  // The size below should be a range like the one above.
+  strncpy (p + 1, s, 15);               // { dg-warning "writing 15 bytes into a region of size 14 " }
+  return p;
+}
+
+struct S
+{
+  char a2_3_4[2][3][4];
+  char a3_4_5[3][4][5];
+};
+
+extern struct S sa[];
+
+void nowarn_sa_cstidx_cstsize (const char* const s[])
+{
+  strncpy (sa[0].a2_3_4[0][0], s[0], 4);
+  strncpy (sa[0].a2_3_4[0][1], s[1], 4);
+  strncpy (sa[0].a2_3_4[0][2], s[2], 4);
+
+  strncpy (sa[0].a2_3_4[1][0], s[3], 4);
+  strncpy (sa[0].a2_3_4[1][1], s[4], 4);
+  strncpy (sa[0].a2_3_4[1][2], s[5], 4);
+
+  strncpy (sa[1].a2_3_4[0][0], s[6], 4);
+  strncpy (sa[1].a2_3_4[0][1], s[7], 4);
+  strncpy (sa[1].a2_3_4[0][2], s[8], 4);
+
+  strncpy (sa[1].a2_3_4[1][0], s[9], 4);
+  strncpy (sa[1].a2_3_4[1][1], s[10], 4);
+  strncpy (sa[1].a2_3_4[1][2], s[11], 4);
+}
+
+void warn_sa_cstidx_cstsize (const char* const s[])
+{
+  strncpy (sa[0].a2_3_4[0][0], s[0], 5);    // { dg-warning "writing 5 bytes into a region of size 4 " }
+  strncpy (sa[0].a2_3_4[0][1], s[1], 6);    // { dg-warning "writing 6 bytes into a region of size 4 " }
+  strncpy (sa[0].a2_3_4[0][2], s[2], 7);    // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+  strncpy (sa[0].a2_3_4[1][0], s[3], 5);    // { dg-warning "writing 5 bytes into a region of size 4 " }
+  strncpy (sa[0].a2_3_4[1][1], s[4], 6);    // { dg-warning "writing 6 bytes into a region of size 4 " }
+  strncpy (sa[0].a2_3_4[1][2], s[5], 7);    // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+  strncpy (sa[1].a2_3_4[0][0], s[6], 5);    // { dg-warning "writing 5 bytes into a region of size 4 " }
+  strncpy (sa[1].a2_3_4[0][1], s[7], 6);    // { dg-warning "writing 6 bytes into a region of size 4 " }
+  strncpy (sa[1].a2_3_4[0][2], s[8], 7);    // { dg-warning "writing 7 bytes into a region of size 4 " }
+
+  strncpy (sa[1].a2_3_4[1][0], s[9], 5);    // { dg-warning "writing 5 bytes into a region of size 4 " }
+  strncpy (sa[1].a2_3_4[1][1], s[10], 6);   // { dg-warning "writing 6 bytes into a region of size 4 " }
+  strncpy (sa[1].a2_3_4[1][2], s[11], 7);   // { dg-warning "writing 7 bytes into a region of size 4 " }
+}
+
+void nowarn_sa_cstidx_varsize (const char* const s[], unsigned n)
+{
+  strncpy (sa[0].a2_3_4[0][0], s[0], n);
+  strncpy (sa[0].a2_3_4[0][1], s[1], n);
+  strncpy (sa[0].a2_3_4[0][2], s[2], n);
+
+  strncpy (sa[0].a2_3_4[1][0], s[3], n);
+  strncpy (sa[0].a2_3_4[1][1], s[4], n);
+  strncpy (sa[0].a2_3_4[1][2], s[5], n);
+
+  strncpy (sa[1].a2_3_4[0][0], s[6], n);
+  strncpy (sa[1].a2_3_4[0][1], s[7], n);
+  strncpy (sa[1].a2_3_4[0][2], s[8], n);
+
+  strncpy (sa[1].a2_3_4[1][0], s[9], n);
+  strncpy (sa[1].a2_3_4[1][1], s[10], n);
+  strncpy (sa[1].a2_3_4[1][2], s[11], n);
+}
+
+void nowarn_sa_loop (const char* const s[], unsigned n)
+{
+  for (unsigned i0 = 0; i0 != 5; ++i0)
+    for (unsigned i1 = 0; i1 != 3; ++i1)
+      for (unsigned i2 = 0; i2 != 2; ++i2)
+       strncpy (sa[i0].a2_3_4[i1][i2], s[i2], n);
+}
+
+
+/* Verify that a note after the warning points to the accessed object
+   and mentions the starting offset of the access.  Another alternative
+   might be for the offset to be the starting offset of the overflow.
+   As it is, it's not clear to which of the two the offset refers.  */
+
+void test_note (const char *s)
+{
+  extern void sink (void*);
+
+  {
+    char a[1][1][2];                    // { dg-message "destination object" }
+    strncpy (a[0][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
+    sink (a);
+  }
+
+  {
+    char a[1][2][2];                    // { dg-message "at offset 2 into " }
+    strncpy (a[0][1], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
+    sink (a);
+  }
+
+  {
+    char a[1][2][2];                    // { dg-message "at offset 4 into " }
+    strncpy (a[1][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
+    sink (a);
+  }
+
+  {
+    char a[2][1][2];                    // { dg-message "at offset 2 into " }
+    strncpy (a[1][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
+    sink (a);
+  }
+
+  {
+    char a[2][2][3];                    // { dg-message "at offset 9 into " }
+    strncpy (a[1][1], s, 4);            // { dg-warning "writing 4 bytes into a region of size 3 " }
+    sink (a);
+  }
+
+  {
+    char a[2][3][3];                    // { dg-message "at offset 12 into " }
+    strncpy (a[1][1], s, 5);            // { dg-warning "writing 5 bytes into a region of size 3 " }
+    sink (a);
+  }
+
+  {
+    char a[2][3][3];                    // { dg-message "at offset 12 into " }
+    strncpy (a[1][1], s, 6);            // { dg-warning "writing 6 bytes into a region of size 3 " }
+    sink (a);
+  }
+
+  {
+    char a[2][3][3];                    // { dg-message "at offset 15 into " }
+    strncpy (a[1][2], s, 7);            // { dg-warning "writing 7 bytes into a region of size 3 " }
+    sink (a);
+  }
+
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-38.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-38.c
new file mode 100644 (file)
index 0000000..12bda2b
--- /dev/null
@@ -0,0 +1,94 @@
+/* Verify that non-constant offsets don't suppress warnings in cases
+   when the size of the access alone makes it invalid, regardless of
+   the offset value.
+   Also verify that writing into unknown objects through pointers
+   (or pointer members) doesn't trigger warnings.
+   { dg-do compile }
+   { dg-options "-O1 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+
+
+struct Xp { char *p; } xp;
+struct Xa { char *a[2]; } xa;
+
+void nowarn_copy_read_pointed_obj_plus_cst (void *d)
+{
+  int i = 0;
+  memcpy (d, xp.p + i, 9);
+  memcpy (d, xa.a[i], 9);
+}
+
+void nowarn_copy_read_pointed_obj_plus_var (void *d, int i)
+{
+  memcpy (d, xp.p + i, 9);
+  memcpy (d, xa.a[i], 9);
+}
+
+
+void warn_copy_read_pointer_plus_cst (void *d)
+{
+  int i = 0;
+  memcpy (d, &xp.p + i, 9);   // { dg-warning "reading 9 bytes from a region of size . " }
+}
+
+void warn_copy_read_pointer_plus_var (void *d, int i)
+{
+  memcpy (d, &xp.p + i, 9);   // { dg-warning "reading 9 bytes from a region of size . " }
+}
+
+
+void nowarn_copy_write_pointed_obj_plus_cst (const void *s)
+{
+  int i = 0;
+  memcpy (xp.p + i, s, 9);
+  memcpy (xa.a[i], s, 9);
+}
+
+void nowarn_copy_write_pointed_obj_plus_var (const void *s, int i)
+{
+  memcpy (xp.p + i, s, 9);
+  memcpy (xa.a[i], s, 9);
+}
+
+
+void warn_copy_write_pointer_plus_cst (const void *s)
+{
+  int i = 0;
+  memcpy (&xp.p + i, s, 9);   // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+void warn_copy_write_pointer_plus_var (const void *s, int i)
+{
+  memcpy (&xp.p + i, s, 9);   // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+
+void nowarn_set_pointed_obj_plus_cst (void)
+{
+  int i = 0;
+  memset (xp.p + i, 0, 9);
+  memset (xa.a[i], 0, 9);
+}
+
+void nowarn_set_pointed_obj_plus_var (int i)
+{
+  memset (xp.p + i, 0, 9);
+  memset (xa.a[i], 0, 9);
+}
+
+
+void warn_set_pointer_plus_cst (void)
+{
+  int i = 0;
+  memset (&xp.p + i, 0, 9);   // { dg-warning "writing 9 bytes into a region of size . " }
+}
+
+void warn_set_pointer_plus_var (int i)
+{
+  memset (&xp.p + i, 0, 9);   // { dg-warning "writing 9 bytes into a region of size . " }
+  memset (&xa.a[i], 0, 17);   // { dg-warning "writing 17 bytes into a region of size \[0-9\]+ " }
+}
index 6e836c5d4d3f3a110b309d6eca73ee807ddc59a6..2409e597aacc5e08e70811910b3f636a188092d7 100644 (file)
@@ -99,7 +99,7 @@ void test_memcpy_range (void *d, const void *s)
 
   memcpy (buf + 5, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" } */
 
-  memcpy (buf + size_max, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" { xfail *-*-* } } */
+  memcpy (buf + size_max, s, UR (1, 2));  /* { dg-warning "writing between 1 and 2 bytes into a region of size 0 overflows the destination" "excessive pointer offset" } */
 
   memcpy (buf, s, UR (ssize_max, size_max));   /* { dg-warning "writing \[0-9\]+ or more bytes into a region of size 5 overflows the destination" } */
   memcpy (buf, s, UR (ssize_max + 1, size_max));  /* { dg-warning "specified \(bound|size\) between \[0-9\]+ and \[0-9\]+ exceeds maximum object size" } */
index 87dd6ac4e89c5812c847c4cf1098d0616976e7cd..5398e9f1f9c41975c35f96292ab396b5eef8ce9c 100644 (file)
@@ -1,6 +1,6 @@
 /* Test exercising -Wstringop-overflow warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wstringop-overflow=1" } */
+/* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */
 
 #define offsetof(type, mem)   __builtin_offsetof (type, mem)
 
@@ -120,7 +120,7 @@ void test_memop_warn_alloc (const void *src)
 
   /* Verify the same as above but by writing into the first mmeber
      of the first element of the array.  */
-  memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+  memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size " "memcpy into allocated" } */
   escape (a, src);
 
   n = range (12, 32);
@@ -133,33 +133,33 @@ void test_memop_warn_alloc (const void *src)
   /* The following idiom of clearing multiple members of a struct is
      used in a few places in the Linux kernel.  Verify that a warning
      is issued for it when it writes past the end of the array object.  */
-  memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b->c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b->c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
   /* Same as above but clearing just elements of the second element
      of the array.  */
-  memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size " "memcpy into allocated" } */
   escape (b);
 
-  memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
+  memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" } */
   escape (b);
 }
 
index 741c1f88eaa54763926a119b396ecec99df12f83..12d2491b9f5b53e46a34bd5a700d297df7f8d616 100644 (file)
@@ -106,7 +106,7 @@ void test_memop_warn_alloc (void *p)
 
   memcpy (p, &a[0], n);   /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */
 
-  memcpy (p, &a[0].a, n);   /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" { xfail *-*-*} } */
+  memcpy (p, &a[0].a, n);   /* { dg-warning "reading between 8 and 32 bytes from a region of size " "memcpy from allocated" } */
 
   n = range (12, 32);
 
index 0a9ac6c1da201326ba6ebdeb0848fdd14a351969..19e9a60858383a15922a57eb46ab6efdb6743884 100644 (file)
@@ -75,7 +75,7 @@ VERIFY (i0 ? (a8 + 2) : (b8 + 2), 7, 6);
 VERIFY (i0 ? (a8 + 0) : (c4 + 0), 9, 4);
 VERIFY (i0 ? (a8 + 0) : (c4 + 1), 9, 3);
 VERIFY (i0 ? (a8 + 0) : (c4 + 3), 9, 1);
-VERIFY (i0 ? (a8 + 0) : (c4 + 4), 9, 0);
+VERIFY (i0 ? (a8 + 0) : (c4 + 4), 8, 0);
 VERIFY (i0 ? (a8 + 1) : (c4 + 0), 8, 4);
 VERIFY (i0 ? (a8 + 1) : (c4 + 1), 8, 3);
 VERIFY (i0 ? (a8 + 1) : (c4 + 2), 8, 2);
index 8855065f5774a328e156477b18e7a701ca7f20b5..bc55b27cff08ea3b5bccbb5b0e500762e76f2695 100644 (file)
@@ -172,7 +172,7 @@ compute_object_offset (const_tree expr, const_tree var)
    is true, else null.  An object's initializer affects the object's
    size if it's a struct type with a flexible array member.  */
 
-static tree
+tree
 decl_init_size (tree decl, bool min)
 {
   tree size = DECL_SIZE_UNIT (decl);
@@ -259,6 +259,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
          offset_int mem_offset;
          if (mem_ref_offset (pt_var).is_constant (&mem_offset))
            {
+             if (*poff)
+               *poff = wide_int_to_tree (ptrdiff_type_node,
+                                         mem_offset + wi::to_offset (*poff));
+             else
+               *poff = wide_int_to_tree (ptrdiff_type_node, mem_offset);
              offset_int dsz = wi::sub (sz, mem_offset);
              if (wi::neg_p (dsz))
                sz = 0;
@@ -413,12 +418,12 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
        {
+         *poff = bytes;
          if (TREE_CODE (bytes) == INTEGER_CST
              && tree_int_cst_lt (var_size, bytes))
            bytes = size_zero_node;
          else
            bytes = size_binop (MINUS_EXPR, var_size, bytes);
-         *poff = bytes;
        }
       if (var != pt_var
          && pt_var_size
@@ -441,7 +446,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   else if (!pt_var_size)
     return false;
   else
-    bytes = pt_var_size;
+    {
+      bytes = pt_var_size;
+      if (!*poff)
+       *poff = size_zero_node;
+    }
 
   if (tree_fits_uhwi_p (bytes))
     {
index 871dcdba06ab99d75431764148433f5c49e74b68..e677c973ab960edd30dcfb3a7ba3dff6237560a8 100644 (file)
@@ -24,5 +24,6 @@ extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
 extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *,
                                         tree * = NULL, tree * = NULL);
+extern tree decl_init_size (tree, bool);
 
 #endif  // GCC_TREE_OBJECT_SIZE_H
index 93d095e18961e9c7f377a3e95ddbf0e66dea50a5..fbaee745f7d13a521f00a8dcac7c2628b55658de 100644 (file)
@@ -5589,7 +5589,6 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
              set_strinfo (idx, si);
              si->writable = true;
              si->dont_invalidate = true;
-             maybe_warn_overflow (stmt, lenrange[2], rvals);
            }
        }
     }