PR middle-end/91582 - missing heap overflow detection for strcpy
authorMartin Sebor <msebor@redhat.com>
Sat, 14 Dec 2019 00:52:46 +0000 (00:52 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Sat, 14 Dec 2019 00:52:46 +0000 (17:52 -0700)
PR middle-end/91582 - missing heap overflow detection for strcpy
PR middle-end/92868 - ICE: tree check: expected integer_cst, have ssa_name

gcc/ChangeLog:

PR middle-end/91582
PR middle-end/92868
* builtins.c (addr_decl_size): New function.
(gimple_call_alloc_size): Add arguments.
(compute_objsize): Add an argument.  Set *PDECL even for allocated
objects.
Correct checking for negative wide_int.
Correct handling of negative outer offsets into unknown regions
or with unknown inner offsets.
Extend offsets to at most sizetype precision.
Only handle constant subobject sizes.
* builtins.h (gimple_call_alloc_size): Add arguments.
* tree.c (component_ref_size): Always return sizetype.
* tree-ssa-strlen.c (strinfo::alloc): New member.
(get_addr_stridx): Add argument.
(get_stridx): Use ptrdiff_t.  Add argument.
(new_strinfo): Set new member.
(get_string_length): Handle alloca and VLA.
(dump_strlen_info): Dump more state.
(maybe_invalidate): Print more info.  Decrease indentation.
(unshare_strinfo): Set new member.
(valid_builtin_call): Handle alloca and VLA.
(maybe_warn_overflow): Check and set no-warning bit.  Improve
handling of offsets.  Print allocated objects.
(handle_builtin_strlen): Handle strinfo records with null lengths.
(handle_builtin_strcpy): Add argument.  Call maybe_warn_overflow.
(is_strlen_related_p): Handle dynamically allocated objects.
(get_range): Add argument.
(handle_builtin_malloc): Rename...
(handle_alloc): ...to this and handle all allocation functions.
(handle_builtin_memset): Call maybe_warn_overflow.
(count_nonzero_bytes): Handle more MEM_REF forms.
(strlen_check_and_optimize_call): Call handle_alloc_call.  Pass
arguments to more callees.
(handle_integral_assign): Add argument.  Create strinfo entries
for MEM_REF assignments.
(check_and_optimize_stmt): Handle more MEM_REF forms.

gcc/testsuite/ChangeLog:

PR middle-end/91582
* c-c++-common/Wrestrict.c: Adjust expected warnings.
* gcc/testsuite/c-c++-common/Wstringop-truncation-4.c: Enable more
warnings.
* gcc/testsuite/c-c++-common/Wstringop-truncation.c: Remove an xfail.
* gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
* gcc.dg/Warray-bounds-47.c: Same.
* gcc.dg/Warray-bounds-52.c: New test.
* gcc.dg/Wstringop-overflow-27.c: New test.
* gcc.dg/Wstringop-overflow-28.c: New test.
* gcc.dg/Wstringop-overflow-29.c: New test.
* gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
* gcc.dg/attr-copy-2.c: Adjust expected warnings.
* gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
* gcc.dg/strlenopt-86.c: Relax test.
* gcc.target/i386/pr82002-1.c: Prune expected warnings.

From-SVN: r279392

22 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/builtins.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wrestrict.c
gcc/testsuite/c-c++-common/Wstringop-truncation-4.c
gcc/testsuite/c-c++-common/Wstringop-truncation.c
gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
gcc/testsuite/gcc.dg/Warray-bounds-46.c
gcc/testsuite/gcc.dg/Warray-bounds-47.c
gcc/testsuite/gcc.dg/Warray-bounds-52.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-27.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-28.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-29.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-alloc_size.c
gcc/testsuite/gcc.dg/attr-copy-2.c
gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
gcc/testsuite/gcc.dg/strlenopt-86.c
gcc/testsuite/gcc.target/i386/pr82002-1.c
gcc/tree-ssa-strlen.c
gcc/tree-ssa-strlen.h
gcc/tree.c

index cc14a0469c4103d2a25221534b46251587903461..5073ffaf3b9d1fa1fc1259757c936c9c61889e34 100644 (file)
@@ -1,3 +1,43 @@
+2019-12-13  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91582
+       PR middle-end/92868
+       * builtins.c (addr_decl_size): New function.
+       (gimple_call_alloc_size): Add arguments.
+       (compute_objsize): Add an argument.  Set *PDECL even for allocated
+       objects.
+       Correct checking for negative wide_int.
+       Correct handling of negative outer offsets into unknown regions
+       or with unknown inner offsets.
+       Extend offsets to at most sizetype precision.
+       Only handle constant subobject sizes.
+       * builtins.h (gimple_call_alloc_size): Add arguments.
+       * tree.c (component_ref_size): Always return sizetype.
+       * tree-ssa-strlen.c (strinfo::alloc): New member.
+       (get_addr_stridx): Add argument.
+       (get_stridx): Use ptrdiff_t.  Add argument.
+       (new_strinfo): Set new member.
+       (get_string_length): Handle alloca and VLA.
+       (dump_strlen_info): Dump more state.
+       (maybe_invalidate): Print more info.  Decrease indentation.
+       (unshare_strinfo): Set new member.
+       (valid_builtin_call): Handle alloca and VLA.
+       (maybe_warn_overflow): Check and set no-warning bit.  Improve
+       handling of offsets.  Print allocated objects.
+       (handle_builtin_strlen): Handle strinfo records with null lengths.
+       (handle_builtin_strcpy): Add argument.  Call maybe_warn_overflow.
+       (is_strlen_related_p): Handle dynamically allocated objects.
+       (get_range): Add argument.
+       (handle_builtin_malloc): Rename...
+       (handle_alloc): ...to this and handle all allocation functions.
+       (handle_builtin_memset): Call maybe_warn_overflow.
+       (count_nonzero_bytes): Handle more MEM_REF forms.
+       (strlen_check_and_optimize_call): Call handle_alloc_call.  Pass
+       arguments to more callees.
+       (handle_integral_assign): Add argument.  Create strinfo entries
+       for MEM_REF assignments.
+       (check_and_optimize_stmt): Handle more MEM_REF forms.
+
 2019-12-13  Iain Sandoe  <iain@sandoe.co.uk>
 
        * config/rs6000/darwin.h (DARWIN_DYLIB1_SPEC): New.
index 4c08214ba2933b20e209728706aacbc9d47e59b5..3e89f2a7f26ffa7318848ad5b6a46b79a5445144 100644 (file)
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "calls.h"
 #include "varasm.h"
 #include "tree-object-size.h"
+#include "tree-ssa-strlen.h"
 #include "realmpfr.h"
 #include "cfgrtl.h"
 #include "except.h"
@@ -3696,11 +3697,13 @@ check_access (tree exp, tree, tree, tree dstwrite,
   return true;
 }
 
-/* If STMT is a call to an allocation function, returns the size
-   of the object allocated by the call.  */
+/* If STMT is a call to an allocation function, returns the constant
+   size of the object allocated by the call represented as sizetype.
+   If nonnull, sets RNG1[] to the range of the size.  */
 
 tree
-gimple_call_alloc_size (gimple *stmt)
+gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
+                       const vr_values *rvals /* = NULL */)
 {
   if (!stmt)
     return NULL_TREE;
@@ -3747,11 +3750,12 @@ gimple_call_alloc_size (gimple *stmt)
 
   tree size = gimple_call_arg (stmt, argidx1);
 
-  wide_int rng1[2];
-  if (TREE_CODE (size) == INTEGER_CST)
-    rng1[0] = rng1[1] = wi::to_wide (size);
-  else if (TREE_CODE (size) != SSA_NAME
-          || get_range_info (size, rng1, rng1 + 1) != VR_RANGE)
+  wide_int rng1_buf[2];
+  /* If RNG1 is not set, use the buffer.  */
+  if (!rng1)
+    rng1 = rng1_buf;
+
+  if (!get_range (size, rng1, rvals))
     return NULL_TREE;
 
   if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
@@ -3761,20 +3765,18 @@ gimple_call_alloc_size (gimple *stmt)
      of the upper bounds as a constant.  Ignore anti-ranges.  */
   tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
   wide_int rng2[2];
-  if (TREE_CODE (n) == INTEGER_CST)
-    rng2[0] = rng2[1] = wi::to_wide (n);
-  else if (TREE_CODE (n) != SSA_NAME
-          || get_range_info (n, rng2, rng2 + 1) != VR_RANGE)
+  if (!get_range (n, rng2, rvals))
     return NULL_TREE;
 
-  /* Extend to the maximum precsion to avoid overflow.  */
+  /* Extend to the maximum precision to avoid overflow.  */
   const int prec = ADDR_MAX_PRECISION;
   rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
   rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
   rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
   rng2[1] = wide_int::from (rng2[1], prec, UNSIGNED);
 
-  /* Return the lesser of SIZE_MAX and the product of the upper bounds.  */
+  /* Compute products of both bounds for the caller but return the lesser
+     of SIZE_MAX and the product of the upper bounds as a constant.  */
   rng1[0] = rng1[0] * rng2[0];
   rng1[1] = rng1[1] * rng2[1];
   tree size_max = TYPE_MAX_VALUE (sizetype);
@@ -3787,36 +3789,76 @@ gimple_call_alloc_size (gimple *stmt)
   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.  */
+
+static tree
+addr_decl_size (tree dest, tree *pdecl, tree *poff)
+{
+  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;
+    }
+
+  return NULL_TREE;
+}
+
 /* Helper to compute the size of the object referenced by the DEST
    expression which must have pointer type, using Object Size type
-   OSTYPE (only the least significant 2 bits are used).  Return
-   an estimate of the size of the object 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, set *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.
+   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.
+
    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 */)
+                tree *poff /* = NULL */, const vr_values *rvals /* = NULL */)
 {
   tree dummy_decl = NULL_TREE;
   if (!pdecl)
     pdecl = &dummy_decl;
 
-  tree dummy_off = size_zero_node;
+  tree dummy_off = NULL_TREE;
   if (!poff)
     poff = &dummy_off;
 
-  unsigned HOST_WIDE_INT size;
-
   /* Only the two least significant bits are meaningful.  */
   ostype &= 3;
 
+  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;
+
+  unsigned HOST_WIDE_INT size;
   if (compute_builtin_object_size (dest, ostype, &size, pdecl, poff))
     return build_int_cst (sizetype, size);
 
@@ -3826,8 +3868,15 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
       if (is_gimple_call (stmt))
        {
          /* If STMT is a call to an allocation function get the size
-            from its argument(s).  */
-         return gimple_call_alloc_size (stmt);
+            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;
        }
 
       if (!is_gimple_assign (stmt))
@@ -3853,17 +3902,21 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
                  /* Ignore negative offsets for now.  For others,
                     use the lower bound as the most optimistic
                     estimate of the (remaining) size.  */
-                 if (wi::sign_mask (wioff))
+                 if (wi::neg_p (wioff))
                    ;
-                 else if (wi::ltu_p (wioff, wisiz))
-                   {
-                     *poff = size_binop (PLUS_EXPR, *poff, off);
-                     return wide_int_to_tree (TREE_TYPE (size),
-                                              wi::sub (wisiz, wioff));
-                   }
                  else
                    {
-                     *poff = size_binop (PLUS_EXPR, *poff, off);
+                     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;
                    }
                }
@@ -3875,32 +3928,29 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
              enum value_range_kind rng = get_range_info (off, &min, &max);
 
              if (rng == VR_RANGE)
-               {
-                 if (tree size = compute_objsize (dest, ostype, pdecl))
-                   {
-                     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::sign_mask (min)
-                         || wi::sign_mask (max))
-                       ;
-                     else if (wi::ltu_p (min, wisiz))
-                       {
-                         *poff = size_binop (PLUS_EXPR, *poff,
-                                             wide_int_to_tree (sizetype, min));
+               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));
-                       }
-                     else
-                       {
-                         *poff = size_binop (PLUS_EXPR, *poff,
-                                             wide_int_to_tree (sizetype, min));
-                         return size_zero_node;
-                       }
-                   }
-               }
+                       return size_zero_node;
+                     }
+                 }
            }
        }
       else if (code != ADDR_EXPR)
@@ -3926,10 +3976,25 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
              && *poff && integer_zerop (*poff))
            return size_zero_node;
 
-         /* A valid offset into a declared object cannot be negative.  */
-         if (tree_int_cst_sgn (*poff) < 0)
+         /* 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);
+
          /* Adjust SIZE either up or down by the sum of *POFF and OFF
             above.  */
          if (TREE_CODE (dest) == ARRAY_REF)
@@ -3938,29 +4003,35 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
              tree eltype = TREE_TYPE (dest);
              tree tpsize = TYPE_SIZE_UNIT (eltype);
              if (tpsize && TREE_CODE (tpsize) == INTEGER_CST)
-               off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize);
+               {
+                 wide_int wsz = wi::to_wide (tpsize, offrng->get_precision ());
+                 offrng[0] *= wsz;
+                 offrng[1] *= wsz;
+               }
              else
                return NULL_TREE;
            }
 
-         wide_int offrng[2];
-         if (TREE_CODE (off) == INTEGER_CST)
-           offrng[0] = offrng[1] = wi::to_wide (off);
-         else if (TREE_CODE (off) == SSA_NAME)
+         wide_int wisize = wi::to_wide (size);
+
+         if (!*poff)
            {
-             wide_int min, max;
-             enum value_range_kind rng
-               = get_range_info (off, offrng, offrng + 1);
-             if (rng != VR_RANGE)
-               return NULL_TREE;
+             /* 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;
            }
-         else
-           return NULL_TREE;
 
          /* Convert to the same precision to keep wide_int from "helpfuly"
             crashing whenever it sees other argumments.  */
-         offrng[0] = wide_int::from (offrng[0], ADDR_MAX_BITSIZE, SIGNED);
-         offrng[1] = wide_int::from (offrng[1], ADDR_MAX_BITSIZE, SIGNED);
+         offrng[0] = wide_int::from (offrng[0], sizprec, SIGNED);
+         offrng[1] = wide_int::from (offrng[1], sizprec, SIGNED);
 
          tree dstoff = *poff;
          if (integer_zerop (*poff))
@@ -3972,14 +4043,14 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
              *poff = size_binop (PLUS_EXPR, *poff, off);
            }
 
-         if (wi::sign_mask (offrng[0]) >= 0)
+         if (!wi::neg_p (offrng[0]))
            {
              if (TREE_CODE (size) != INTEGER_CST)
                return NULL_TREE;
 
              /* Return the difference between the size and the offset
                 or zero if the offset is greater.  */
-             wide_int wisize = wi::to_wide (size, ADDR_MAX_BITSIZE);
+             wide_int wisize = wi::to_wide (size, sizprec);
              if (wi::ltu_p (wisize, offrng[0]))
                return size_zero_node;
 
@@ -3999,39 +4070,25 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
          else
            return NULL_TREE;
 
-         dstoffrng[0] = wide_int::from (dstoffrng[0], ADDR_MAX_BITSIZE, SIGNED);
-         dstoffrng[1] = wide_int::from (dstoffrng[1], ADDR_MAX_BITSIZE, SIGNED);
+         dstoffrng[0] = wide_int::from (dstoffrng[0], sizprec, SIGNED);
+         dstoffrng[1] = wide_int::from (dstoffrng[1], sizprec, SIGNED);
 
-         wide_int declsize = wi::to_wide (size);
-         if (wi::sign_mask (dstoffrng[0]) > 0)
-           declsize += dstoffrng[0];
+         if (!wi::neg_p (dstoffrng[0]))
+           wisize += dstoffrng[0];
 
          offrng[1] += dstoffrng[1];
-         if (wi::sign_mask (offrng[1]) < 0)
+         if (wi::neg_p (offrng[1]))
            return size_zero_node;
 
-         return wide_int_to_tree (sizetype, declsize);
+         return wide_int_to_tree (sizetype, wisize);
        }
 
       return NULL_TREE;
     }
 
-  if (TREE_CODE (dest) == COMPONENT_REF)
-    {
-      *pdecl = TREE_OPERAND (dest, 1);
-      return component_ref_size (dest);
-    }
-
-  if (TREE_CODE (dest) != ADDR_EXPR)
-    return NULL_TREE;
-
-  tree ref = TREE_OPERAND (dest, 0);
-  if (DECL_P (ref))
-    {
-      *pdecl = ref;
-      if (tree size = DECL_SIZE_UNIT (ref))
-       return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
-    }
+  /* 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)
index 0fcccc12a3947a1aaf05e7e01fd040092f2bd354..2736f161b6bccaa8c3a87f01e8c01f3ed25ede0d 100644 (file)
@@ -133,8 +133,12 @@ extern tree fold_call_stmt (gcall *, bool);
 extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
 extern bool is_simple_builtin (tree);
 extern bool is_inexpensive_builtin (tree);
-extern tree gimple_call_alloc_size (gimple *);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
+
+class vr_values;
+tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
+                            const vr_values * = NULL);
+extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
+                            const vr_values * = NULL);
 
 extern bool readonly_data_expr (tree exp);
 extern bool init_target_chars (void);
index 2a4d9a09b491fa4e0093b1f40715f0425279a75f..e182244b2081c3f9da5ed7386e2d9a87fb2310f7 100644 (file)
@@ -1,3 +1,22 @@
+2019-12-13  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91582
+       * c-c++-common/Wrestrict.c: Adjust expected warnings.
+       * c-c++-common/Wstringop-truncation-4.c: Enable more
+       warnings.
+       * c-c++-common/Wstringop-truncation.c: Remove an xfail.
+       * gcc.dg/Warray-bounds-46.c: Disable -Wstringop-overflow.
+       * gcc.dg/Warray-bounds-47.c: Same.
+       * gcc.dg/Warray-bounds-52.c: New test.
+       * gcc.dg/Wstringop-overflow-27.c: New test.
+       * gcc.dg/Wstringop-overflow-28.c: New test.
+       * gcc.dg/Wstringop-overflow-29.c: New test.
+       * gcc.dg/attr-alloc_size.c (test): Disable -Warray-bounds.
+       * gcc.dg/attr-copy-2.c: Adjust expected warnings.
+       * gcc.dg/builtin-stringop-chk-5.c: Adjust text of expected messages.
+       * gcc.dg/strlenopt-86.c: Relax test.
+       * gcc.target/i386/pr82002-1.c: Prune expected warnings.
+
 2019-12-13  Roman Zhuykov  <zhroma@ispras.ru>
 
        PR rtl-optimization/92591
index c852b06bbd77fd78368cdca1f69838438c18bd6d..1903f502abda7942a94f760ecabcd3898aabfe51 100644 (file)
@@ -731,10 +731,16 @@ void test_strcpy_range (void)
 
   r = SR (3, DIFF_MAX - 3);
   T (8, "01",  a + r, a);
-  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The accesses below might trigger either
+       -Wrestrict: accessing 4 bytes at offsets [3, \[0-9\]+] and 0 may overlap 1 byte at offset 3
+     or
+       -Wstringop-overflow: writing 4 bytes into a region of size 0
+     Either of the two is appropriate.  */
+  T (8, "012", a + r, a);            /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
 
   r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
-  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "\\\[-Wrestrict|-Wstringop-overflow" } */
 
   /* Exercise the full range of ptrdiff_t.  */
   r = signed_value ();
index 15209536add79e112ef62d03d3551d11d0ba4b4d..6ed6a28a7af2c5dfb6cc68ddd0aab61afe3dcd7e 100644 (file)
@@ -21,9 +21,13 @@ struct Arrays
 
 void test_arrays (struct Arrays *p, const char *s)
 {
+  /* Expect accesses to all three arrays to trigger the warning,
+     including the trailing one.  The size argument is a good
+     enough indication that it is not being used as a "legacy"
+     flexible array member.  */
   strncpy (p->a, s, sizeof p->a);           /* { dg-warning "\\\[-Wstringop-truncation" } */
   strncpy ((char*)p->b, s, sizeof p->b);    /* { dg-warning "\\\[-Wstringop-truncation" } */
-  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-warning "\\\[-Wstringop-truncation" } */
 }
 
 struct Pointers
@@ -49,9 +53,11 @@ struct ConstArrays
 
 void test_const_arrays (struct ConstArrays *p, const char *s)
 {
+  /* Expect accesses to all three arrays to trigger the warning,
+     including the trailing one.  */
   strncpy ((char*)p->a, s, sizeof p->a);    /* { dg-warning "\\\[-Wstringop-truncation" } */
   strncpy ((char*)p->b, s, sizeof p->b);    /* { dg-warning "\\\[-Wstringop-truncation" } */
-  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-warning "\\\[-Wstringop-truncation" } */
 }
 
 struct ConstPointers
@@ -77,9 +83,11 @@ struct VolatileArrays
 
 void test_volatile_arrays (struct VolatileArrays *p, const char *s)
 {
+  /* Expect accesses to all three arrays to trigger the warning,
+     including the trailing one.  */
   strncpy ((char*)p->a, s, sizeof p->a);    /* { dg-warning "\\\[-Wstringop-truncation" } */
   strncpy ((char*)p->b, s, sizeof p->b);    /* { dg-warning "\\\[-Wstringop-truncation" } */
-  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-warning "\\\[-Wstringop-truncation" } */
 }
 
 struct VolatilePointers
@@ -105,9 +113,11 @@ struct ConstVolatileArrays
 
 void test_const_volatile_arrays (struct ConstVolatileArrays *p, const char *s)
 {
+  /* Expect accesses to all three arrays to trigger the warning,
+     including the trailing one.  */
   strncpy ((char*)p->a, s, sizeof p->a);    /* { dg-warning "\\\[-Wstringop-truncation" } */
   strncpy ((char*)p->b, s, sizeof p->b);    /* { dg-warning "\\\[-Wstringop-truncation" } */
-  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+  strncpy ((char*)p->c, s, sizeof p->c);    /* { dg-warning "\\\[-Wstringop-truncation" } */
 }
 
 struct ConstVolatilePointers
index 592a9494ca4c392b1ca9729100b1aca2fc73b3c5..5e43405fde8340b64393d1d4248de838851da36d 100644 (file)
@@ -300,8 +300,7 @@ void test_strncpy_array (Dest *pd, int i, const char* s)
   CPY (pd->a5, s, 5);               /* { dg-warning "specified bound 5 equals destination size" } */
   CPY (pd->a5, s, sizeof pd->a5);   /* { dg-warning "specified bound 5 equals destination size" } */
 
-  /* The following is not yet handled.  */
-  CPY (pd->a5 + i, s, sizeof pd->a5);   /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */
+  CPY (pd->a5 + i, s, sizeof pd->a5);   /* { dg-warning "specified bound 5 equals destination size" "member array" } */
 
   /* Verify that a copy that nul-terminates is not diagnosed.  */
   CPY (pd->a5, "1234", sizeof pd->a5);
index db67136b5e7915d55032a6d66d9517a5b5da0cd8..da9ad6fd6a29a6a24c9327bc0a71c2414998ac7c 100644 (file)
@@ -12,7 +12,7 @@ void sink (void*);
 struct Ax
 {
   char n;
-  char a[];                     // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" }
+  char a[];                     // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -93,7 +93,7 @@ NOIPA void gaxx ()
 struct A0
 {
   char n;
-  char a[0];                    // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" }
+  char a[0];                    // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -160,7 +160,7 @@ NOIPA void ga0x ()
 struct A1
 {
   char n;
-  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" }
+  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -234,7 +234,7 @@ NOIPA void ga1x ()
 struct A1i
 {
   char n;
-  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" }
+  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
   char x;
 };
 
@@ -307,7 +307,7 @@ NOIPA void ga1ix ()
 struct Bx
 {
   char n;
-  char a[];                     // { dg-message "at offset 0 to object 'Bx::a' declared here" }
+  char a[];                     // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
 
   // Verify the warning for a constant.
   Bx () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
@@ -332,7 +332,7 @@ NOIPA void gbxi (int i)
 struct B0
 {
   char n;
-  char a[0];                    // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" }
+  char a[0];                    // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
 
   B0 () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -348,7 +348,7 @@ NOIPA void gb0 (void)
 struct B1
 {
   char n;
-  char a[1];                    // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" }
+  char a[1];                    // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
 
   B1 () { a[1] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -362,7 +362,7 @@ NOIPA void gb1 (void)
 
 struct B123
 {
-  char a[123];                  // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" }
+  char a[123];                  // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
 
   B123 () { a[123] = 0; }       // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -376,7 +376,7 @@ NOIPA void gb123 (void)
 
 struct B234
 {
-  char a[234];                  // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" }
+  char a[234];                  // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
 
   B234 (int i) { a[i] = 0; }    // { dg-warning "\\\[-Wstringop-overflow" }
 };
index 4980f93a4707466bf4d4e3e78adfd1214e67e961..74e78cbdbe8348bca6387463a767e3aeabb0a0cd 100644 (file)
@@ -3,7 +3,7 @@
    Test to verify that past-the-end accesses by string functions to member
    arrays by-reference objects are diagnosed.
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-unused-local-typedefs -ftrack-macro-expansion=0" }  */
+   { dg-options "-O2 -Wall -Wno-unused-local-typedefs -Wno-stringop-overflow -ftrack-macro-expansion=0" }  */
 
 #define SA(expr) typedef int StaticAssert [2 * !!(expr) - 1]
 
index 06ad488d1e0e6409c96491da8e96e9cbd9faae74..848ef3651633974865ed77ef2fb10cfc31159597 100644 (file)
@@ -1,7 +1,7 @@
 /* PR middle-end/91830 - Bogus -Warray-bounds on strcpy into a member
    of a subobject compiling binutils
    { dg-do compile }
-   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -ftrack-macro-expansion=0" } */
 
 extern char* strcpy (char*, const char*);
 extern void sink (void*);
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-52.c b/gcc/testsuite/gcc.dg/Warray-bounds-52.c
new file mode 100644 (file)
index 0000000..1a7d76f
--- /dev/null
@@ -0,0 +1,97 @@
+/* PR middle-end/92341 - missing -Warray-bounds indexing past the end
+   of a compound literal
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX    __INT_MAX__
+#define INT_MIN    (-__INT_MAX__ - 1)
+
+void sink (int, ...);
+
+
+#define T(...) sink (__LINE__, (__VA_ARGS__))
+
+
+void direct_idx_cst (void)
+{
+  T ((int[]){ }[-1]);           // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
+  T ((int[]){ }[0]);            // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
+  T ((int[]){ }[1]);            // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
+
+  T ((int[]){ 1 }[-1]);         // { dg-warning "array subscript -1 is below array bounds of 'int\\\[1]'" }
+  T ((int[]){ 1 }[0]);
+  T ((int[]){ 1 }[1]);          // { dg-warning "array subscript 1 is above array bounds of 'int\\\[1]'" }
+  T ((int[]){ 1 }[INT_MIN]);    // { dg-warning "array subscript -\[0-9\]+ is below array bounds of 'int\\\[1]'" }
+  T ((int[]){ 1 }[INT_MAX]);    // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
+  T ((int[]){ 1 }[SIZE_MAX]);   // { dg-warning "array subscript \[0-9\]+ is above array bounds of 'int\\\[1]'" }
+}
+
+
+void direct_idx_var (int i)
+{
+  T ((char[]){ }[i]);           // { dg-warning "array subscript i is outside array bounds of 'char\\\[0]'" }
+  T ((int[]){ }[i]);            // { dg-warning "array subscript i is outside array bounds of 'int\\\[0]'" }
+}
+
+
+void direct_idx_range (void)
+{
+  ptrdiff_t i = SR (-2, -1);
+
+  T ((int[]){ 1 }[i]);          // { dg-warning "array subscript \[ \n\r]+ is outside array bounds of 'int\\\[0]'" "pr?????" { xfail *-*-* } }
+}
+
+
+#undef T
+#define T(idx, ...) do {                       \
+    int *p = (__VA_ARGS__);                    \
+    sink (p[idx]);                             \
+  } while (0)
+
+void ptr_idx_cst (void)
+{
+  T (-1, (int[]){ });           // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[0]'" }
+  T ( 0, (int[]){ });           // { dg-warning "array subscript 0 is outside array bounds of 'int\\\[0]'" }
+  T (+1, (int[]){ });           // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[0]'" }
+
+  T (-1, (int[]){ 1 });         // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" }
+  T ( 0, (int[]){ 1 });
+  T (+1, (int[]){ 1 });         // { dg-warning "array subscript 1 is outside array bounds of 'int\\\[1]'" }
+  T (INT_MIN, (int[]){ 1 });    // { dg-warning "array subscript -\[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { xfail ilp32 } }
+  T (INT_MAX, (int[]){ 1 });    // { dg-warning "array subscript \[0-9\]+ is outside array bounds of 'int\\\[1]'" "lp64" { target lp64 } }
+                                // { dg-warning "array subscript -1 is outside array bounds of 'int\\\[1]'" "ilp32" { target ilp32 } .-1 }
+  T (SIZE_MAX, (int[]){ 1 });   // { dg-warning "array subscript -?\[0-9\]+ is outside array bounds of 'int\\\[1]'" }
+}
+
+
+void ptr_idx_var (int i)
+{
+  T (i, (int[]){ });            // { dg-warning "array subscript \[^\n\r\]+ is outside array bounds of 'int\\\[0]'" }
+  T (i, (int[]){ 1 });
+  T (i, (int[]){ i, 1 });
+}
+
+void ptr_idx_range (void)
+{
+  ptrdiff_t i = SR (-2, -1);
+
+  T (i, (int[]){ });            // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[0]'" }
+  T (i, (int[]){ 1 });          // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
+  T (i, (int[]){ i });          // { dg-warning "array subscript \\\[-2, -1] is outside array bounds of 'int\\\[1]'" }
+
+  i = SR (0, 1);
+
+  T (i, (int[]){ });            // { dg-warning "array subscript \\\[0, 1] is outside array bounds of 'int\\\[0]'" }
+  T (i, (int[]){ 1 });
+
+  i = SR (1, 2);
+  T (i, (int[]){ 1 });          // { dg-warning "array subscript \\\[1, 2] is outside array bounds of 'int\\\[1]'" }
+
+  i = SR (2, 3);
+  T (i, (int[]){ 1, 2, 3 });
+
+  i = SR (3, 4);
+  T (i, (int[]){ 2, 3, 4 });          // { dg-warning "array subscript \\\[3, 4] is outside array bounds of 'int\\\[3]'" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
new file mode 100644 (file)
index 0000000..249ce2b
--- /dev/null
@@ -0,0 +1,293 @@
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+   PR middle-end/85484 - missing -Wstringop-overflow for strcpy with
+   a string of non-const length
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+extern void* memcpy (void*, const void*, size_t);
+extern void* memset (void*, int, size_t);
+extern char* strcpy (char*, const char*);
+extern size_t strlen (const char*);
+
+void sink (void*);
+
+
+void test_memcpy_nowarn (const void *s, int i, size_t n)
+{
+  sink (memcpy (calloc (1, 1), s, 1));
+  sink (memcpy (calloc (1, 2), s, 1));
+  sink (memcpy (calloc (2, 1), s, 1));
+  sink (memcpy (calloc (3, 1), s, 2));
+  sink (memcpy (calloc (3, 1), "12", 2));
+  sink (memcpy (calloc (3, 1), s, 3));
+  sink (memcpy (calloc (3, 1), "12", 3));
+  sink (memcpy (calloc (i, 1), s, 1));
+  sink (memcpy (calloc (n, 1), s, 1));
+  sink (memcpy (calloc (1, n), "", 1));
+  sink (memcpy (calloc (1, i), "", 1));
+  sink (memcpy (calloc (i, 1), "123", 3));
+  sink (memcpy (calloc (n, 1), "123", 3));
+  sink (memcpy (calloc (1, i), "123456", 7));
+  sink (memcpy (calloc (1, n), "123456", 7));
+  sink (memcpy (calloc (n, 1), s, 12345));
+  sink (memcpy (calloc (1, n), s, n - 1));
+  sink (memcpy (calloc (n, 1), s, n));
+
+  sink (memcpy ((char*)calloc (1, 1) + i, "123", 1));
+  sink (memcpy ((char*)calloc (n, 1) + i, "123", n));
+
+  sink (memcpy ((char*)calloc (1, 1) + i, s, 1));
+  sink (memcpy ((char*)calloc (n, 1) + i, s, n));
+
+  sink (memcpy (malloc (1), s, 1));
+  sink (memcpy (malloc (2), s, 1));
+  sink (memcpy (malloc (3), s, 2));
+  sink (memcpy (malloc (3), "12", 2));
+  sink (memcpy (malloc (3), s, 3));
+  sink (memcpy (malloc (3), "12", 3));
+  sink (memcpy (malloc (n), s, 1));
+  sink (memcpy (malloc (n), "", 1));
+  sink (memcpy (malloc (n), "123", 3));
+  sink (memcpy (malloc (n), "123456", 7));
+  sink (memcpy (malloc (n), s, 12345));
+  sink (memcpy (malloc (n), s, n - 1));
+  sink (memcpy (malloc (n), s, n));
+
+  {
+    const int a[] = { 1, 2, 3, 4 };
+    void *p = (char*)malloc (sizeof a);
+    memcpy (p, a, sizeof a);
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4, 5 };
+    size_t nelts = sizeof a / sizeof *a;
+    int vla[nelts];
+    memcpy (vla, a, nelts * sizeof *vla);
+    sink (vla);
+  }
+}
+
+
+void test_memcpy_warn (const int *s, size_t n)
+{
+  {
+    void *p = (char*)malloc (0);
+    memcpy (p, s, 1);                    // { dg-warning "writing 1 byte into a region of size 0" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (1);
+    memcpy (p, s, 2);                    // { dg-warning "writing 2 bytes into a region of size 1" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (2);
+    memcpy (p, s, 3);                    // { dg-warning "writing 3 bytes into a region of size 2" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (3);
+    memcpy (p, s, 4);                    // { dg-warning "writing 4 bytes into a region of size 3" }
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4 };
+    void *p = (char*)malloc (sizeof *a);
+    memcpy (p, a, sizeof a);              // { dg-warning "" }
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4, 5 };
+    size_t nelts = sizeof a / sizeof *a;
+    char vla[nelts];
+    memcpy (vla, a, nelts * sizeof *a);   // { dg-warning "" }
+    sink (vla);
+  }
+
+  {
+    void *p = malloc (n);
+    memcpy (p, s, n * sizeof *s);         // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
+    sink (p);
+  }
+}
+
+void test_memset_nowarn (int x, size_t n)
+{
+  sink (memset (calloc (1, 1), x, 1));
+  sink (memset (calloc (1, 2), x, 1));
+  sink (memset (calloc (2, 1), x, 1));
+  sink (memset (calloc (3, 1), x, 2));
+  sink (memset (calloc (3, 1), x, 3));
+  sink (memset (calloc (n, 1), x, 1));
+  sink (memset (calloc (n, 1), x, 12345));
+  sink (memset (calloc (1, n), x, n - 1));
+  sink (memset (calloc (n, 1), x, n));
+
+  sink (memset (malloc (1), x, 1));
+  sink (memset (malloc (2), x, 1));
+  sink (memset (malloc (3), x, 2));
+  sink (memset (malloc (3), x, 3));
+  sink (memset (malloc (n), x, 1));
+  sink (memset (malloc (n), x, 12345));
+  sink (memset (malloc (n), x, n - 1));
+  sink (memset (malloc (n), x, n));
+
+  {
+    const int a[] = { 1, 2, 3, 4 };
+    void *p = (char*)malloc (sizeof a);
+    memset (p, x, sizeof a);
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4, 5 };
+    size_t nelts = sizeof a / sizeof *a;
+    int vla[nelts];
+    memset (vla, x, nelts * sizeof *vla);
+    sink (vla);
+  }
+}
+
+
+void test_memset_warn (int x, size_t n)
+{
+  {
+    void *p = (char*)malloc (0);
+    memset (p, x, 1);                    // { dg-warning "writing 1 byte into a region of size 0" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (1);
+    memset (p, x, 2);                    // { dg-warning "writing 2 bytes into a region of size 1" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (2);
+    memset (p, x, 3);                    // { dg-warning "writing 3 bytes into a region of size 2" }
+    sink (p);
+  }
+
+  {
+    void *p = (char*)malloc (3);
+    memset (p, x, 4);                    // { dg-warning "writing 4 bytes into a region of size 3" }
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4 };
+    void *p = (char*)malloc (sizeof *a);
+    memset (p, 0, sizeof a);              // { dg-warning "" }
+    sink (p);
+  }
+
+  {
+    const int a[] = { 1, 2, 3, 4, 5 };
+    size_t nelts = sizeof a / sizeof *a;
+    char vla[nelts];
+    memset (vla, 0, nelts * sizeof *a);   // { dg-warning "" }
+    sink (vla);
+  }
+
+  {
+    void *p = malloc (n);
+    memset (p, x, n * sizeof (int));      // { dg-warning "\\\[-Wstringop-overflow" "" { xfail *-*-* } }
+    sink (p);
+  }
+}
+
+
+void test_strcpy_nowarn (const char *s)
+{
+  {
+    const char a[] = "12";
+    int n = strlen (a);
+    char *t = (char*)calloc (2, n);
+    strcpy (t, a);
+    sink (t);
+  }
+
+  {
+    const char a[] = "123";
+    unsigned n = strlen (a) + 1;
+    char *t = (char*)calloc (n, 1);
+    strcpy (t, a);
+    sink (t);
+  }
+
+  {
+    const char a[] = "1234";
+    size_t n = strlen (a) * 2;
+    char *t = (char*)malloc (n);
+    strcpy (t, a);
+    sink (t);
+  }
+
+  {
+    const char a[] = "1234";
+    size_t len = strlen (a) + 1;
+    char vla[len];
+    strcpy (vla, a);
+    sink (vla);
+  }
+
+  {
+    size_t n = strlen (s) + 1;
+    char *t = (char*)malloc (n);
+    strcpy (t, s);
+    sink (t);
+  }
+}
+
+
+void test_strcpy_warn (const char *s)
+{
+  {
+    const char a[] = "123";
+    /* Verify that using signed int for the strlen result works (i.e.,
+       that the conversion from signed int to size_t doesn't prevent
+       the detection.  */
+    int n = strlen (a);
+    char *t = (char*)calloc (n, 1);     // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note" { xfail *-*-* } }
+                                        // { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note" { target *-*-* } .-1 }
+    strcpy (t, a);                      // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
+
+    sink (t);
+  }
+
+  {
+    const char a[] = "1234";
+    size_t n = strlen (a);
+    char *t = (char*)malloc (n);        // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note" { xfail *-*-* } }
+                                        // { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note" { target *-*-* } .-1 }
+    strcpy (t, a);                      // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
+    sink (t);
+  }
+
+  // Exercise PR middle-end/85484.
+  {
+    size_t len = strlen (s);
+    char vla[len];                      // { dg-message "at offset 0 to an object declared here" "vla note" }
+    strcpy (vla, s);                    // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
+    sink (vla);
+  }
+
+  {
+    size_t n = strlen (s);
+    char *t = (char*)malloc (n);        // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
+    strcpy (t, s);                      // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
+    sink (t);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
new file mode 100644 (file)
index 0000000..8844b9f
--- /dev/null
@@ -0,0 +1,236 @@
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX     __INT_MAX__
+#define INT_MIN     (-INT_MAX - 1)
+
+#define ATTR(...)   __attribute__ ((__VA_ARGS__))
+#define NOIPA       ATTR (noipa)
+
+extern void* alloca (size_t);
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+
+extern ATTR (alloc_size (1), malloc) char* alloc1 (size_t);
+extern ATTR (alloc_size (1, 2), malloc) char* alloc2 (size_t, size_t);
+
+extern char* strcpy (char*, const char*);
+
+void sink (void*, ...);
+
+
+/* Verify warning in stores to an object of variable size N in a known
+   range, at an offset (N + I) with a constant I.  */
+
+void same_size_and_offset_idx_cst (void)
+{
+#define T(size, off, idx) do {                 \
+    size_t n_ = size;                          \
+    ptrdiff_t i_ = idx;                                \
+    char *p_ = alloc1 (n_);                    \
+    p_ += off;                                 \
+    p_[i_] = 0;                                        \
+    sink (p_);                                 \
+  } while (0)
+
+  {
+    const size_t n = UR (2, 3);
+
+    T (n, n, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+    T (n, n, -3);
+    T (n, n, -2);
+    T (n, n, -1);
+    T (n, n,  0);
+    T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+  }
+
+  {
+    const size_t n = UR (3, 4);
+
+    T (n, n, -5);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+    T (n, n, -4);
+    T (n, n, -3);
+    T (n, n, -2);
+    T (n, n, -1);
+    T (n, n,  0);
+    T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+  }
+
+  {
+    const size_t n = UR (5, SIZE_MAX - 2);
+    T (n, n, -1);
+    T (n, n, -1);
+    T (n, n, -1);
+    T (n, n, -1);
+  }
+}
+
+
+/* Verify warning in stores to an object of variable size N in a known
+   range, at an offset (M + I) with a variable M in some range and
+   constant I.  */
+
+void different_size_and_offset_idx_cst (void)
+{
+  {
+    const size_t n = UR (2, 3);
+    const size_t i = UR (1, 2);
+
+    T (n, i, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+    T (n, i, -3);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+    T (n, i, -2);
+    T (n, i, -1);
+    T (n, i,  0);
+    T (n, i,  1);
+    T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+  }
+
+  {
+    const size_t n = UR (3, 4);
+    const size_t i = UR (2, 5);
+
+    T (n, i, -6);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+
+    /* The offsets -5 and -4 are both necessarily invalid even if the sum
+       (i - 5) and (i - 4) are (or could be) in bounds because they imply
+       that the intermediate offset (p + i) is out of bounds.  */
+    T (n, i, -5);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+    T (n, i, -4);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+    T (n, i, -3);
+    T (n, i, -2);
+    T (n, i, -1);
+    T (n, i,  0);
+    T (n, i,  1);
+    T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
+                    // { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+  }
+}
+
+
+/* Verify warning in stores to an object of variable size N in a known
+   range, at an offset (M + I) with a variable M in some range and
+   constant I.  */
+void different_size_and_offset_idx_var (void)
+{
+  {
+    const size_t n = UR (3, 4);
+    const size_t i = UR (1, 2);
+
+    T (n, i, SR (DIFF_MIN, 0));
+    T (n, i, SR (      -3, 0));
+    T (n, i, SR (      -1, 0));
+    T (n, i, SR (       0, 1));
+    T (n, i, SR (       1, 2));
+    T (n, i, SR (       2, 3));
+    /* The warning is issued below but the offset and the size in
+       the note are wrong.  See the FIXME in compute_objsize().  */
+    T (n, i, SR (       3, 4));    // { dg-warning "\\\[-Wstringop-overflow" }
+                                   // { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
+                                   // { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
+ }
+}
+
+
+void ptr_add_2 (int n, int i0, int i1)
+{
+  if (n < 1 || 2 < n) n = 2;
+
+  if (i0 < 0 || 1 < i0) i0 = 0;
+  if (i1 < 1 || 2 < i1) i1 = 1;
+
+  char *p = (char*)__builtin_malloc (n);
+  char *q = p;
+
+  q += i0;
+  q[0] = 0;   // p[0]
+  q += i1;
+  q[0] = 1;   // p[1]
+  q[1] = 2;   // p[2]     // { dg-warning "\\\[-Wstringop-overflow" }
+
+  sink (p, q);
+}
+
+void ptr_add_3 (int n, int i0, int i1, int i2)
+{
+  if (n < 3 || 4 < n) n = 3;
+
+  if (i0 < 0 || 1 < i0) i0 = 0;
+  if (i1 < 1 || 2 < i1) i1 = 1;
+  if (i2 < 2 || 3 < i2) i2 = 2;
+
+  char *p = (char*)__builtin_malloc (n);
+  char *q = p;
+
+  q += i0;
+  q[0] = 0;   // p[0]
+  q += i1;
+  q[0] = 1;   // p[1]
+  q[1] = 2;   // p[2]
+  q += i2;
+  q[0] = 3;   // p[3]
+  q[1] = 4;   // p[4]     // { dg-warning "\\\[-Wstringop-overflow" }
+
+  sink (p, q);
+}
+
+void ptr_add_4 (int n, int i0, int i1, int i2, int i3)
+{
+  if (n < 7 || 8 < n) n = 7;
+
+  if (i0 < 0 || 1 < i0) i0 = 0;
+  if (i1 < 1 || 2 < i1) i1 = 1;
+  if (i2 < 2 || 3 < i2) i2 = 2;
+  if (i3 < 3 || 4 < i3) i3 = 3;
+
+  char *p = (char*)__builtin_malloc (n);
+  char *q = p;
+
+  q += i0;
+  q[0] = 0;   // p[0]
+  q += i1;
+  q[0] = 1;   // p[1]
+  q[1] = 2;   // p[2]
+  q += i2;
+  q[0] = 3;   // p[3]
+  q[1] = 4;   // p[4]
+  q[2] = 5;   // p[5]
+  q += i3;
+  q[0] = 6;   // p[6]
+  q[1] = 7;   // p[7]
+  q[2] = 8;   // p[8]     // { dg-warning "\\\[-Wstringop-overflow" }
+
+  sink (p, q);
+}
+
+void ptr_sub_from_end (int n, int i0, int i1, int i2, int i3)
+{
+  if (n < 1 || 2 < n) n = 2;
+
+  char *p = (char*)__builtin_malloc (n);
+  char *q = p;
+
+  // The following isn't diagnosed due to a bug/limitation.
+  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 *-*-* } }
+
+  /* The following isn't diagnosed because the warning doesn't recognize
+     the index below as necessarily having the same value as the size
+     argument to malloc.  All it considers is the range.  */
+  q[0] = 2;                       // { dg-warning "\\\[-Wstringop-overflow" "pr92937: store just past the end" { xfail *-*-* } }
+  q[1] = 3;                       // { dg-warning "\\\[-Wstringop-overflow" }
+
+  sink (p, q);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
new file mode 100644 (file)
index 0000000..c011d05
--- /dev/null
@@ -0,0 +1,66 @@
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+   Verify calls via function pointers.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+typedef __attribute__ ((alloc_size (1))) char* allocfn_t (unsigned);
+
+extern allocfn_t allocfn;
+
+void sink (void*);
+
+void direct_call (void)
+{
+  char *q = allocfn (0);            // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
+  q[0] = 0;                         // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (q);
+}
+
+
+void local_ptr_call (void)
+{
+  allocfn_t *ptr = allocfn;
+  char *q = ptr (1);                // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
+  q[0] = 0;
+  q[-1] = 0;                        // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (q);
+}
+
+
+void global_ptr_call (void)
+{
+  extern allocfn_t *ptralloc;
+
+  allocfn_t *ptr = ptralloc;
+  char *q = ptr (2);               // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
+  q[0] = 0;
+  q[1] = 1;
+  q[3] = 3;                        // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (q);
+}
+
+void global_ptr_array_call (void)
+{
+  extern allocfn_t * (arralloc[]);
+
+  allocfn_t *ptr = arralloc[0];
+  char *q = ptr (2);               // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
+  q[0] = 1;
+  q[1] = 2;
+  q[3] = 3;                        // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (q);
+}
+
+
+struct S { allocfn_t *ptralloc; };
+
+void member_ptr_call (struct S *p)
+{
+  char *q = p->ptralloc (3);       // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
+  q[0] = 0;
+  q[1] = 1;
+  q[2] = 2;
+  q[5] = 0;                        // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (q);
+}
+
index 7b0dc6e4535efdb750626e641fe43237cce27882..4c0cd9a14c477eb0ec7f80256aaf2b21fb6044ac 100644 (file)
@@ -22,15 +22,15 @@ test (void)
   strcpy (p, "Hello");
   p = malloc1 (6);
   strcpy (p, "Hello");
-  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
   p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
   p = calloc1 (2, 5);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
   p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5);
   strcpy (p, "World");
-  strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */
+  strcpy (p, "Hello World"); /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "strcpy" } */
 }
 
index f311ca32aa602a249384fcb22432157af8aebbb5..ffc7208f4a7369b5e795322b4019c03c8fc9b731 100644 (file)
@@ -99,7 +99,7 @@ void* xref12 (int);
 void* call_xref12 (void)
 {
   void *p = xref12 (3);
-  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
   return p;
 }
 
@@ -197,7 +197,7 @@ void* falias_malloc (void);
 void* call_falias_malloc (void)
 {
   char *p = falias_malloc ();
-  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Wstringop-overflow=]" } */
+  __builtin___strcpy_chk (p, "123", __builtin_object_size (p, 0));   /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" } */
   return p;
 }
 
index 320cd51fcf2d9fd1eb3a174eb4e3011f01add4a6..87dd6ac4e89c5812c847c4cf1098d0616976e7cd 100644 (file)
@@ -110,7 +110,7 @@ void test_memop_warn_alloc (const void *src)
 
   struct A *a = __builtin_malloc (sizeof *a * 2);
 
-  memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
+  memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
   escape (a, src);
 
   /* At -Wstringop-overflow=1 the destination is considered to be
@@ -127,7 +127,7 @@ void test_memop_warn_alloc (const void *src)
 
   struct B *b = __builtin_malloc (sizeof *b * 2);
 
-  memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" "memcpy into allocated" } */
+  memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
   escape (b);
 
   /* The following idiom of clearing multiple members of a struct is
index 3e86fa3c90aba9810809d88b23e38e172221cb8f..d2029443556843d51b5be585927e9fdf9f128834 100644 (file)
@@ -9,11 +9,11 @@
 unsigned n0, n1;
 
 void*
-keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
+keep_strlen_calloc_store_cst_memset (int i, unsigned a, unsigned b)
 {
   char *p = __builtin_calloc (a, 1);
 
-  p[1] = 'x';
+  p[i] = 'x';
 
   __builtin_memset (p, 0, b);
 
@@ -23,11 +23,11 @@ keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
 }
 
 void*
-keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
+keep_strlen_calloc_store_var_memset (int i, int x, unsigned a, unsigned b)
 {
   char *p = __builtin_calloc (a, 1);
 
-  p[1] = x;
+  p[i] = x;
 
   __builtin_memset (p, 0, b);
 
@@ -37,11 +37,11 @@ keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
 }
 
 void*
-keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
+keep_strlen_calloc_store_memset_2 (int i, int x, unsigned a, unsigned b, unsigned c)
 {
   char *p = __builtin_calloc (a, 1);
 
-  p[1] = x;
+  p[i] = x;
   __builtin_memset (p, 0, b);
 
   n0 = __builtin_strlen (p);
index 86678a019929f0990d6a73437e2e08805b1d2f2d..b4d4bd3d12599a8a2172edc61ab34fa3f662a0b4 100644 (file)
@@ -10,3 +10,5 @@ b ()
   a (c);
   a (c);
 }
+
+// { dg-prune-output "\\\[-Wstringop-overflow" }
index 212ac7152bf5a5266a64384ccb8efd5fff074166..6ef07411e32aef3d424afe02b03d3ef40f872431 100644 (file)
@@ -84,14 +84,20 @@ struct strinfo
   tree nonzero_chars;
   /* Any of the corresponding pointers for querying alias oracle.  */
   tree ptr;
-  /* This is used for two things:
+  /* STMT is used for two things:
 
      - To record the statement that should be used for delayed length
        computations.  We maintain the invariant that all related strinfos
        have delayed lengths or none do.
 
-     - To record the malloc or calloc call that produced this result.  */
+     - To record the malloc or calloc call that produced this result
+       to optimize away malloc/memset sequences.  STMT is reset after
+       a calloc-allocated object has been stored a non-zero value into.  */
   gimple *stmt;
+  /* Set to the dynamic allocation statement for the object (alloca,
+     calloc, malloc, or VLA).  Unlike STMT, once set for a strinfo
+     object, ALLOC doesn't change.  */
+  gimple *alloc;
   /* Pointer to '\0' if known, if NULL, it can be computed as
      ptr + length.  */
   tree endptr;
@@ -189,20 +195,21 @@ static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
 static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
 
 /* Sets MINMAX to either the constant value or the range VAL is in
-   and returns true on success.  When nonnull, uses RVALS to get
-   VAL's range.  Otherwise uses get_range_info.  */
+   and returns either the constant value or VAL on success or null
+   when the range couldn't be determined.  Uses RVALS when nonnull
+   to determine the range, otherwise get_range_info.  */
 
-static bool
-get_range (tree val, wide_int minmax[2], const vr_values *rvals = NULL)
+tree
+get_range (tree val, wide_int minmax[2], const vr_values *rvals /* = NULL */)
 {
-  if (tree_fits_uhwi_p (val))
+  if (TREE_CODE (val) == INTEGER_CST)
     {
       minmax[0] = minmax[1] = wi::to_wide (val);
-      return true;
+      return val;
     }
 
   if (TREE_CODE (val) != SSA_NAME)
-    return false;
+    return NULL_TREE;
 
   if (rvals)
     {
@@ -215,20 +222,20 @@ get_range (tree val, wide_int minmax[2], const vr_values *rvals = NULL)
        = (CONST_CAST (class vr_values *, rvals)->get_value_range (val));
       value_range_kind rng = vr->kind ();
       if (rng != VR_RANGE || !range_int_cst_p (vr))
-       return false;
+       return NULL_TREE;
 
       minmax[0] = wi::to_wide (vr->min ());
       minmax[1] = wi::to_wide (vr->max ());
-      return true;
+      return val;
     }
 
   value_range_kind rng = get_range_info (val, minmax, minmax + 1);
   if (rng == VR_RANGE)
-    return true;
+    return val;
 
   /* Do not handle anti-ranges and instead make use of the on-demand
      VRP if/when it becomes available (hopefully in GCC 11).  */
-  return false;
+  return NULL_TREE;
 }
 
 /* Return:
@@ -320,7 +327,7 @@ get_next_strinfo (strinfo *si)
 /* Helper function for get_stridx.  Return the strinfo index of the address
    of EXP, which is available in PTR if nonnull.  If OFFSET_OUT, it is
    OK to return the index for some X <= &EXP and store &EXP - X in
-   *OFFSET_OUT.  */
+   *OFFSET_OUT.  When nonnull uses RVALS to determine range information.  */
 
 static int
 get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
@@ -380,13 +387,14 @@ get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
    to a known strinfo with an offset and OFFRNG is non-null, sets
    both elements of the OFFRNG array to the range of the offset and
    returns the index of the known strinfo.  In this case the result
-   must not be used in for functions that modify the string.  */
+   must not be used in for functions that modify the string.
+   When nonnull, uses RVALS to determine range information.  */
 
 static int
-get_stridx (tree exp, wide_int offrng[2] = NULL)
+get_stridx (tree exp, wide_int offrng[2] = NULL, const vr_values *rvals = NULL)
 {
   if (offrng)
-    offrng[0] = offrng[1] = wi::zero (TYPE_PRECISION (sizetype));
+    offrng[0] = offrng[1] = wi::zero (TYPE_PRECISION (ptrdiff_type_node));
 
   if (TREE_CODE (exp) == SSA_NAME)
     {
@@ -465,7 +473,7 @@ get_stridx (tree exp, wide_int offrng[2] = NULL)
                       return the index corresponding to the SSA_NAME.
                       Do this irrespective of the whether the offset
                       is known.  */
-                   if (get_range (off, offrng))
+                   if (get_range (off, offrng, rvals))
                      {
                        /* When the offset range is known, increment it
                           it by the constant offset computed in prior
@@ -672,6 +680,7 @@ new_strinfo (tree ptr, int idx, tree nonzero_chars, bool full_string_p)
   si->nonzero_chars = nonzero_chars;
   si->ptr = ptr;
   si->stmt = NULL;
+  si->alloc = NULL;
   si->endptr = NULL_TREE;
   si->refcount = 1;
   si->idx = idx;
@@ -838,6 +847,8 @@ get_string_length (strinfo *si)
            if (chainsi->nonzero_chars == NULL)
              set_endptr_and_length (loc, chainsi, lhs);
          break;
+       case BUILT_IN_ALLOCA:
+       case BUILT_IN_ALLOCA_WITH_ALIGN:
        case BUILT_IN_MALLOC:
          break;
        /* BUILT_IN_CALLOC always has si->nonzero_chars set.  */
@@ -885,45 +896,57 @@ dump_strlen_info (FILE *fp, gimple *stmt, const vr_values *rvals)
                  fprintf (fp, ", ptr = ");
                  print_generic_expr (fp, si->ptr);
                }
-             fprintf (fp, ", nonzero_chars = ");
-             print_generic_expr (fp, si->nonzero_chars);
-             if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+
+             if (si->nonzero_chars)
                {
-                 value_range_kind rng = VR_UNDEFINED;
-                 wide_int min, max;
-                 if (rvals)
+                 fprintf (fp, ", nonzero_chars = ");
+                 print_generic_expr (fp, si->nonzero_chars);
+                 if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
                    {
-                     const value_range_equiv *vr
-                       = CONST_CAST (class vr_values *, rvals)
-                       ->get_value_range (si->nonzero_chars);
-                     rng = vr->kind ();
-                     if (range_int_cst_p (vr))
+                     value_range_kind rng = VR_UNDEFINED;
+                     wide_int min, max;
+                     if (rvals)
                        {
-                         min = wi::to_wide (vr->min ());
-                         max = wi::to_wide (vr->max ());
+                         const value_range *vr
+                           = CONST_CAST (class vr_values *, rvals)
+                           ->get_value_range (si->nonzero_chars);
+                         rng = vr->kind ();
+                         if (range_int_cst_p (vr))
+                           {
+                             min = wi::to_wide (vr->min ());
+                             max = wi::to_wide (vr->max ());
+                           }
+                         else
+                           rng = VR_UNDEFINED;
                        }
                      else
-                       rng = VR_UNDEFINED;
-                   }
-                 else
-                   rng = get_range_info (si->nonzero_chars, &min, &max);
+                       rng = get_range_info (si->nonzero_chars, &min, &max);
 
-                 if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
-                   {
-                     fprintf (fp, " %s[%llu, %llu]",
-                              rng == VR_RANGE ? "" : "~",
-                              (long long) min.to_uhwi (),
-                              (long long) max.to_uhwi ());
+                     if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
+                       {
+                         fprintf (fp, " %s[%llu, %llu]",
+                                  rng == VR_RANGE ? "" : "~",
+                                  (long long) min.to_uhwi (),
+                                  (long long) max.to_uhwi ());
+                       }
                    }
                }
-             fprintf (fp, " , refcount = %i", si->refcount);
+
+             fprintf (fp, ", refcount = %i", si->refcount);
              if (si->stmt)
                {
                  fprintf (fp, ", stmt = ");
                  print_gimple_expr (fp, si->stmt, 0);
                }
+             if (si->alloc)
+               {
+                 fprintf (fp, ", alloc = ");
+                 print_gimple_expr (fp, si->alloc, 0);
+               }
              if (si->writable)
                fprintf (fp, ", writable");
+             if (si->dont_invalidate)
+               fprintf (fp, ", dont_invalidate");
              if (si->full_string_p)
                fprintf (fp, ", full_string_p");
              if (strinfo *next = get_next_strinfo (si))
@@ -1197,80 +1220,87 @@ get_range_strlen_dynamic (tree src, c_strlen_data *pdata,
     BITMAP_FREE (visited);
 }
 
-/* Invalidate string length information for strings whose length
-   might change due to stores in stmt, except those marked DON'T
-   INVALIDATE.  For string-modifying statements, ZERO_WRITE is
-   set when the statement wrote only zeros.  */
+/* Invalidate string length information for strings whose length might
+   change due to stores in STMT, except those marked DONT_INVALIDATE.
+   For string-modifying statements, ZERO_WRITE is set when the statement
+   wrote only zeros.
+   Returns true if any STRIDX_TO_STRINFO entries were considered
+   for invalidation.  */
 
 static bool
 maybe_invalidate (gimple *stmt, bool zero_write = false)
 {
   if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "  %s()\n", __func__);
+    {
+      fprintf (dump_file, "%s called for ", __func__);
+      print_gimple_stmt (dump_file, stmt, TDF_LINENO);
+    }
 
   strinfo *si;
-  unsigned int i;
   bool nonempty = false;
 
-  for (i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i)
-    if (si != NULL)
-      {
-       if (!si->dont_invalidate)
-         {
-           ao_ref r;
-           tree size = NULL_TREE;
-           if (si->nonzero_chars)
-             {
-               /* Include the terminating nul in the size of the string
-                  to consider when determining possible clobber.  */
-               tree type = TREE_TYPE (si->nonzero_chars);
-               size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
-                                   build_int_cst (type, 1));
-             }
-           ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
-           if (stmt_may_clobber_ref_p_1 (stmt, &r))
-             {
-               if (dump_file && (dump_flags & TDF_DETAILS))
-                 {
-                   if (size && tree_fits_uhwi_p (size))
-                     fprintf (dump_file,
-                              "  statement may clobber string "
-                              HOST_WIDE_INT_PRINT_UNSIGNED " long\n",
-                              tree_to_uhwi (size));
-                   else
-                     fprintf (dump_file,
-                              "  statement may clobber string\n");
-                 }
+  for (unsigned i = 1; vec_safe_iterate (stridx_to_strinfo, i, &si); ++i)
+    {
+      if (si == NULL || !POINTER_TYPE_P (TREE_TYPE (si->ptr)))
+       continue;
 
-               set_strinfo (i, NULL);
-               free_strinfo (si);
-               continue;
-             }
+      nonempty = true;
 
-           if (size
-               && !zero_write
-               && si->stmt
-               && is_gimple_call (si->stmt)
-               && (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
-                   == BUILT_IN_CALLOC))
-             {
-               /* If the clobber test above considered the length of
-                  the string (including the nul), then for (potentially)
-                  non-zero writes that might modify storage allocated by
-                  calloc consider the whole object and if it might be
-                  clobbered by the statement reset the allocation
-                  statement.  */
-               ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
-               if (stmt_may_clobber_ref_p_1 (stmt, &r))
-                 si->stmt = NULL;
-             }
-         }
-       si->dont_invalidate = false;
-       nonempty = true;
-      }
+      /* Unconditionally reset DONT_INVALIDATE.  */
+      bool dont_invalidate = si->dont_invalidate;
+      si->dont_invalidate = false;
+
+      if (dont_invalidate)
+       continue;
+
+      ao_ref r;
+      tree size = NULL_TREE;
+      if (si->nonzero_chars)
+       {
+         /* Include the terminating nul in the size of the string
+            to consider when determining possible clobber.  */
+         tree type = TREE_TYPE (si->nonzero_chars);
+         size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
+                             build_int_cst (type, 1));
+       }
+      ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
+      if (stmt_may_clobber_ref_p_1 (stmt, &r))
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fputs ("  statement may clobber object ", dump_file);
+             print_generic_expr (dump_file, si->ptr);
+             if (size && tree_fits_uhwi_p (size))
+               fprintf (dump_file, " " HOST_WIDE_INT_PRINT_UNSIGNED
+                        " bytes in size", tree_to_uhwi (size));
+             fputc ('\n', dump_file);
+           }
+
+         set_strinfo (i, NULL);
+         free_strinfo (si);
+         continue;
+       }
+
+      if (size
+         && !zero_write
+         && si->stmt
+         && is_gimple_call (si->stmt)
+         && (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
+             == BUILT_IN_CALLOC))
+       {
+         /* If the clobber test above considered the length of
+            the string (including the nul), then for (potentially)
+            non-zero writes that might modify storage allocated by
+            calloc consider the whole object and if it might be
+            clobbered by the statement reset the statement.  */
+         ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
+         if (stmt_may_clobber_ref_p_1 (stmt, &r))
+           si->stmt = NULL;
+       }
+    }
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "  %s() ==> %i\n", __func__, nonempty);
+    fprintf (dump_file, "%s returns %i\n", __func__, nonempty);
 
   return nonempty;
 }
@@ -1289,6 +1319,7 @@ unshare_strinfo (strinfo *si)
 
   nsi = new_strinfo (si->ptr, si->idx, si->nonzero_chars, si->full_string_p);
   nsi->stmt = si->stmt;
+  nsi->alloc = si->alloc;
   nsi->endptr = si->endptr;
   nsi->first = si->first;
   nsi->prev = si->prev;
@@ -1582,6 +1613,8 @@ valid_builtin_call (gimple *stmt)
        return false;
       break;
 
+    case BUILT_IN_ALLOCA:
+    case BUILT_IN_ALLOCA_WITH_ALIGN:
     case BUILT_IN_CALLOC:
     case BUILT_IN_MALLOC:
     case BUILT_IN_MEMCPY:
@@ -1858,92 +1891,159 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
 }
 
 /* Diagnose buffer overflow by a STMT writing LEN + PLUS_ONE bytes,
-   into an object designated by the LHS of STMT otherise.  */
+   either into a region allocated for the object SI when non-null,
+   or into an object designated by the LHS of STMT otherwise.
+   When nonnull uses RVALS to determine range information.
+   RAWMEM may be set by memcpy and other raw memory functions
+   to allow accesses across subobject boundaries.  */
 
 static void
 maybe_warn_overflow (gimple *stmt, tree len,
                     const vr_values *rvals = NULL,
-                    strinfo *si = NULL, bool plus_one = false)
+                    strinfo *si = NULL, bool plus_one = false,
+                    bool rawmem = false)
 {
   if (!len || gimple_no_warning_p (stmt))
     return;
 
+  /* The DECL of the function performing the write if it is done
+     by one.  */
   tree writefn = NULL_TREE;
-  tree destdecl = NULL_TREE;
-  tree destsize = NULL_TREE;
+  /* The destination expression involved in the store STMT.  */
   tree dest = NULL_TREE;
 
-  /* The offset into the destination object set by compute_objsize
-     but already reflected in DESTSIZE.  */
-  tree destoff = NULL_TREE;
-
   if (is_gimple_assign (stmt))
-    {
-      dest = gimple_assign_lhs (stmt);
-      if (TREE_NO_WARNING (dest))
-       return;
-
-      /* For assignments try to determine the size of the destination
-        first.  Set DESTOFF to the the offset on success.  */
-      tree off = size_zero_node;
-      destsize = compute_objsize (dest, 1, &destdecl, &off);
-      if (destsize)
-       destoff = off;
-    }
+    dest = gimple_assign_lhs (stmt);
   else if (is_gimple_call (stmt))
     {
-      writefn = gimple_call_fndecl (stmt);
       dest = gimple_call_arg (stmt, 0);
+      writefn = gimple_call_fndecl (stmt);
     }
 
+  if (TREE_NO_WARNING (dest))
+    return;
+
   /* The offset into the destination object computed below and not
-     reflected in DESTSIZE.  Either DESTOFF is set above or OFFRNG
-     below.  */
+     reflected in DESTSIZE.  */
   wide_int offrng[2];
-  offrng[0] = wi::zero (TYPE_PRECISION (sizetype));
-  offrng[1] = offrng[0];
+  const int off_prec = TYPE_PRECISION (ptrdiff_type_node);
+  offrng[0] = offrng[1] = wi::zero (off_prec);
 
-  if (!destsize && !si && dest)
+  if (!si)
     {
-      /* For both assignments and calls, if no destination STRINFO was
-        provided, try to get it from the DEST.  */
+      /* If no destination STRINFO was provided try to get it from
+        the DEST argument.  */
       tree ref = dest;
-      tree off = NULL_TREE;
       if (TREE_CODE (ref) == ARRAY_REF)
        {
          /* Handle stores to VLAs (represented as
             ARRAY_REF (MEM_REF (vlaptr, 0), N].  */
-         off = TREE_OPERAND (ref, 1);
+         tree off = TREE_OPERAND (ref, 1);
          ref = TREE_OPERAND (ref, 0);
+         if (get_range (off, offrng, rvals))
+           {
+             offrng[0] = offrng[0].from (offrng[0], off_prec, SIGNED);
+             offrng[1] = offrng[1].from (offrng[1], off_prec, SIGNED);
+           }
+         else
+           {
+             offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
+             offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+           }
        }
 
       if (TREE_CODE (ref) == MEM_REF)
        {
          tree mem_off = TREE_OPERAND (ref, 1);
-         if (off)
+         ref = TREE_OPERAND (ref, 0);
+         wide_int memoffrng[2];
+         if (get_range (mem_off, memoffrng, rvals))
            {
-             if (!integer_zerop (mem_off))
-               return;
+             offrng[0] += memoffrng[0];
+             offrng[1] += memoffrng[1];
            }
          else
-           off = mem_off;
-         ref = TREE_OPERAND (ref, 0);
+           {
+             offrng[0] = wi::to_wide (TYPE_MIN_VALUE (ptrdiff_type_node));
+             offrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+           }
        }
 
-      if (int idx = get_stridx (ref, offrng))
+      wide_int stroffrng[2];
+      if (int idx = get_stridx (ref, stroffrng, rvals))
        {
          si = get_strinfo (idx);
-         if (off && TREE_CODE (off) == INTEGER_CST)
+         offrng[0] += stroffrng[0];
+         offrng[1] += stroffrng[1];
+       }
+    }
+
+  /* The allocation call if the destination object was allocated
+     by one.  */
+  gimple *alloc_call = NULL;
+  /* The DECL of the destination object if known and not dynamically
+     allocated.  */
+  tree destdecl = NULL_TREE;
+  /* The offset into the destination object set by compute_objsize
+     but already reflected in DESTSIZE.  */
+  tree destoff = NULL_TREE;
+  /* The size of the destination region (which is smaller than
+     the destination object for stores at a non-zero offset).  */
+  tree destsize = NULL_TREE;
+
+  /* Compute the range of sizes of the destination object.  The range
+     is constant for declared objects but may be a range for allocated
+     objects.  */
+  const int siz_prec = TYPE_PRECISION (size_type_node);
+  wide_int sizrng[2];
+  if (si)
+    {
+      destsize = gimple_call_alloc_size (si->alloc, sizrng, rvals);
+      alloc_call = si->alloc;
+    }
+  else
+    offrng[0] = offrng[1] = wi::zero (off_prec);
+
+  if (!destsize)
+    {
+      /* If there is no STRINFO for DEST, fall back on compute_objsize.  */
+      tree off = NULL_TREE;
+      destsize = compute_objsize (dest, rawmem ? 0 : 1, &destdecl, &off, rvals);
+      if (destsize)
+       {
+         /* Remember OFF but clear OFFRNG that may have been set above.  */
+         destoff = off;
+         offrng[0] = offrng[1] = wi::zero (off_prec);
+
+         if (destdecl && TREE_CODE (destdecl) == SSA_NAME)
            {
-             wide_int wioff = wi::to_wide (off, offrng->get_precision ());
-             offrng[0] += wioff;
-             offrng[1] += wioff;
+             gimple *stmt = SSA_NAME_DEF_STMT (destdecl);
+             if (is_gimple_call (stmt))
+               alloc_call = stmt;
+             destdecl = NULL_TREE;
+           }
+
+         if (!get_range (destsize, sizrng, rvals))
+           {
+             /* On failure, rather than failing, set the maximum range
+                so that overflow in allocated objects whose size depends
+                on the strlen of the source can still be diagnosed
+                below.  */
+             sizrng[0] = wi::zero (siz_prec);
+             sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
            }
        }
-      else
-       return;
     }
 
+  if (!destsize)
+    {
+      sizrng[0] = wi::zero (siz_prec);
+      sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (sizetype));
+    };
+
+  sizrng[0] = sizrng[0].from (sizrng[0], siz_prec, UNSIGNED);
+  sizrng[1] = sizrng[1].from (sizrng[1], siz_prec, UNSIGNED);
+
   /* Return early if the DESTSIZE size expression is the same as LEN
      and the offset into the destination is zero.  This might happen
      in the case of a pair of malloc and memset calls to allocate
@@ -1961,37 +2061,43 @@ maybe_warn_overflow (gimple *stmt, tree len,
       lenrng[1] += 1;
     }
 
-  /* Compute the range of sizes of the destination object.  The range
-     is constant for declared objects but may be a range for allocated
-     objects.  */
-  wide_int sizrng[2];
-  if (!destsize || !get_range (destsize, sizrng, rvals))
-    {
-      /* On failure, rather than bailing outright, use the maximum range
-        so that overflow in allocated objects whose size depends on
-        the strlen of the source can still be diagnosed below.  */
-      sizrng[0] = wi::zero (lenrng->get_precision ());
-      sizrng[1] = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
-    }
-
-  /* The size of the remaining space in the destination computed as
-     the size of the latter minus the offset into it.  */
+  /* The size of the remaining space in the destination computed
+     as the size of the latter minus the offset into it.  */
   wide_int spcrng[2] = { sizrng[0], sizrng[1] };
-  if (wi::sign_mask (offrng[0]))
+  if (wi::neg_p (offrng[0]) && wi::neg_p (offrng[1]))
     {
-      /* FIXME: Handle negative offsets into allocated objects.  */
-      if (destdecl)
-       spcrng[0] = spcrng[1] = wi::zero (spcrng->get_precision ());
-      else
+      /* When the offset is negative and the size of the destination
+        object unknown there is little to do.
+        FIXME: Detect offsets that are necessarily invalid regardless
+        of the size of the object.  */
+      if (!destsize)
        return;
+
+      /* The remaining space is necessarily zero.  */
+      spcrng[0] = spcrng[1] = wi::zero (spcrng->get_precision ());
+    }
+  else if (wi::neg_p (offrng[0]))
+    {
+      /* When the lower bound of the offset is negative but the upper
+        bound is not, reduce the upper bound of the remaining space
+        by the upper bound of the offset but leave the lower bound
+        unchanged.  If that makes the upper bound of the space less
+        than the lower bound swap the two.  */
+      spcrng[1] -= wi::ltu_p (offrng[1], spcrng[1]) ? offrng[1] : spcrng[1];
+      if (wi::ltu_p (spcrng[1], spcrng[0]))
+       std::swap (spcrng[1], spcrng[0]);
     }
   else
     {
+      /* When the offset is positive reduce the remaining space by
+        the lower bound of the offset or clear it if the offset is
+        greater.  */
       spcrng[0] -= wi::ltu_p (offrng[0], spcrng[0]) ? offrng[0] : spcrng[0];
       spcrng[1] -= wi::ltu_p (offrng[0], spcrng[1]) ? offrng[0] : spcrng[1];
     }
 
-  if (wi::leu_p (lenrng[0], spcrng[0]))
+  if (wi::leu_p (lenrng[0], spcrng[0])
+      && wi::leu_p (lenrng[1], spcrng[1]))
     return;
 
   if (lenrng[0] == spcrng[1]
@@ -2092,6 +2198,8 @@ maybe_warn_overflow (gimple *stmt, tree len,
   if (!warned)
     return;
 
+  gimple_set_no_warning (stmt, true);
+
   /* If DESTOFF is not null, use it to format the offset value/range.  */
   if (destoff)
     get_range (destoff, offrng);
@@ -2117,17 +2225,91 @@ maybe_warn_overflow (gimple *stmt, tree len,
                offstr, destdecl);
       return;
     }
+
+  if (!alloc_call)
+    return;
+
+  tree allocfn = gimple_call_fndecl (alloc_call);
+  if (!allocfn)
+    {
+      /* For an ALLOC_CALL via a function pointer make a small effort
+        to determine the destination of the pointer.  */
+      allocfn = gimple_call_fn (alloc_call);
+      if (TREE_CODE (allocfn) == SSA_NAME)
+       {
+         gimple *def = SSA_NAME_DEF_STMT (allocfn);
+         if (gimple_assign_single_p (def))
+           {
+             tree rhs = gimple_assign_rhs1 (def);
+             if (DECL_P (rhs))
+               allocfn = rhs;
+             else if (TREE_CODE (rhs) == COMPONENT_REF)
+               allocfn = TREE_OPERAND (rhs, 1);
+           }
+       }
+    }
+
+  if (gimple_call_builtin_p (alloc_call, BUILT_IN_ALLOCA_WITH_ALIGN))
+    {
+      if (sizrng[0] == sizrng[1])
+       inform (gimple_location (alloc_call),
+               "at offset %s to an object with size %wu declared here",
+               offstr, sizrng[0].to_uhwi ());
+      else if (sizrng[0] == 0)
+       {
+         /* Avoid printing impossible sizes.  */
+         if (wi::ltu_p (sizrng[1],
+                        wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+           inform (gimple_location (alloc_call),
+                   "at offset %s to an object with size at most %wu "
+                   "declared here",
+                   offstr, sizrng[1].to_uhwi ());
+         else
+           inform (gimple_location (alloc_call),
+                   "at offset %s to an object declared here", offstr);
+       }
+      else
+       inform (gimple_location (alloc_call),
+               "at offset %s to an object with size between %wu and %wu "
+               "declared here",
+               offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi ());
+      return;
+    }
+
+  if (sizrng[0] == sizrng[1])
+    inform (gimple_location (alloc_call),
+           "at offset %s to an object with size %wu allocated by %qE here",
+           offstr, sizrng[0].to_uhwi (), allocfn);
+  else if (sizrng[0] == 0)
+    {
+      /* Avoid printing impossible sizes.  */
+      if (wi::ltu_p (sizrng[1],
+                    wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2))
+       inform (gimple_location (alloc_call),
+               "at offset %s to an object with size at most %wu allocated "
+               "by %qD here",
+               offstr, sizrng[1].to_uhwi (), allocfn);
+      else
+       inform (gimple_location (alloc_call),
+               "at offset %s to an object allocated by %qE here",
+               offstr, allocfn);
+    }
+  else
+    inform (gimple_location (alloc_call),
+           "at offset %s to an object with size between %wu and %wu "
+           "allocated by %qE here",
+           offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi (), allocfn);
 }
 
 /* Convenience wrapper for the above.  */
 
 static inline void
 maybe_warn_overflow (gimple *stmt, unsigned HOST_WIDE_INT len,
-                    const vr_values *rvals = NULL,
-                    strinfo *si = NULL, bool plus_one = false)
+                    const vr_values *rvals = NULL, strinfo *si = NULL,
+                    bool plus_one = false, bool rawmem = false)
 {
   maybe_warn_overflow (stmt, build_int_cst (size_type_node, len), rvals,
-                      si, plus_one);
+                      si, plus_one, rawmem);
 }
 
 /* Handle a strlen call.  If strlen of the argument is known, replace
@@ -2243,7 +2425,7 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
              tree old = si->nonzero_chars;
              si->nonzero_chars = lhs;
              si->full_string_p = true;
-             if (TREE_CODE (old) == INTEGER_CST)
+             if (old && TREE_CODE (old) == INTEGER_CST)
                {
                  old = fold_convert_loc (loc, TREE_TYPE (lhs), old);
                  tree adj = fold_build2_loc (loc, MINUS_EXPR,
@@ -2422,10 +2604,11 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
 /* Handle a strcpy-like ({st{r,p}cpy,__st{r,p}cpy_chk}) call.
    If strlen of the second argument is known, strlen of the first argument
    is the same after this call.  Furthermore, attempt to convert it to
-   memcpy.  */
+   memcpy.  Uses RVALS to determine range information.  */
 
 static void
-handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
+                      const vr_values *rvals)
 {
   int idx, didx;
   tree src, dst, srclen, len, lhs, type, fn, oldlen;
@@ -2459,6 +2642,11 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   else if (idx < 0)
     srclen = build_int_cst (size_type_node, ~idx);
 
+  maybe_warn_overflow (stmt, srclen, rvals, olddsi, true);
+
+  if (olddsi != NULL)
+    adjust_last_stmt (olddsi, stmt, false);
+
   loc = gimple_location (stmt);
   if (srclen == NULL_TREE)
     switch (bcode)
@@ -2709,26 +2897,58 @@ is_strlen_related_p (tree src, tree len)
   if (TREE_CODE (len) != SSA_NAME)
     return false;
 
-  gimple *def_stmt = SSA_NAME_DEF_STMT (len);
-  if (!def_stmt)
+  if (TREE_CODE (src) == SSA_NAME)
+    {
+      gimple *srcdef = SSA_NAME_DEF_STMT (src);
+      if (is_gimple_assign (srcdef))
+       {
+         /* Handle bitwise AND used in conversions from wider size_t
+            to narrower unsigned types.  */
+         tree_code code = gimple_assign_rhs_code (srcdef);
+         if (code == BIT_AND_EXPR
+             || code == NOP_EXPR)
+           return is_strlen_related_p (gimple_assign_rhs1 (srcdef), len);
+
+         return false;
+       }
+
+      if (gimple_call_builtin_p (srcdef, BUILT_IN_NORMAL))
+       {
+         /* If SRC is the result of a call to an allocation function
+            or strlen, use the function's argument instead.  */
+         tree func = gimple_call_fndecl (srcdef);
+         built_in_function code = DECL_FUNCTION_CODE (func);
+         if (code == BUILT_IN_ALLOCA
+             || code == BUILT_IN_ALLOCA_WITH_ALIGN
+             || code == BUILT_IN_MALLOC
+             || code == BUILT_IN_STRLEN)
+           return is_strlen_related_p (gimple_call_arg (srcdef, 0), len);
+
+         /* FIXME: Handle other functions with attribute alloc_size.  */
+         return false;
+       }
+    }
+
+  gimple *lendef = SSA_NAME_DEF_STMT (len);
+  if (!lendef)
     return false;
 
-  if (is_gimple_call (def_stmt))
+  if (is_gimple_call (lendef))
     {
-      tree func = gimple_call_fndecl (def_stmt);
-      if (!valid_builtin_call (def_stmt)
+      tree func = gimple_call_fndecl (lendef);
+      if (!valid_builtin_call (lendef)
          || DECL_FUNCTION_CODE (func) != BUILT_IN_STRLEN)
        return false;
 
-      tree arg = gimple_call_arg (def_stmt, 0);
+      tree arg = gimple_call_arg (lendef, 0);
       return is_strlen_related_p (src, arg);
     }
 
-  if (!is_gimple_assign (def_stmt))
+  if (!is_gimple_assign (lendef))
     return false;
 
-  tree_code code = gimple_assign_rhs_code (def_stmt);
-  tree rhs1 = gimple_assign_rhs1 (def_stmt);
+  tree_code code = gimple_assign_rhs_code (lendef);
+  tree rhs1 = gimple_assign_rhs1 (lendef);
   tree rhstype = TREE_TYPE (rhs1);
 
   if ((TREE_CODE (rhstype) == POINTER_TYPE && code == POINTER_PLUS_EXPR)
@@ -2741,7 +2961,7 @@ is_strlen_related_p (tree src, tree len)
       return is_strlen_related_p (src, rhs1);
     }
 
-  if (tree rhs2 = gimple_assign_rhs2 (def_stmt))
+  if (tree rhs2 = gimple_assign_rhs2 (lendef))
     {
       /* Integer subtraction is considered strlen-related when both
         arguments are integers and second one is strlen-related.  */
@@ -3187,34 +3407,37 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
-   call.  */
+   call.  Uses RVALS to determine range information.  */
 
 static void
-handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
+                      const vr_values *rvals)
 {
-  int idx, didx;
-  tree src, dst, len, lhs, oldlen, newlen;
+  tree lhs, oldlen, newlen;
   gimple *stmt = gsi_stmt (*gsi);
-  strinfo *si, *dsi, *olddsi;
+  strinfo *si, *dsi;
 
-  len = gimple_call_arg (stmt, 2);
-  src = gimple_call_arg (stmt, 1);
-  dst = gimple_call_arg (stmt, 0);
-  idx = get_stridx (src);
-  if (idx == 0)
-    return;
+  tree len = gimple_call_arg (stmt, 2);
+  tree src = gimple_call_arg (stmt, 1);
+  tree dst = gimple_call_arg (stmt, 0);
 
-  didx = get_stridx (dst);
-  olddsi = NULL;
+  int didx = get_stridx (dst);
+  strinfo *olddsi = NULL;
   if (didx > 0)
     olddsi = get_strinfo (didx);
   else if (didx < 0)
     return;
 
   if (olddsi != NULL
-      && tree_fits_uhwi_p (len)
       && !integer_zerop (len))
-    adjust_last_stmt (olddsi, stmt, false);
+    {
+      maybe_warn_overflow (stmt, len, rvals, olddsi, false, true);
+      adjust_last_stmt (olddsi, stmt, false);
+    }
+
+  int idx = get_stridx (src);
+  if (idx == 0)
+    return;
 
   bool full_string_p;
   if (idx > 0)
@@ -3611,10 +3834,11 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     gimple_set_no_warning (stmt, true);
 }
 
-/* Handle a call to malloc or calloc.  */
+/* Handle a call to an allocation function like alloca, malloc or calloc,
+   or an ordinary allocation function declared with attribute alloc_size.  */
 
 static void
-handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
+handle_alloc_call (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_call_lhs (stmt);
@@ -3628,59 +3852,89 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     length = build_int_cst (size_type_node, 0);
   strinfo *si = new_strinfo (lhs, idx, length, length != NULL_TREE);
   if (bcode == BUILT_IN_CALLOC)
-    si->endptr = lhs;
+    {
+      /* Only set STMT for calloc and malloc.  */
+      si->stmt = stmt;
+      /* Only set ENDPTR for calloc.  */
+      si->endptr = lhs;
+    }
+  else if (bcode == BUILT_IN_MALLOC)
+    si->stmt = stmt;
+
+  /* Set ALLOC is set for all allocation functions.  */
+  si->alloc = stmt;
   set_strinfo (idx, si);
   si->writable = true;
-  si->stmt = stmt;
   si->dont_invalidate = true;
 }
 
 /* Handle a call to memset.
    After a call to calloc, memset(,0,) is unnecessary.
    memset(malloc(n),0,n) is calloc(n,1).
-   return true when the call is transformed, false otherwise.  */
+   return true when the call is transformed, false otherwise.
+   When nonnull uses RVALS to determine range information.  */
 
 static bool
-handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write)
+handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
+                      const vr_values *rvals)
 {
-  gimple *stmt2 = gsi_stmt (*gsi);
-  if (!integer_zerop (gimple_call_arg (stmt2, 1)))
-    return false;
-
-  /* Let the caller know the memset call cleared the destination.  */
-  *zero_write = true;
-
-  tree ptr = gimple_call_arg (stmt2, 0);
-  int idx1 = get_stridx (ptr);
+  gimple *memset_stmt = gsi_stmt (*gsi);
+  tree ptr = gimple_call_arg (memset_stmt, 0);
+  /* Set to the non-constant offset added to PTR.  */
+  wide_int offrng[2];
+  int idx1 = get_stridx (ptr, offrng, rvals);
   if (idx1 <= 0)
     return false;
   strinfo *si1 = get_strinfo (idx1);
   if (!si1)
     return false;
-  gimple *stmt1 = si1->stmt;
-  if (!stmt1 || !is_gimple_call (stmt1))
+  gimple *alloc_stmt = si1->alloc;
+  if (!alloc_stmt || !is_gimple_call (alloc_stmt))
+    return false;
+  tree callee1 = gimple_call_fndecl (alloc_stmt);
+  if (!valid_builtin_call (alloc_stmt))
+    return false;
+  tree alloc_size = gimple_call_arg (alloc_stmt, 0);
+  tree memset_size = gimple_call_arg (memset_stmt, 2);
+
+  /* Check for overflow.  */
+  maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, true);
+
+  /* Bail when there is no statement associated with the destination
+     (the statement may be null even when SI1->ALLOC is not).  */
+  if (!si1->stmt)
     return false;
-  tree callee1 = gimple_call_fndecl (stmt1);
-  if (!valid_builtin_call (stmt1))
+
+  /* Avoid optimizing if store is at a variable offset from the beginning
+     of the allocated object.  */
+  if (offrng[0] != 0 || offrng[0] != offrng[1])
     return false;
+
+  /* Bail when the call writes a non-zero value.  */
+  if (!integer_zerop (gimple_call_arg (memset_stmt, 1)))
+    return false;
+
+  /* Let the caller know the memset call cleared the destination.  */
+  *zero_write = true;
+
   enum built_in_function code1 = DECL_FUNCTION_CODE (callee1);
-  tree size = gimple_call_arg (stmt2, 2);
   if (code1 == BUILT_IN_CALLOC)
-    /* Not touching stmt1 */ ;
+    /* Not touching alloc_stmt */ ;
   else if (code1 == BUILT_IN_MALLOC
-          && operand_equal_p (gimple_call_arg (stmt1, 0), size, 0))
+          && operand_equal_p (memset_size, alloc_size, 0))
     {
-      gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1);
+      /* Replace the malloc + memset calls with calloc.  */
+      gimple_stmt_iterator gsi1 = gsi_for_stmt (si1->stmt);
       update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
-                         size, build_one_cst (size_type_node));
+                         alloc_size, build_one_cst (size_type_node));
       si1->nonzero_chars = build_int_cst (size_type_node, 0);
       si1->full_string_p = true;
       si1->stmt = gsi_stmt (gsi1);
     }
   else
     return false;
-  tree lhs = gimple_call_lhs (stmt2);
-  unlink_stmt_vdef (stmt2);
+  tree lhs = gimple_call_lhs (memset_stmt);
+  unlink_stmt_vdef (memset_stmt);
   if (lhs)
     {
       gimple *assign = gimple_build_assign (lhs, ptr);
@@ -3689,7 +3943,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write)
   else
     {
       gsi_remove (gsi, true);
-      release_defs (stmt2);
+      release_defs (memset_stmt);
     }
 
   return true;
@@ -4391,7 +4645,8 @@ int ssa_name_limit_t::next_ssa_name (tree ssa_name)
    OFFSET and NBYTES are the offset into the representation and
    the size of the access to it determined from a MEM_REF or zero
    for other expressions.
-   Avoid recursing deeper than the limits in SNLIM allow.
+   Uses RVALS to determine range information.
+   Avoids recursing deeper than the limits in SNLIM allow.
    Returns true on success and false otherwise.  */
 
 static bool
@@ -4438,6 +4693,29 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
       if (maxlen + 1 < nbytes)
        return false;
 
+      if (!nbytes
+         && TREE_CODE (si->ptr) == SSA_NAME
+         && !POINTER_TYPE_P (TREE_TYPE (si->ptr)))
+       {
+         /* SI->PTR is an SSA_NAME with a DEF_STMT like
+              _1 = MEM <unsigned int> [(char * {ref-all})s_4(D)];  */
+         gimple *stmt = SSA_NAME_DEF_STMT (exp);
+         if (gimple_assign_single_p (stmt)
+             && gimple_assign_rhs_code (stmt) == MEM_REF)
+           {
+             tree rhs = gimple_assign_rhs1 (stmt);
+             if (tree refsize = TYPE_SIZE_UNIT (TREE_TYPE (rhs)))
+               if (tree_fits_uhwi_p (refsize))
+                 {
+                   nbytes = tree_to_uhwi (refsize);
+                   maxlen = nbytes;
+                 }
+           }
+
+         if (!nbytes)
+           return false;
+       }
+
       if (nbytes <= minlen)
        *nulterm = false;
 
@@ -4454,7 +4732,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
        lenrange[1] = maxlen;
 
       if (lenrange[2] < nbytes)
-       (lenrange[2] = nbytes);
+       lenrange[2] = nbytes;
 
       /* Since only the length of the string are known and not its contents,
         clear ALLNUL and ALLNONNUL purely on the basis of the length.  */
@@ -4672,7 +4950,8 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
    the next statement in the basic block and false otherwise.  */
 
 static bool
-handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rvals)
+handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
+             const vr_values *rvals)
 {
   int idx = -1;
   strinfo *si = NULL;
@@ -5076,16 +5355,23 @@ is_char_type (tree type)
 }
 
 /* Check the built-in call at GSI for validity and optimize it.
+   Uses RVALS to determine range information.
    Return true to let the caller advance *GSI to the next statement
    in the basic block and false otherwise.  */
 
 static bool
-strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
-                               bool *zero_write,
+strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
                                const vr_values *rvals)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    {
+      tree fntype = gimple_call_fntype (stmt);
+      if (fntype && lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (fntype)))
+       handle_alloc_call (BUILT_IN_NONE, gsi);
+    }
+
   /* When not optimizing we must be checking printf calls which
      we do even for user-defined functions when they are declared
      with attribute format.  */
@@ -5108,7 +5394,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
     case BUILT_IN_STRCPY_CHK:
     case BUILT_IN_STPCPY:
     case BUILT_IN_STPCPY_CHK:
-      handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
+      handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
       break;
 
     case BUILT_IN_STRNCAT:
@@ -5127,18 +5413,20 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
     case BUILT_IN_MEMCPY_CHK:
     case BUILT_IN_MEMPCPY:
     case BUILT_IN_MEMPCPY_CHK:
-      handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
+      handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi, rvals);
       break;
     case BUILT_IN_STRCAT:
     case BUILT_IN_STRCAT_CHK:
       handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
       break;
+    case BUILT_IN_ALLOCA:
+    case BUILT_IN_ALLOCA_WITH_ALIGN:
     case BUILT_IN_MALLOC:
     case BUILT_IN_CALLOC:
-      handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
+      handle_alloc_call (DECL_FUNCTION_CODE (callee), gsi);
       break;
     case BUILT_IN_MEMSET:
-      if (handle_builtin_memset (gsi, zero_write))
+      if (handle_builtin_memset (gsi, zero_write, rvals))
        return false;
       break;
     case BUILT_IN_MEMCMP:
@@ -5163,7 +5451,8 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
    If GSI's basic block needs clean-up of EH, set *CLEANUP_EH to true.  */
 
 static void
-handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
+handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
+                       const vr_values *rvals)
 {
   gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_assign_lhs (stmt);
@@ -5266,6 +5555,31 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
            }
        }
     }
+  else if (code == MEM_REF && TREE_CODE (lhs) == SSA_NAME)
+    {
+      if (int idx = new_stridx (lhs))
+       {
+         /* Record multi-byte assignments from MEM_REFs.  */
+         bool storing_all_nonzero_p;
+         bool storing_all_zeros_p;
+         bool full_string_p;
+         unsigned lenrange[] = { UINT_MAX, 0, 0 };
+         tree rhs = gimple_assign_rhs1 (stmt);
+         const bool ranges_valid
+           = count_nonzero_bytes (rhs, lenrange, &full_string_p,
+                                  &storing_all_zeros_p, &storing_all_nonzero_p,
+                                  rvals);
+         if (ranges_valid)
+           {
+             tree length = build_int_cst (sizetype, lenrange[0]);
+             strinfo *si = new_strinfo (lhs, idx, length, full_string_p);
+             set_strinfo (idx, si);
+             si->writable = true;
+             si->dont_invalidate = true;
+             maybe_warn_overflow (stmt, lenrange[2], rvals);
+           }
+       }
+    }
 
   if (strlen_to_stridx)
     {
@@ -5318,29 +5632,35 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
        }
       else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
        /* Handle assignment to a character.  */
-       handle_integral_assign (gsi, cleanup_eh);
+       handle_integral_assign (gsi, cleanup_eh, rvals);
       else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
        {
          tree type = TREE_TYPE (lhs);
          if (TREE_CODE (type) == ARRAY_TYPE)
            type = TREE_TYPE (type);
 
-         bool is_char_store = is_char_type (type);
-         if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
-           {
-             /* To consider stores into char objects via integer types
-                other than char but not those to non-character objects,
-                determine the type of the destination rather than just
-                the type of the access.  */
-             tree ref = TREE_OPERAND (lhs, 0);
-             type = TREE_TYPE (ref);
-             if (TREE_CODE (type) == POINTER_TYPE)
-               type = TREE_TYPE (type);
-             if (TREE_CODE (type) == ARRAY_TYPE)
-               type = TREE_TYPE (type);
-             if (is_char_type (type))
-               is_char_store = true;
-           }
+       bool is_char_store = is_char_type (type);
+       if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
+         {
+           /* To consider stores into char objects via integer types
+              other than char but not those to non-character objects,
+              determine the type of the destination rather than just
+              the type of the access.  */
+           for (int i = 0; i != 2; ++i)
+             {
+               tree ref = TREE_OPERAND (lhs, i);
+               type = TREE_TYPE (ref);
+               if (TREE_CODE (type) == POINTER_TYPE)
+                 type = TREE_TYPE (type);
+               if (TREE_CODE (type) == ARRAY_TYPE)
+                 type = TREE_TYPE (type);
+               if (is_char_type (type))
+                 {
+                   is_char_store = true;
+                   break;
+                 }
+             }
+         }
 
          /* Handle a single or multibyte assignment.  */
          if (is_char_store && !handle_store (gsi, &zero_write, rvals))
index 4d43fc65e9eef6f26f9d70675d57be0f78cddf6a..46f2c0a39967e8f64e6b69bafa7f065d688fdc30 100644 (file)
@@ -25,8 +25,10 @@ extern bool is_strlen_related_p (tree, tree);
 extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
 extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
 
-struct c_strlen_data;
 class vr_values;
+extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
+
+struct c_strlen_data;
 extern void get_range_strlen_dynamic (tree , c_strlen_data *, const vr_values *);
 
 /* APIs internal to strlen pass.  Defined in in gimple-ssa-sprintf.c.  */
index d4d9d62cfc622040bcf1cf48b41bcb0d7ef5d608..f7033b43f9f4b28e33e99fcc324d4524b70cf98d 100644 (file)
@@ -13583,8 +13583,8 @@ get_initializer_for (tree init, tree decl)
    determine the size of an initialized flexible array member.
    If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
    an interior zero-length array.
-   Returns the size (which might be zero for an object with
-   an uninitialized flexible array member) or null if the size
+   Returns the size as sizetype (which might be zero for an object
+   with an uninitialized flexible array member) or null if the size
    cannot be determined.  */
 
 tree
@@ -13733,7 +13733,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
          memsz64 -= baseoff;
          return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
        }
-      return integer_zero_node;
+      return size_zero_node;
     }
 
   /* Return "don't know" for an external non-array object since its
@@ -13744,7 +13744,7 @@ component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
          && DECL_EXTERNAL (base)
          && (!typematch
              || TREE_CODE (basetype) != ARRAY_TYPE)
-         ? NULL_TREE : integer_zero_node);
+         ? NULL_TREE : size_zero_node);
 }
 
 /* Return the machine mode of T.  For vectors, returns the mode of the