PR middle-end/91582 - missing heap overflow detection for strcpy

gcc/ChangeLog:

	PR middle-end/91582
	* builtins.c (gimple_call_alloc_size): Add argument.
	* builtins.h (gimple_call_alloc_size): Same.
	* tree-object-size.c (compute_builtin_object_size): Clear argument.
	* 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): Call maybe_warn_overflow.
	(is_strlen_related_p): Handle dynamically allocated objects.
	(get_range): Add argument.
	(handle_builtin_malloc): Rename...
	(handle_aalloc): ...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.
	* g++.dg/warn/Wstringop-overflow-3.C: Adjust text of expected messages.
	* 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/Warray-bounds-56.c: New test.
	* gcc.dg/Wstringop-overflow-23.c: New test.
	* gcc.dg/Wstringop-overflow-24.c: New test.
	* gcc.dg/Wstringop-overflow-25.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.dg/strlenopt-91.c: New test.
	* gcc.dg/strlenopt-92.c: New test.
	* gcc.target/i386/pr82002-1.c: Prune expected warnings.
	* gcc.target/i386/pr82002-2a.c: Same.
	* gcc.target/i386/pr82002-2b.c: Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 8565fd007e5..ed7bdffa2b2 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3565,10 +3565,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
 }
 
 /* If STMT is a call to an allocation function, returns the size
-   of the object allocated by the call.  */
+   of the object allocated by the call.  If nonnull, set 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 */)
 {
   if (!stmt)
     return NULL_TREE;
@@ -3615,7 +3616,11 @@ gimple_call_alloc_size (gimple *stmt)
 
   tree size = gimple_call_arg (stmt, argidx1);
 
-  wide_int rng1[2];
+  wide_int rng1_buf[2];
+  /* If RNG1 is not set, use the buffer.  */
+  if (!rng1)
+    rng1 = rng1_buf;
+
   if (!get_range (size, rng1))
     return NULL_TREE;
 
@@ -3641,7 +3646,10 @@ gimple_call_alloc_size (gimple *stmt)
   rng1[1] = rng1[1] * rng2[1];
   tree size_max = TYPE_MAX_VALUE (sizetype);
   if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
-    return size_max;
+    {
+      rng1[1] = wi::to_wide (size_max);
+      return size_max;
+    }
 
   return wide_int_to_tree (sizetype, rng1[1]);
 }
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 1c7c4ed9d06..3c494f93779 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -134,7 +134,7 @@ 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);
-tree gimple_call_alloc_size (gimple *);
+tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL);
 extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL);
 
 extern bool readonly_data_expr (tree exp);
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index fee4cc271cd..2af88f4dff3 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1415,9 +1415,12 @@ default_ref_may_alias_errno (ao_ref *ref)
   if (TYPE_UNSIGNED (TREE_TYPE (base))
       || TYPE_MODE (TREE_TYPE (base)) != TYPE_MODE (integer_type_node))
     return false;
-  /* The default implementation assumes an errno location
-     declaration is never defined in the current compilation unit.  */
+  /* The default implementation assumes an errno location declaration
+     is never defined in the current compilation unit and be aliased
+     by a local or read-only variable.  */
   if (DECL_P (base)
+      && DECL_EXTERNAL (base)
+      && !TREE_READONLY (base)
       && !TREE_STATIC (base))
     return true;
   else if (TREE_CODE (base) == MEM_REF
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
index c852b06bbd7..1903f502abd 100644
--- a/gcc/testsuite/c-c++-common/Wrestrict.c
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -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 ();
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
index 6f008549262..db67136b5e7 100644
--- a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
@@ -12,7 +12,7 @@ void sink (void*);
 struct Ax
 {
   char n;
-  char a[];                     // { dg-message "at offset \[0-2\] from object 'Ax::a' declared here" }
+  char a[];                     // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" }
 };
 
 // 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\] from 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" }
 };
 
 // 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\] from 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" }
 };
 
 // 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\] from 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" }
   char x;
 };
 
@@ -307,7 +307,7 @@ NOIPA void ga1ix ()
 struct Bx
 {
   char n;
-  char a[];                     // { dg-message "at offset 0 from object 'Bx::a' declared here" }
+  char a[];                     // { dg-message "at offset 0 to object 'Bx::a' declared here" }
 
   // 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 from 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" }
 
   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 from 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" }
 
   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 from 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" }
 
   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 from 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" }
 
   B234 (int i) { a[i] = 0; }    // { dg-warning "\\\[-Wstringop-overflow" }
 };
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-46.c b/gcc/testsuite/gcc.dg/Warray-bounds-46.c
index 4980f93a470..74e78cbdbe8 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-46.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-46.c
@@ -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]
 
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-47.c b/gcc/testsuite/gcc.dg/Warray-bounds-47.c
index 06ad488d1e0..848ef365163 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-47.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-47.c
@@ -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
index 00000000000..c27e6a494f3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-52.c
@@ -0,0 +1,96 @@
+/* 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]'" }
+  T (INT_MAX, (int[]){ 1 });    // { dg-warning "array subscript \[0-9\]+ is outside array bounds of 'int\\\[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/Warray-bounds-56.c b/gcc/testsuite/gcc.dg/Warray-bounds-56.c
new file mode 100644
index 00000000000..644fcd02cb6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-56.c
@@ -0,0 +1,24 @@
+/* PR middle-end/82612 - missing -Warray-bounds on a non-zero offset
+   from the address of a non-array object
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+int i;
+int f0 (void)
+{
+  int *p = &i;
+  return p[2];      // { dg-warning "-Warray-bounds" }
+}
+
+int f1 (void)
+{
+  int i;
+  int *p = &i;
+  return p[2];      // { dg-warning "-Warray-bounds" }
+}
+
+int f2 (int i)
+{
+  int *p = &i;
+  return p[2];      // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
new file mode 100644
index 00000000000..6eed76a44d9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c
@@ -0,0 +1,389 @@
+/* PR middle-end/91582 - missing heap overflow detection for strcpy
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -Wno-memset-transposed-args -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX    __INT_MAX__
+#define INT_MIN    (-INT_MAX - 1)
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* calloc (size_t, size_t);
+extern void* malloc (size_t);
+extern void* memset (void*, int, size_t);
+extern char* strcpy (char*, const char*);
+extern size_t strlen (const char*);
+
+void sink (void*);
+
+#define T(N, ACCESS)				\
+  do {						\
+    char *p = ALLOC (N);			\
+    ACCESS;					\
+    sink (p);					\
+  } while (0)
+
+
+void test_byte_store_malloc (void)
+{
+#define ALLOC __builtin_malloc
+
+  T (1, p[-1] = 0);
+  // { dg-warning "writing 1 byte into a region of size 0 " "warning" { target *-*-* } .-1 }
+  // { dg-message "at offset -1 to an object with size 1 allocated by '__builtin_malloc'" "note" { target *-*-* } .-2 }
+
+  T (1, p[ 0] = 0);
+
+  T (1, p[ 1] = 0);
+  // { dg-warning "writing 1 byte into a region of size 0 " "warning" { target *-*-* } .-1 }
+  // { dg-message "at offset 1 to an object with size 1 allocated by '__builtin_malloc'" "note" { target *-*-* } .-2 }
+
+  T (1, p[ 2] = 0);
+  // { dg-warning "writing 1 byte into a region of size 0 " "warning" { target *-*-* } .-1 }
+  // { dg-message "at offset 2 to an object with size 1 allocated by '__builtin_malloc'" "note" { target *-*-* } .-2 }
+
+  T (9, p[-2] = 0);
+  // { dg-warning "writing 1 byte into a region of size 0 " "warning" { target *-*-* } .-1 }
+  // { dg-message "at offset -2 to an object with size 9 allocated by '__builtin_malloc'" "note" { target *-*-* } .-2 }
+
+  T (9, p[ 0] = 0);
+  T (9, p[ 8] = 0);
+  T (9, p[ 9] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  // Exercise boundary conditions.
+  T (1, p[DIFF_MIN] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset -\[1-9\]\[0-9\]+ to an object with size 1 allocated by '__builtin_malloc'" "note" { target *-*-* } .-1 }
+  T (1, p[INT_MIN] = 0);    // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[DIFF_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[SIZE_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+
+  size_t i = UR (3, 5);
+
+  T (1, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset \\\[3, 5] to an object with size 1 allocated by '__builtin_malloc'" "note" { target *-*-* } .-1 }
+  T (3, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  T (4, p[i] = 0);
+
+  size_t n = UR (6, 7);
+
+  T (n, p[-1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset -1 to an object with size between 6 and 7 allocated by '__builtin_malloc'" "note" { target *-*-* } .-1 }
+  T (n, p[ 0] = 0);
+  T (n, p[ 6] = 0);
+  T (n, p[ 7] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  T (n, p[i] = 0);
+  T (n, p[i + 3] = 0);
+  T (n, p[i + 4] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset \\\[7, 9] to an object with size between 6 and 7 allocated by '__builtin_malloc' here" "note" { target *-*-* } .-1 }
+  T (n + 1, p[i + 4] = 0);
+
+  char s[] = "123";
+  n = strlen (s);
+  T (n, p[n] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset 3 to an object with size 3 allocated by '__builtin_malloc' here" "note" { target *-*-* } .-1 }
+}
+
+
+void test_byte_store_alloca (void)
+{
+#undef ALLOC
+#define ALLOC __builtin_alloca
+
+  T (1, p[-1] = 0);   // { dg-warning "writing 1 byte into a region of size 0 " }
+  T (1, p[ 0] = 0);
+  T (1, p[ 1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[ 2] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  T (9, p[-1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (9, p[ 0] = 0);
+  T (9, p[ 8] = 0);
+  T (9, p[ 9] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  // Exercise boundary conditions.
+  T (1, p[DIFF_MIN] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[INT_MIN] = 0);    // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[DIFF_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[SIZE_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+
+  size_t i = UR (3, 5);
+
+  T (1, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  T (3, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  T (4, p[i] = 0);
+
+  size_t n = UR (6, 7);
+
+  T (n, p[-1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (n, p[ 0] = 0);
+  T (n, p[ 6] = 0);
+  T (n, p[ 7] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  char s[] = "1234";
+  n = strlen (s);
+  T (n, p[n] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset 4 to an object with size 4 allocated by '__builtin_alloca' here" "note" { target *-*-* } .-1 }
+}
+
+
+void test_byte_store_vla (void)
+{
+  {
+    size_t n = 3;
+    char vla[n];            // { dg-message "at offset 3 to object 'vla\[^\\n\\r\'\]*' with size 3 declared here" "note" }
+    vla[3] = 0;             // { dg-warning "writing 1 byte into a region of size 0 \\\[-Wstringop-overflow=]" }
+    sink (vla);
+  }
+
+#define VLA(N, ACCESS)				\
+  do {						\
+    size_t nelts = N;				\
+    char a[nelts];				\
+    ACCESS;					\
+    sink (a);					\
+  } while (0)
+
+  VLA (1, a[-2] = 0);       // { dg-warning "writing 1 byte into a region of size 0 " }
+  VLA (1, a[-1] = 0);       // { dg-warning "writing 1 byte into a region of size 0 " }
+  VLA (1, a[ 0] = 0);
+  VLA (1, a[ 1] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (1, a[ 2] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+
+  VLA (9, a[-10] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+  // The following fails because the no-warning bit is set on the store
+  // for some unknown reason.
+  VLA (9, a[-1] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (9, a[ 0] = 0);
+  VLA (9, a[ 8] = 0);
+  VLA (9, a[ 9] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+
+  // Exercise boundary conditions.
+  VLA (1, a[DIFF_MIN] = 0); // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (1, a[INT_MIN] = 0);  // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (1, a[DIFF_MAX] = 0); // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (1, a[SIZE_MAX] = 0); // { dg-warning "\\\[-Wstringop-overflow" }
+
+  {
+    int i = SR (-5, -1);
+
+    {
+      size_t n = 3;
+      char vla[n];          // { dg-message "at offset \\\[-5, -1] to object 'vla\[^\n\r\]*' with size 3 declared here" "note" }
+      vla[i] = 0;           // { dg-warning "writing 1 byte into a region of size 0" }
+      sink (vla);
+    }
+
+    VLA (1, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (3, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (4, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+  }
+
+  {
+    int i = SR (3, 5);
+
+    {
+      size_t n = 3;
+      char vla[n];          // { dg-message "at offset \\\[3, 5] to object 'vla\[^\n\r\]*' with size 3 declared here" "note" }
+      vla[i] = 0;           // { dg-warning "writing 1 byte into a region of size 0" }
+      sink (vla);
+    }
+
+    VLA (1, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (3, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (4, a[i] = 0);
+  }
+
+  {
+    size_t i = UR (3, 5);
+
+    {
+      size_t n = 3;
+      char vla[n];          // { dg-message "at offset \\\[3, 5] to object 'vla\[^\n\r\]*' with size 3 declared here" "note" }
+      vla[i] = 0;           // { dg-warning "writing 1 byte into a region of size 0" }
+      sink (vla);
+    }
+
+    VLA (1, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (3, a[i] = 0);      // { dg-warning "\\\[-Wstringop-overflow" }
+    VLA (4, a[i] = 0);
+  }
+
+  size_t n = UR (6, 7);
+
+  VLA (n, a[-1] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+  VLA (n, a[ 0] = 0);
+  VLA (n, a[ 6] = 0);
+  VLA (n, a[ 7] = 0);       // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+void test_byte_store_vla_strlen (void)
+{
+  char s[] = "12345";
+  size_t n = strlen (s);
+  VLA (n, a[n] = 0);        // { dg-warning "\\\[-Wstringop-overflow" }
+  // { dg-message "at offset 5 to an object with size 5 declared here" "note" { target *-*-* } .-1 }
+}
+
+
+void test_byte_store_alloc_size (void)
+{
+  extern __attribute__ ((alloc_size (1), malloc)) void*
+    alloc_1_size (size_t);
+
+  {
+    size_t n = 3;
+    char *p = alloc_1_size (n);   // { dg-message "at offset 3 to an object with size 3 allocated by 'alloc_1_size' here" "note" }
+    p[3] = 0;               // { dg-warning "writing 1 byte into a region of size 0 \\\[-Wstringop-overflow=]" }
+    sink (p);
+  }
+
+  extern __attribute__ ((alloc_size (2), malloc)) void*
+    alloc_2_int (size_t, int);
+
+  {
+    int n = 5;
+    char *p = alloc_2_int (9, n);
+    p[4] = 0;
+    sink (p);
+  }
+
+  extern __attribute__ ((alloc_size (2), malloc)) void*
+    alloc_2_int (size_t, int);
+
+  {
+    int n = 5;
+    char *p = alloc_2_int (9, n);
+    p[4] = 0;
+    // The access below is not diagnosed because GCC doesn't recognize
+    // that the pointer returned by malloc functions doesn't alias any
+    // live object in the process.  I.e., it assumes that the store to
+    // p[4] above modifies p itself.
+    p[5] = 0;               // { dg-warning "writing 1 byte into a region of size 0 \\\[-Wstringop-overflow=]" "pr87313" { xfail *-*-*} }
+   sink (p);
+  }
+
+  {
+    int n = 5;
+    char *p = alloc_2_int (9, n);    // { dg-message "at offset 5 to an object with size 5 allocated by 'alloc_2_int' here" "note" }
+    p[5] = 0;               // { dg-warning "writing 1 byte into a region of size 0 \\\[-Wstringop-overflow=]" }
+    sink (p);
+  }
+
+#undef ALLOC
+#define ALLOC alloc_1_size
+
+  T (1, p[-1] = 0);   // { dg-warning "writing 1 byte into a region of size 0 " }
+  T (1, p[ 0] = 0);
+  T (1, p[ 1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[ 2] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  T (9, p[-1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (9, p[ 0] = 0);
+  T (9, p[ 8] = 0);
+  T (9, p[ 9] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+
+  // Exercise boundary conditions.
+  T (1, p[DIFF_MIN] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[INT_MIN] = 0);    // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[DIFF_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+  T (1, p[SIZE_MAX] = 0);   // { dg-warning "\\\[-Wstringop-overflow" }
+
+  size_t i = UR (3, 5);
+
+  T (1, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  T (3, p[i] = 0);          // { dg-warning "\\\[-Wstringop-overflow" }
+  T (4, p[i] = 0);
+
+  size_t n = UR (6, 7);
+
+  T (n, p[-1] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+  T (n, p[ 0] = 0);
+  T (n, p[ 6] = 0);
+  T (n, p[ 7] = 0);         // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+void test_memset_malloc (void)
+{
+#undef ALLOC
+#define ALLOC __builtin_malloc
+
+  T (0, memset (p, 0, 0));
+  T (0, memset (p, 0, 1));  // { dg-warning "writing 1 byte into a region of size 0 " }
+
+  T (0, memset (p, 1, 0));
+  T (0, memset (p, 1, 1));  // { dg-warning "writing 1 byte into a region of size 0 " }
+
+  T (3, memset (p, 2, 0));
+  T (3, memset (p, 2, 1));
+  T (3, memset (p, 2, 3));
+  T (3, memset (p, 2, 4));  // { dg-warning "writing 4 bytes into a region of size 3 " }
+
+  T (3, memset (p + 1, 2, 1));
+  T (3, memset (p + 1, 3, 2));
+  T (3, memset (p + 1, 4, 3));  // { dg-warning "writing 3 bytes into a region of size 2 " }
+
+  T (4, memset (&p[2], 2, 2));
+  T (4, memset (&p[2], 4, 3));  // { dg-warning "writing 3 bytes into a region of size 2 " }
+
+  T (3, memset (p, 5, INT_MAX));  // { dg-warning "\\\[-Wstringop-overflow" }
+
+  size_t n = UR (3, 5);
+
+  T (n, memset (p, 6, 0));
+  T (n, memset (p, 7, 1));
+  T (n, memset (p, 8, 3));
+  T (n, memset (p, 9, 4));
+  T (n, memset (p, 0, 5));
+  T (n, memset (p, 1, 6));  // { dg-warning "writing 6 bytes into a region of size between 3 and 5" }
+
+  int i = SR (-32767, 32767);
+  T (n, memset (p + i, 0, 1));
+  T (n, memset (p + i, 0, 3));
+  T (n, memset (p + i, 0, 5));
+  T (n, memset (p + i, 0, 6));    // { dg-warning "writing 6 bytes into a region of size between 3 and 5" }
+}
+
+
+#define S(N) ("0123456789" + 10 - N)
+
+const char str0[] = "";
+const char str1[] = "0";
+const char str2[] = "01";
+
+void test_strcpy_malloc (const void *s, size_t n)
+{
+  const char s0[] = "";
+  const char s1[] = "0";
+  const char s2[] = "01";
+
+  // Each of the strcpy calls below is represented differently.  Verify
+  // that they are all handled.
+  //   __builtin_memcpy (p_6, "", 1);
+  T (0, strcpy (p, ""));      // { dg-warning "writing 1 byte into a region of size 0 " }
+  //   MEM[(char * {ref-all})p_10] = 0;
+  T (0, strcpy (p, S (0)));   // { dg-warning "writing 1 byte into a region of size 0 " }
+  //   MEM[(char * {ref-all})p_14] = MEM[(char * {ref-all})&str0];
+  T (0, strcpy (p, str0));    // { dg-warning "writing 1 byte into a region of size 0 " }
+  //   s0 = "";
+  //   __builtin_memcpy (p_18, &s0, 1);
+  T (0, strcpy (p, s0));      // { dg-warning "writing 1 byte into a region of size 0 " }
+
+  T (1, strcpy (p, ""));
+  T (1, strcpy (p, S (0)));
+  T (1, strcpy (p, str0));
+  T (1, strcpy (p, s0));
+
+  T (1, strcpy (p, "1"));     // { dg-warning "writing 2 bytes into a region of size 1 " }
+  //   __builtin_memcpy (p_42, &MEM <char[11]> [(void *)"0123456789" + 9B], 2);
+  T (1, strcpy (p, S (1)));   // { dg-warning "writing 2 bytes into a region of size 1 " }
+  //   MEM <unsigned char[2]> [(char * {ref-all})p_46] = MEM <unsigned char[2]> [(char * {ref-all})&str1];
+  T (1, strcpy (p, str1));    // { dg-warning "writing 2 bytes into a region of size 1 " }
+  T (1, strcpy (p, s1));      // { dg-warning "writing 2 bytes into a region of size 1 " }
+
+  T (2, strcpy (p, S (0)));
+  T (2, strcpy (p, S (2)));   // { dg-warning "writing 3 bytes into a region of size 2 " }
+  T (2, strcpy (p, str2));    // { dg-warning "writing 3 bytes into a region of size 2 " }
+  T (2, strcpy (p, s2));      // { dg-warning "writing 3 bytes into a region of size 2 " }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
new file mode 100644
index 00000000000..1c3401f1206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c
@@ -0,0 +1,290 @@
+/* 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" }
+    strcpy (t, a);                      // { dg-warning "writing 4 bytes into a region of size 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" }
+    strcpy (t, a);                      // { dg-warning "writing 5 bytes into a region of size 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-25.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c
new file mode 100644
index 00000000000..cf306150738
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-25.c
@@ -0,0 +1,16 @@
+/* { dg-do compile }
+   { dg-options "-O2 -Wall " } */
+
+char a[32];
+
+void f (int i)
+{
+  __builtin_sprintf (a + 2, "%i", i);
+
+  char *p = a + 2;
+
+  while (p[0] == 0 && p[1])
+    ++p;
+
+  *--p = 'x';       // { dg-bogus "\\\[-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c
index 7b0dc6e4535..a5f15c2a177 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+/* { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
 
 extern void abort (void);
 
@@ -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" } */
 }
 
diff --git a/gcc/testsuite/gcc.dg/attr-copy-2.c b/gcc/testsuite/gcc.dg/attr-copy-2.c
index f311ca32aa6..cdb4891a0ae 100644
--- a/gcc/testsuite/gcc.dg/attr-copy-2.c
+++ b/gcc/testsuite/gcc.dg/attr-copy-2.c
@@ -2,7 +2,7 @@
    Exercise attribute copy for functions.
    { dg-do compile }
    { dg-require-alias "" }
-   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+   { dg-options "-O2 -Wall" } */
 
 #define Assert(expr)   typedef char AssertExpr[2 * !!(expr) - 1]
 
@@ -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;
 }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
index 320cd51fcf2..f1d46b82a6c 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
@@ -1,4 +1,4 @@
-/* Test exercising -Wstringop-overflow warnings.  */
+/* Test exercising -Wrawmem-overflow and -Wstringop-overflow warnings.  */
 /* { dg-do compile } */
 /* { dg-options "-O2 -Wstringop-overflow=1" } */
 
@@ -49,7 +49,7 @@ void test_memop_warn_local (const void *src)
   memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
   escape (a, src);
 
-  /* At -Wstringop-overflow=1 the destination is considered to be
+  /* At -Wrawmem-overflow=1 the destination is considered to be
      the whole array and its size is therefore sizeof a.  */
   memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
   escape (a, src);
@@ -110,10 +110,10 @@ 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" } */
   escape (a, src);
 
-  /* At -Wstringop-overflow=1 the destination is considered to be
+  /* At -Wrawmem-overflow=1 the destination is considered to be
      the whole array and its size is therefore sizeof a.  */
   memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
   escape (a, src);
@@ -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" } */
   escape (b);
 
   /* The following idiom of clearing multiple members of a struct is
diff --git a/gcc/testsuite/gcc.dg/strlenopt-86.c b/gcc/testsuite/gcc.dg/strlenopt-86.c
index 3e86fa3c90a..d2029443556 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-86.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-86.c
@@ -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);
diff --git a/gcc/testsuite/gcc.dg/strlenopt-91.c b/gcc/testsuite/gcc.dg/strlenopt-91.c
new file mode 100644
index 00000000000..1ede65f6e0a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-91.c
@@ -0,0 +1,40 @@
+/* Test to verify that alloca calls followed by memset to zero out
+   the allocated object don't end up transformed into calloc.
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+void sink (void*);
+
+void alloca_memset_cst (void)
+{
+  void *p = __builtin_alloca (8);
+  __builtin_memset (p, 0, 8);
+  sink (p);
+}
+
+void alloca_memset_var (unsigned n)
+{
+  void *p = __builtin_alloca (n);
+  __builtin_memset (p, 0, n);
+  sink (p);
+}
+
+
+void vla_memset_cst (void)
+{
+  unsigned n = 32;
+
+  char a[n];
+  __builtin_memset (a, 0, n);
+  sink (a);
+}
+
+
+void vla_memset_var (unsigned n)
+{
+  char a[n];
+  __builtin_memset (a, 0, n);
+  sink (a);
+}
+
+/* { dg-final { scan-tree-dump-times "calloc" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-92.c b/gcc/testsuite/gcc.dg/strlenopt-92.c
new file mode 100644
index 00000000000..6d947c22025
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-92.c
@@ -0,0 +1,55 @@
+/* Verify that a strlen() call is eliminated for a pointer to a region
+   of memory allocated by calloc() even if a byte is written at some
+   non-zero offset into the region that isn't known to be nul.
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+unsigned n0, n1;
+
+void*
+keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
+{
+  char *p = __builtin_calloc (a, 1);
+
+  p[1] = 'x';
+
+  __builtin_memset (p, 0, b);
+
+  n0 = __builtin_strlen (p);
+
+  return p;
+}
+
+void*
+keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
+{
+  char *p = __builtin_calloc (a, 1);
+
+  p[1] = x;
+
+  __builtin_memset (p, 0, b);
+
+  n0 = __builtin_strlen (p);
+
+  return p;
+}
+
+void*
+keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
+{
+  char *p = __builtin_calloc (a, 1);
+
+  p[1] = x;
+  __builtin_memset (p, 0, b);
+
+  n0 = __builtin_strlen (p);
+
+  p[3] = x;
+  __builtin_memset (p, 0, c);
+
+  n1 = __builtin_strlen (p);
+
+  return p;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_strlen" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr82002-1.c b/gcc/testsuite/gcc.target/i386/pr82002-1.c
index 86678a01992..b4d4bd3d125 100644
--- a/gcc/testsuite/gcc.target/i386/pr82002-1.c
+++ b/gcc/testsuite/gcc.target/i386/pr82002-1.c
@@ -10,3 +10,5 @@ b ()
   a (c);
   a (c);
 }
+
+// { dg-prune-output "\\\[-Wstringop-overflow" }
diff --git a/gcc/testsuite/gcc.target/i386/pr82002-2a.c b/gcc/testsuite/gcc.target/i386/pr82002-2a.c
index dbcc105fcd2..66df36ad317 100644
--- a/gcc/testsuite/gcc.target/i386/pr82002-2a.c
+++ b/gcc/testsuite/gcc.target/i386/pr82002-2a.c
@@ -11,4 +11,4 @@ b ()
   a (c);
 }
 
-/* { dg-prune-output "\\\[-Wstringop-overflow" }  */
+// { dg-prune-output "\\\[-Wstringop-overflow" }
diff --git a/gcc/testsuite/gcc.target/i386/pr82002-2b.c b/gcc/testsuite/gcc.target/i386/pr82002-2b.c
index e1ad737263c..040f0d0fe96 100644
--- a/gcc/testsuite/gcc.target/i386/pr82002-2b.c
+++ b/gcc/testsuite/gcc.target/i386/pr82002-2b.c
@@ -11,4 +11,4 @@ b ()
   a (c);
 }
 
-/* { dg-prune-output "\\\[-Wstringop-overflow" }  */
+// { dg-prune-output "\\\[-Wstringop-overflow" }
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index d591c36dea3..0f22860bcaf 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -466,7 +466,6 @@ alloc_object_size (const gcall *call, int object_size_type)
   return unknown[object_size_type];
 }
 
-
 /* If object size is propagated from one of function's arguments directly
    to its return value, return that argument for GIMPLE_CALL statement CALL.
    Otherwise return NULL.  */
@@ -671,6 +670,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
     }
 
   *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+  *poff = NULL_TREE;
   return *psize != unknown[object_size_type];
 }
 
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index cdca50aeb79..d03b669d77e 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -85,14 +85,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;
@@ -190,7 +196,8 @@ 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.  */
+   and returns either the constant value or VAL on success or null
+   when the range couldn't be determined .  */
 
 tree
 get_range (tree val, wide_int minmax[2], const vr_values *rvals /* = NULL */)
@@ -388,10 +395,10 @@ get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
    must not be used in for functions that modify the string.  */
 
 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)
     {
@@ -470,12 +477,12 @@ 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
 			   iterations and store it in the OFFRNG array.  */
-			offrng[0] += offset;
+ 			offrng[0] += offset;
 			offrng[1] += offset;
 		      }
 		    else
@@ -677,6 +684,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;
@@ -843,6 +851,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.  */
@@ -890,45 +900,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))
@@ -1202,80 +1224,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;
 }
@@ -1294,6 +1323,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;
@@ -1587,6 +1617,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:
@@ -1863,7 +1895,8 @@ 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 otherise.  */
 
 static void
 maybe_warn_overflow (gimple *stmt, tree len,
@@ -1873,82 +1906,144 @@ maybe_warn_overflow (gimple *stmt, tree len,
   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 (computed below).  */
   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);
+      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 = size_zero_node;
+      destsize = compute_objsize (dest, 1, &destdecl, &off);
+      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
@@ -1966,26 +2061,13 @@ 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 remaining space in the destiation 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]))
     {
       /* FIXME: Handle negative offsets into allocated objects.  */
-      if (destdecl)
+      if (destsize)
 	spcrng[0] = spcrng[1] = wi::zero (spcrng->get_precision ());
       else
 	return;
@@ -1996,7 +2078,8 @@ maybe_warn_overflow (gimple *stmt, tree len,
       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]
@@ -2097,6 +2180,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);
@@ -2110,18 +2195,74 @@ maybe_warn_overflow (gimple *stmt, tree len,
     sprintf (offstr, "[%lli, %lli]",
 	     (long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
 
-  if (destdecl && DECL_P (destdecl))
+  if (destdecl)
     {
       if (tree size = DECL_SIZE_UNIT (destdecl))
 	inform (DECL_SOURCE_LOCATION (destdecl),
-		"at offset %s from object %qD with size %E declared here",
+		"at offset %s to object %qD with size %E declared here",
 		offstr, destdecl, size);
       else
 	inform (DECL_SOURCE_LOCATION (destdecl),
-		"at offset %s from object %qD declared here",
+		"at offset %s to object %qD declared here",
 		offstr, destdecl);
       return;
     }
+
+  if (!alloc_call)
+    return;
+
+  tree allocfn = gimple_call_fndecl (alloc_call);
+
+  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 %qD 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 %qD here",
+		offstr, allocfn);
+    }
+  else
+    inform (gimple_location (alloc_call),
+	    "at offset %s to an object with size between %wu and %wu "
+	    "allocated by %qD here",
+	    offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi (), allocfn);
 }
 
 /* Convenience wrapper for the above.  */
@@ -2248,7 +2389,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,
@@ -2426,7 +2567,8 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
    memcpy.  */
 
 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;
@@ -2460,6 +2602,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)
@@ -2710,26 +2857,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)
@@ -2742,7 +2921,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.  */
@@ -3191,31 +3370,34 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
    call.  */
 
 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);
+      adjust_last_stmt (olddsi, stmt, false);
+    }
+
+  int idx = get_stridx (src);
+  if (idx == 0)
+    return;
 
   bool full_string_p;
   if (idx > 0)
@@ -3612,10 +3794,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);
@@ -3629,10 +3812,19 @@ 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;
 }
 
@@ -3642,46 +3834,61 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
    return true when the call is transformed, false otherwise.  */
 
 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 (stmt1);
-  if (!valid_builtin_call (stmt1))
+  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);
+
+  /* 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 (alloc_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);
@@ -3690,7 +3897,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;
@@ -4433,6 +4640,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;
 
@@ -4449,7 +4679,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.  */
@@ -4666,7 +4896,8 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
    '*(int*)a = 12345').  Return true when handled.  */
 
 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;
@@ -5073,12 +5304,18 @@ is_char_type (tree type)
    in the CFG 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);
+    }
+
   if (!flag_optimize_strlen
       || !strlen_optimize
       || !valid_builtin_call (stmt))
@@ -5104,7 +5341,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:
@@ -5123,18 +5360,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:
@@ -5158,7 +5397,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);
@@ -5261,6 +5501,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)
     {
@@ -5312,7 +5577,7 @@ 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);
@@ -5326,14 +5591,20 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
 	       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;
+	    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.  */
