public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] track dynamic allocation in strlen (PR 91582)
@ 2019-11-12  4:53 Martin Sebor
  2019-11-18 19:04 ` Martin Sebor
  2019-11-30 17:03 ` [PATCH] track dynamic allocation in strlen (PR 91582) Christophe Lyon
  0 siblings, 2 replies; 14+ messages in thread
From: Martin Sebor @ 2019-11-12  4:53 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1661 bytes --]

The attached patch extends the strlen pass to detect out-of-bounds
accesses to memory allocated by calls to other allocation functions
besides calloc and malloc, as well as VLAs, and user-defined
functions declared with attribute alloc_size.  There is some
overlap with the _FORTIFY_SOURCE detection but thanks to
the extensive use of ranges, this enhancement detects many more
cases of overflow.

The solution primarily improves warnings but some of the changes
also improve codegen in some cases as a side-effect.  I hope to
take better advantage of the optimization opportunities the dynamic
memory tracking opens up (and also better buffer overflow and array
out-of-bounds detection) in GCC 11.

Although the strlen pass already tracks some dynamic memory calls
(calloc and malloc) rather than extending the same infrastructure
(strinfo::stmt) to others I took the approach of adding a separate
data member for the other calls (strinfo::alloc) and tracking those
independently.  I did this to keep the changes only minimally
intrusive.  In the future (post GCC 10) it might be worth
considering merging both.

Besides introducing the new member and making use of it, the rest
of the changes were prompted by weaknesses exposed by test cases
involving dynamically allocated objects.

The patch is intended to apply on top of the two related patches
posted last week ([1] and [2]).  For all tests to pass also expects
the fix for PR 92412 posted earlier today ([3]).

Martin

[1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
[2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
[3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html

[-- Attachment #2: gcc-91582.diff --]
[-- Type: text/x-patch, Size: 87383 bytes --]

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.  */

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] track dynamic allocation in strlen (PR 91582)
  2019-11-12  4:53 [PATCH] track dynamic allocation in strlen (PR 91582) Martin Sebor
@ 2019-11-18 19:04 ` Martin Sebor
  2019-11-25 18:04   ` [PING 2][PATCH] " Martin Sebor
  2019-11-30 17:03 ` [PATCH] track dynamic allocation in strlen (PR 91582) Christophe Lyon
  1 sibling, 1 reply; 14+ messages in thread
From: Martin Sebor @ 2019-11-18 19:04 UTC (permalink / raw)
  To: gcc-patches

Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html

On 11/11/19 6:27 PM, Martin Sebor wrote:
> The attached patch extends the strlen pass to detect out-of-bounds
> accesses to memory allocated by calls to other allocation functions
> besides calloc and malloc, as well as VLAs, and user-defined
> functions declared with attribute alloc_size.  There is some
> overlap with the _FORTIFY_SOURCE detection but thanks to
> the extensive use of ranges, this enhancement detects many more
> cases of overflow.
> 
> The solution primarily improves warnings but some of the changes
> also improve codegen in some cases as a side-effect.  I hope to
> take better advantage of the optimization opportunities the dynamic
> memory tracking opens up (and also better buffer overflow and array
> out-of-bounds detection) in GCC 11.
> 
> Although the strlen pass already tracks some dynamic memory calls
> (calloc and malloc) rather than extending the same infrastructure
> (strinfo::stmt) to others I took the approach of adding a separate
> data member for the other calls (strinfo::alloc) and tracking those
> independently.  I did this to keep the changes only minimally
> intrusive.  In the future (post GCC 10) it might be worth
> considering merging both.
> 
> Besides introducing the new member and making use of it, the rest
> of the changes were prompted by weaknesses exposed by test cases
> involving dynamically allocated objects.
> 
> The patch is intended to apply on top of the two related patches
> posted last week ([1] and [2]).  For all tests to pass also expects
> the fix for PR 92412 posted earlier today ([3]).
> 
> Martin
> 
> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PING 2][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-11-18 19:04 ` Martin Sebor
@ 2019-11-25 18:04   ` Martin Sebor
  2019-12-07  0:19     ` [PING 3][PATCH] " Martin Sebor
  0 siblings, 1 reply; 14+ messages in thread
From: Martin Sebor @ 2019-11-25 18:04 UTC (permalink / raw)
  To: gcc-patches

Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html

On 11/18/19 11:23 AM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
> 
> On 11/11/19 6:27 PM, Martin Sebor wrote:
>> The attached patch extends the strlen pass to detect out-of-bounds
>> accesses to memory allocated by calls to other allocation functions
>> besides calloc and malloc, as well as VLAs, and user-defined
>> functions declared with attribute alloc_size.  There is some
>> overlap with the _FORTIFY_SOURCE detection but thanks to
>> the extensive use of ranges, this enhancement detects many more
>> cases of overflow.
>>
>> The solution primarily improves warnings but some of the changes
>> also improve codegen in some cases as a side-effect.  I hope to
>> take better advantage of the optimization opportunities the dynamic
>> memory tracking opens up (and also better buffer overflow and array
>> out-of-bounds detection) in GCC 11.
>>
>> Although the strlen pass already tracks some dynamic memory calls
>> (calloc and malloc) rather than extending the same infrastructure
>> (strinfo::stmt) to others I took the approach of adding a separate
>> data member for the other calls (strinfo::alloc) and tracking those
>> independently.  I did this to keep the changes only minimally
>> intrusive.  In the future (post GCC 10) it might be worth
>> considering merging both.
>>
>> Besides introducing the new member and making use of it, the rest
>> of the changes were prompted by weaknesses exposed by test cases
>> involving dynamically allocated objects.
>>
>> The patch is intended to apply on top of the two related patches
>> posted last week ([1] and [2]).  For all tests to pass also expects
>> the fix for PR 92412 posted earlier today ([3]).
>>
>> Martin
>>
>> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
>> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
>> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html
> 

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] track dynamic allocation in strlen (PR 91582)
  2019-11-12  4:53 [PATCH] track dynamic allocation in strlen (PR 91582) Martin Sebor
  2019-11-18 19:04 ` Martin Sebor
@ 2019-11-30 17:03 ` Christophe Lyon
  2019-12-06 18:23   ` Martin Sebor
  1 sibling, 1 reply; 14+ messages in thread
From: Christophe Lyon @ 2019-11-30 17:03 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches

On Tue, 12 Nov 2019 at 02:28, Martin Sebor <msebor@gmail.com> wrote:
>
> The attached patch extends the strlen pass to detect out-of-bounds
> accesses to memory allocated by calls to other allocation functions
> besides calloc and malloc, as well as VLAs, and user-defined
> functions declared with attribute alloc_size.  There is some
> overlap with the _FORTIFY_SOURCE detection but thanks to
> the extensive use of ranges, this enhancement detects many more
> cases of overflow.
>
> The solution primarily improves warnings but some of the changes
> also improve codegen in some cases as a side-effect.  I hope to
> take better advantage of the optimization opportunities the dynamic
> memory tracking opens up (and also better buffer overflow and array
> out-of-bounds detection) in GCC 11.
>
> Although the strlen pass already tracks some dynamic memory calls
> (calloc and malloc) rather than extending the same infrastructure
> (strinfo::stmt) to others I took the approach of adding a separate
> data member for the other calls (strinfo::alloc) and tracking those
> independently.  I did this to keep the changes only minimally
> intrusive.  In the future (post GCC 10) it might be worth
> considering merging both.
>
> Besides introducing the new member and making use of it, the rest
> of the changes were prompted by weaknesses exposed by test cases
> involving dynamically allocated objects.
>
> The patch is intended to apply on top of the two related patches
> posted last week ([1] and [2]).  For all tests to pass also expects
> the fix for PR 92412 posted earlier today ([3]).
>
> Martin

Hi Martin,

The new tests gcc.dg/Wstringop-overflow-23.c and gcc.dg/Wstringop-overflow-24.c
fail on arm-eabi (they pass on arm*-linux-gnueabi*):

FAIL: gcc.dg/Wstringop-overflow-23.c (test for excess errors)
Excess errors:
/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c:174:25: warning: passing
argument 4 of 'pfwr4_3' from incompatible pointer type
[-Wincompatible-pointer-types]
/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c:175:25: warning: passing
argument 4 of 'pfwr4_3' from incompatible pointer type
[-Wincompatible-pointer-types]

FAIL: gcc.dg/Wstringop-overflow-24.c (test for excess errors)
Excess errors:
/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:33:12: warning: passing
argument 1 of 'rd1_int' from incompatible pointer type
[-Wincompatible-pointer-types]
/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:35:12: warning: passing
argument 1 of 'rd1_int' from incompatible pointer type
[-Wincompatible-pointer-types]
/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:202:25: warning: passing
argument 4 of 'pfwr4_3' from incompatible pointer type
[-Wincompatible-pointer-types]
/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:203:25: warning: passing
argument 4 of 'pfwr4_3' from incompatible pointer type
[-Wincompatible-pointer-types]

I'm seeing that at r278638

Christophe

>
> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH] track dynamic allocation in strlen (PR 91582)
  2019-11-30 17:03 ` [PATCH] track dynamic allocation in strlen (PR 91582) Christophe Lyon
@ 2019-12-06 18:23   ` Martin Sebor
  0 siblings, 0 replies; 14+ messages in thread
From: Martin Sebor @ 2019-12-06 18:23 UTC (permalink / raw)
  To: Christophe Lyon; +Cc: gcc-patches

On 11/30/19 9:31 AM, Christophe Lyon wrote:
> On Tue, 12 Nov 2019 at 02:28, Martin Sebor <msebor@gmail.com> wrote:
>>
>> The attached patch extends the strlen pass to detect out-of-bounds
>> accesses to memory allocated by calls to other allocation functions
>> besides calloc and malloc, as well as VLAs, and user-defined
>> functions declared with attribute alloc_size.  There is some
>> overlap with the _FORTIFY_SOURCE detection but thanks to
>> the extensive use of ranges, this enhancement detects many more
>> cases of overflow.
>>
>> The solution primarily improves warnings but some of the changes
>> also improve codegen in some cases as a side-effect.  I hope to
>> take better advantage of the optimization opportunities the dynamic
>> memory tracking opens up (and also better buffer overflow and array
>> out-of-bounds detection) in GCC 11.
>>
>> Although the strlen pass already tracks some dynamic memory calls
>> (calloc and malloc) rather than extending the same infrastructure
>> (strinfo::stmt) to others I took the approach of adding a separate
>> data member for the other calls (strinfo::alloc) and tracking those
>> independently.  I did this to keep the changes only minimally
>> intrusive.  In the future (post GCC 10) it might be worth
>> considering merging both.
>>
>> Besides introducing the new member and making use of it, the rest
>> of the changes were prompted by weaknesses exposed by test cases
>> involving dynamically allocated objects.
>>
>> The patch is intended to apply on top of the two related patches
>> posted last week ([1] and [2]).  For all tests to pass also expects
>> the fix for PR 92412 posted earlier today ([3]).
>>
>> Martin
> 
> Hi Martin,
> 
> The new tests gcc.dg/Wstringop-overflow-23.c and gcc.dg/Wstringop-overflow-24.c
> fail on arm-eabi (they pass on arm*-linux-gnueabi*):
> 
> FAIL: gcc.dg/Wstringop-overflow-23.c (test for excess errors)
> Excess errors:
> /gcc/testsuite/gcc.dg/Wstringop-overflow-23.c:174:25: warning: passing
> argument 4 of 'pfwr4_3' from incompatible pointer type
> [-Wincompatible-pointer-types]
> /gcc/testsuite/gcc.dg/Wstringop-overflow-23.c:175:25: warning: passing
> argument 4 of 'pfwr4_3' from incompatible pointer type
> [-Wincompatible-pointer-types]
> 
> FAIL: gcc.dg/Wstringop-overflow-24.c (test for excess errors)
> Excess errors:
> /gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:33:12: warning: passing
> argument 1 of 'rd1_int' from incompatible pointer type
> [-Wincompatible-pointer-types]
> /gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:35:12: warning: passing
> argument 1 of 'rd1_int' from incompatible pointer type
> [-Wincompatible-pointer-types]
> /gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:202:25: warning: passing
> argument 4 of 'pfwr4_3' from incompatible pointer type
> [-Wincompatible-pointer-types]
> /gcc/testsuite/gcc.dg/Wstringop-overflow-24.c:203:25: warning: passing
> argument 4 of 'pfwr4_3' from incompatible pointer type
> [-Wincompatible-pointer-types]
> 
> I'm seeing that at r278638

Those are due to simple oversights in the tests (using int* rather
than int32_t*).  I've fixed that in r279059.

Thanks
Martin

> 
> Christophe
> 
>>
>> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
>> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
>> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-11-25 18:04   ` [PING 2][PATCH] " Martin Sebor
@ 2019-12-07  0:19     ` Martin Sebor
  2019-12-07  0:35       ` Jakub Jelinek
                         ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Martin Sebor @ 2019-12-07  0:19 UTC (permalink / raw)
  To: gcc-patches, Jeff Law

[-- Attachment #1: Type: text/plain, Size: 2526 bytes --]

With part 2 (below) of this work committed, I've rebased the patch
on the top of trunk and on top of the updated part 1 (also below).
Attached is the result, retested on x86_64-linux.

[1] include size and offset in -Wstringop-overflow
     https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00392.html

[2] extend -Wstringop-overflow to allocated objects
     (committed in r278983)
     https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00263.html

On 11/25/19 10:54 AM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
> 
> On 11/18/19 11:23 AM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
>>
>> On 11/11/19 6:27 PM, Martin Sebor wrote:
>>> The attached patch extends the strlen pass to detect out-of-bounds
>>> accesses to memory allocated by calls to other allocation functions
>>> besides calloc and malloc, as well as VLAs, and user-defined
>>> functions declared with attribute alloc_size.  There is some
>>> overlap with the _FORTIFY_SOURCE detection but thanks to
>>> the extensive use of ranges, this enhancement detects many more
>>> cases of overflow.
>>>
>>> The solution primarily improves warnings but some of the changes
>>> also improve codegen in some cases as a side-effect.  I hope to
>>> take better advantage of the optimization opportunities the dynamic
>>> memory tracking opens up (and also better buffer overflow and array
>>> out-of-bounds detection) in GCC 11.
>>>
>>> Although the strlen pass already tracks some dynamic memory calls
>>> (calloc and malloc) rather than extending the same infrastructure
>>> (strinfo::stmt) to others I took the approach of adding a separate
>>> data member for the other calls (strinfo::alloc) and tracking those
>>> independently.  I did this to keep the changes only minimally
>>> intrusive.  In the future (post GCC 10) it might be worth
>>> considering merging both.
>>>
>>> Besides introducing the new member and making use of it, the rest
>>> of the changes were prompted by weaknesses exposed by test cases
>>> involving dynamically allocated objects.
>>>
>>> The patch is intended to apply on top of the two related patches
>>> posted last week ([1] and [2]).  For all tests to pass also expects
>>> the fix for PR 92412 posted earlier today ([3]).
>>>
>>> Martin
>>>
>>> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
>>> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
>>> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html
>>
> 


[-- Attachment #2: gcc-91582.diff --]
[-- Type: text/x-patch, Size: 86230 bytes --]

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-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_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.
	* 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-26.c: New test.
	* gcc.dg/Wstringop-overflow-27.c: New test.
	* gcc.dg/Wstringop-overflow-28.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.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 1ee84f343a3..5db0bd3226c 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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"
@@ -3697,10 +3698,12 @@ 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 */,
+			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,10 +3765,7 @@ 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.  */
@@ -3802,7 +3803,7 @@ gimple_call_alloc_size (gimple *stmt)
 
 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)
@@ -3826,8 +3827,14 @@ 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;
+	      return size;
+	    }
+	  return NULL_TREE;
 	}
 
       if (!is_gimple_assign (stmt))
@@ -3857,13 +3864,13 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
 		    ;
 		  else if (wi::ltu_p (wioff, wisiz))
 		    {
-		      *poff = size_binop (PLUS_EXPR, *poff, off);
+		      *poff = *poff ? size_binop (PLUS_EXPR, *poff, off) : off;
 		      return wide_int_to_tree (TREE_TYPE (size),
 					       wi::sub (wisiz, wioff));
 		    }
 		  else
 		    {
-		      *poff = size_binop (PLUS_EXPR, *poff, off);
+		      *poff = *poff ? size_binop (PLUS_EXPR, *poff, off) : off;
 		      return size_zero_node;
 		    }
 		}
@@ -3888,15 +3895,15 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
 			;
 		      else if (wi::ltu_p (min, wisiz))
 			{
-			  *poff = size_binop (PLUS_EXPR, *poff,
-					      wide_int_to_tree (sizetype, min));
+			  tree t = wide_int_to_tree (sizetype, min);
+			  *poff = *poff ? size_binop (PLUS_EXPR, *poff, t) : t;
 			  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));
+			  tree t = wide_int_to_tree (sizetype, min);
+			  *poff = *poff ? size_binop (PLUS_EXPR, *poff, t) : t;
 			  return size_zero_node;
 			}
 		    }
@@ -3926,10 +3933,19 @@ 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;
+
 	  /* Adjust SIZE either up or down by the sum of *POFF and OFF
 	     above.  */
 	  if (TREE_CODE (dest) == ARRAY_REF)
@@ -3938,24 +3954,28 @@ 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)
+	  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.  */
+	      off = fold_convert (ptrdiff_type_node, off);
+	      if (tree_int_cst_sgn (off) < 0
+		  || tree_int_cst_lt (off, size))
+		return fold_build2 (MINUS_EXPR, size_type_node, size, off);
+	      return integer_zero_node;
 	    }
-	  else
-	    return NULL_TREE;
 
 	  /* Convert to the same precision to keep wide_int from "helpfuly"
 	     crashing whenever it sees other argumments.  */
@@ -4022,6 +4042,9 @@ compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */,
       return component_ref_size (dest);
     }
 
+  if (TREE_CODE (dest) == VAR_DECL)
+    return DECL_SIZE_UNIT (dest);
+
   if (TREE_CODE (dest) != ADDR_EXPR)
     return NULL_TREE;
 
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 0fcccc12a39..2736f161b6b 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -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);
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/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/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c
index 7b0dc6e4535..4c0cd9a14c4 100644
--- a/gcc/testsuite/gcc.dg/attr-alloc_size.c
+++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c
@@ -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..ffc7208f4a7 100644
--- a/gcc/testsuite/gcc.dg/attr-copy-2.c
+++ b/gcc/testsuite/gcc.dg/attr-copy-2.c
@@ -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..87dd6ac4e89 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
@@ -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
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.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/tree-ssa-strlen.h b/gcc/tree-ssa-strlen.h
index 4d43fc65e9e..46f2c0a3996 100644
--- a/gcc/tree-ssa-strlen.h
+++ b/gcc/tree-ssa-strlen.h
@@ -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.  */
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..1a7d76fcc2a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-52.c
@@ -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-26.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-26.c
new file mode 100644
index 00000000000..b59f90d2f8e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-26.c
@@ -0,0 +1,390 @@
+/* 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" "pr?????" { xfail *-*-* } }
+  // { dg-message "at offset 3 to an object with size 3 allocated by '__builtin_malloc' here" "note" { xfail *-*-* } .-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" "pr?????" { xfail *-*-* } }
+  // { dg-message "at offset 4 to an object with size 4 allocated by '__builtin_alloca' here" "note" { xfail *-*-* } .-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" "pr?????" { xfail *-*-* } }
+  // { dg-message "at offset 5 to an object with size 5 declared here" "note" { xfail *-*-* } .-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 was not diagnosed because GCC didn't recognize
+    // that the pointer returned by malloc functions doesn't alias any
+    // live object in the process.  I.e., it assumed that the store to
+    // p[4] above modified p itself.  This is pr87313 but now it is
+    // diagnosed so something might be off with the analysis.
+    p[5] = 0;               // { dg-warning "writing 1 byte into a region of size 0 \\\[-Wstringop-overflow=]" }
+   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-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
new file mode 100644
index 00000000000..249ce2b6ad5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
@@ -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
index 00000000000..861c9206be4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
@@ -0,0 +1,32 @@
+/* Simple test to verify that constants computed by the strlen pass
+   are propagated through the CFG and subsequently made available
+   within the pass.
+  { dg-do compile }
+  { dg-options "-O2 -Wall " } */
+
+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_strlen_memcpy (void)
+{
+  char a[8] = "1234567";
+  char b[7];
+
+  // The value of N is computed to be 7 below.  Verify that it's
+  // available in the store to B[N] as well as in B[N + 1].
+  size_t n = strlen (a);
+  memcpy (b, a, n);
+  b[n] = 0;         // { dg-warning "writing 1 byte into a region of size 0" "constant propagation" { xfail *-*-* } }
+  sink (b);
+
+  b[n + 1] = 0;     // { dg-warning "writing 1 byte into a region of size 0" "constant propagation" { xfail *-*-* } }
+  sink (b);
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 212ac7152bf..525bb6550b4 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "vr-values.h"
 #include "gimple-ssa-evrp-analyze.h"
 
+#pragma GCC optimize ("0")
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
    string length of a string literal (~strlen).  */
@@ -84,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;
@@ -189,20 +196,20 @@ 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 .  */
 
-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:
@@ -383,10 +390,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)
     {
@@ -465,7 +472,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 +679,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 +846,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 +895,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 +1219,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 +1318,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 +1612,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,7 +1890,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 otherwise.  */
 
 static void
 maybe_warn_overflow (gimple *stmt, tree len,
@@ -1868,82 +1901,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, 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 = size_zero_node;
+      destsize = compute_objsize (dest, 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)
+	    {
+	      gimple *stmt = SSA_NAME_DEF_STMT (destdecl);
+	      if (is_gimple_call (stmt))
+		alloc_call = stmt;
+	      destdecl = NULL_TREE;
+	    }
+
+	  if (!get_range (destsize, sizrng, rvals))
 	    {
-	      wide_int wioff = wi::to_wide (off, offrng->get_precision ());
-	      offrng[0] += wioff;
-	      offrng[1] += wioff;
+	      /* 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,26 +2056,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;
@@ -1991,7 +2073,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]
@@ -2092,6 +2175,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,6 +2202,62 @@ maybe_warn_overflow (gimple *stmt, tree len,
 		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.  */
@@ -2243,7 +2384,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,
@@ -2425,7 +2566,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;
@@ -2459,6 +2601,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 +2856,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 +2920,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.  */
@@ -3190,31 +3369,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)
@@ -3611,10 +3793,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,10 +3811,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;
 }
 
@@ -3641,46 +3833,66 @@ 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);
+
+  /* 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;
+
+  /* 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 +3901,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;
@@ -4438,6 +4650,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 +4689,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 +4907,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;
@@ -5080,12 +5316,18 @@ is_char_type (tree type)
    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 +5350,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 +5369,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 +5407,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 +5511,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 +5588,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))

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-07  0:19     ` [PING 3][PATCH] " Martin Sebor
@ 2019-12-07  0:35       ` Jakub Jelinek
  2019-12-11 23:23       ` Martin Sebor
  2020-01-08 11:53       ` Andreas Schwab
  2 siblings, 0 replies; 14+ messages in thread
From: Jakub Jelinek @ 2019-12-07  0:35 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches, Jeff Law

On Fri, Dec 06, 2019 at 05:19:36PM -0700, Martin Sebor wrote:
> With part 2 (below) of this work committed, I've rebased the patch
> on the top of trunk and on top of the updated part 1 (also below).
> Attached is the result, retested on x86_64-linux.

> --- a/gcc/tree-ssa-strlen.c
> +++ b/gcc/tree-ssa-strlen.c
> @@ -61,6 +61,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "vr-values.h"
>  #include "gimple-ssa-evrp-analyze.h"
>  
> +#pragma GCC optimize ("0")
>  /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
>     is an index into strinfo vector, negative value stands for
>     string length of a string literal (~strlen).  */

Why this?  Some debugging left-over?

	Jakub

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-07  0:19     ` [PING 3][PATCH] " Martin Sebor
  2019-12-07  0:35       ` Jakub Jelinek
@ 2019-12-11 23:23       ` Martin Sebor
  2019-12-14  0:56         ` Martin Sebor
  2020-01-08 11:53       ` Andreas Schwab
  2 siblings, 1 reply; 14+ messages in thread
From: Martin Sebor @ 2019-12-11 23:23 UTC (permalink / raw)
  To: gcc-patches, Jeff Law

[-- Attachment #1: Type: text/plain, Size: 2956 bytes --]

Jeff's buildbot exposed a bug in the patch that caused false
positives in cases involving negative offsets into destinations
involving pointers pointing into multiple regions of the same
object.  The attached revision fixes that bug, plus makes
a few minor other fixes pointed out in PR 92868.

On 12/6/19 5:19 PM, Martin Sebor wrote:
> With part 2 (below) of this work committed, I've rebased the patch
> on the top of trunk and on top of the updated part 1 (also below).
> Attached is the result, retested on x86_64-linux.
> 
> [1] include size and offset in -Wstringop-overflow
>      https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00392.html
> 
> [2] extend -Wstringop-overflow to allocated objects
>      (committed in r278983)
>      https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00263.html
> 
> On 11/25/19 10:54 AM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
>>
>> On 11/18/19 11:23 AM, Martin Sebor wrote:
>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
>>>
>>> On 11/11/19 6:27 PM, Martin Sebor wrote:
>>>> The attached patch extends the strlen pass to detect out-of-bounds
>>>> accesses to memory allocated by calls to other allocation functions
>>>> besides calloc and malloc, as well as VLAs, and user-defined
>>>> functions declared with attribute alloc_size.  There is some
>>>> overlap with the _FORTIFY_SOURCE detection but thanks to
>>>> the extensive use of ranges, this enhancement detects many more
>>>> cases of overflow.
>>>>
>>>> The solution primarily improves warnings but some of the changes
>>>> also improve codegen in some cases as a side-effect.  I hope to
>>>> take better advantage of the optimization opportunities the dynamic
>>>> memory tracking opens up (and also better buffer overflow and array
>>>> out-of-bounds detection) in GCC 11.
>>>>
>>>> Although the strlen pass already tracks some dynamic memory calls
>>>> (calloc and malloc) rather than extending the same infrastructure
>>>> (strinfo::stmt) to others I took the approach of adding a separate
>>>> data member for the other calls (strinfo::alloc) and tracking those
>>>> independently.  I did this to keep the changes only minimally
>>>> intrusive.  In the future (post GCC 10) it might be worth
>>>> considering merging both.
>>>>
>>>> Besides introducing the new member and making use of it, the rest
>>>> of the changes were prompted by weaknesses exposed by test cases
>>>> involving dynamically allocated objects.
>>>>
>>>> The patch is intended to apply on top of the two related patches
>>>> posted last week ([1] and [2]).  For all tests to pass also expects
>>>> the fix for PR 92412 posted earlier today ([3]).
>>>>
>>>> Martin
>>>>
>>>> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
>>>> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
>>>> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html
>>>
>>
> 


[-- Attachment #2: gcc-91582.diff --]
[-- Type: text/x-patch, Size: 80856 bytes --]

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 (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.
	* gcc/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_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.
	* 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-26.c: New test.
	* gcc.dg/Wstringop-overflow-27.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.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 279248)
+++ gcc/builtins.c	(working copy)
@@ -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 @@ tree
 
   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,13 +3765,10 @@ tree
      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);
@@ -3774,7 +3775,8 @@ tree
   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);
@@ -3789,26 +3791,29 @@ 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;
 
@@ -3826,8 +3831,14 @@ compute_objsize (tree dest, int ostype, tree *pdec
       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;
+	      return size;
+	    }
+	  return NULL_TREE;
 	}
 
       if (!is_gimple_assign (stmt))
@@ -3853,17 +3864,21 @@ compute_objsize (tree dest, int ostype, tree *pdec
 		  /* 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 +3890,31 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	      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);
+		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));
+		    /* 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
+		      {
+			tree t = wide_int_to_tree (ptrdiff_type_node, min);
+			if (*poff)
+			  {
+			    *poff = fold_convert (ptrdiff_type_node, *poff);
+			    *poff = size_binop (PLUS_EXPR, *poff, t);
+			  }
+			else
+			  *poff = t;
+			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 +3940,25 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	      && *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 argumments.  */
+	  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 +3967,35 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	      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,7 +4007,7 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	      *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;
@@ -3979,7 +4014,7 @@ compute_objsize (tree dest, int ostype, tree *pdec
 
 	      /* 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,29 +4034,42 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	  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;
     }
 
+  /* At the point, if the size can be determiend the offset must be
+     zero but may adjusted by callers.  */
+  *poff = integer_zero_node;
+
   if (TREE_CODE (dest) == COMPONENT_REF)
     {
       *pdecl = TREE_OPERAND (dest, 1);
-      return component_ref_size (dest);
+      /* Only return constant sizes for now while callers depend on it.  */
+      if (tree size = component_ref_size (dest))
+	if (TREE_CODE (size) == INTEGER_CST)
+	  return size;
+      return NULL_TREE;
     }
 
+  if (TREE_CODE (dest) == VAR_DECL)
+    {
+      *pdecl = dest;
+      return DECL_SIZE_UNIT (dest);
+    }
+
   if (TREE_CODE (dest) != ADDR_EXPR)
     return NULL_TREE;
 
Index: gcc/builtins.h
===================================================================
--- gcc/builtins.h	(revision 279247)
+++ gcc/builtins.h	(working copy)
@@ -133,9 +133,13 @@ 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);
 extern unsigned HOST_WIDE_INT target_newline;
Index: gcc/testsuite/c-c++-common/Wrestrict.c
===================================================================
--- gcc/testsuite/c-c++-common/Wrestrict.c	(revision 279247)
+++ gcc/testsuite/c-c++-common/Wrestrict.c	(working copy)
@@ -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: gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
===================================================================
--- gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C	(revision 279248)
+++ gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C	(working copy)
@@ -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: gcc/testsuite/gcc.dg/Warray-bounds-46.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-46.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/Warray-bounds-46.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/Warray-bounds-47.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-47.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/Warray-bounds-47.c	(working copy)
@@ -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*);
Index: gcc/testsuite/gcc.dg/Warray-bounds-52.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-52.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Warray-bounds-52.c	(working copy)
@@ -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]'" }
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-27.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-27.c	(working copy)
@@ -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);
+  }
+}
Index: gcc/testsuite/gcc.dg/attr-alloc_size.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/attr-alloc_size.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/attr-copy-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-copy-2.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/attr-copy-2.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/strlenopt-86.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-86.c	(revision 279247)
+++ gcc/testsuite/gcc.dg/strlenopt-86.c	(working copy)
@@ -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 @@ void*
 }
 
 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 @@ void*
 }
 
 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: gcc/testsuite/gcc.target/i386/pr82002-1.c
===================================================================
--- gcc/testsuite/gcc.target/i386/pr82002-1.c	(revision 279247)
+++ gcc/testsuite/gcc.target/i386/pr82002-1.c	(working copy)
@@ -10,3 +10,5 @@ b ()
   a (c);
   a (c);
 }
+
+// { dg-prune-output "\\\[-Wstringop-overflow" }
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 279248)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -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,20 @@ static int get_stridx_plus_constant (strinfo *, un
 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 .  */
 
-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 +221,20 @@ static void handle_builtin_stxncpy (built_in_funct
 	= (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:
@@ -383,10 +389,10 @@ get_addr_stridx (tree exp, tree ptr, unsigned HOST
    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)
     {
@@ -465,7 +471,7 @@ static int
 		       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 +678,7 @@ new_strinfo (tree ptr, int idx, tree nonzero_chars
   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 +845,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 +894,57 @@ dump_strlen_info (FILE *fp, gimple *stmt, const vr
 		  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 +1218,87 @@ get_range_strlen_dynamic (tree src, c_strlen_data
     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 +1317,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 +1611,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,7 +1889,8 @@ maybe_set_strlen_range (tree lhs, tree src, tree b
 }
 
 /* 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.  */
 
 static void
 maybe_warn_overflow (gimple *stmt, tree len,
@@ -1868,82 +1900,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))
     {
+      dest = gimple_call_arg (stmt, 0);
       writefn = gimple_call_fndecl (stmt);
-      dest = gimple_call_arg (stmt, 0);
     }
 
+  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, 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, 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,26 +2055,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;
@@ -1991,7 +2072,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]
@@ -2092,6 +2174,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,6 +2201,62 @@ maybe_warn_overflow (gimple *stmt, tree len,
 		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.  */
@@ -2243,7 +2383,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,
@@ -2425,7 +2565,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;
@@ -2459,6 +2600,11 @@ static void
   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 +2855,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 +2919,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.  */
@@ -3190,22 +3368,19 @@ handle_builtin_stxncpy (built_in_function, gimple_
    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)
@@ -3212,10 +3387,16 @@ static void
     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)
     {
@@ -3611,10 +3792,11 @@ handle_builtin_strcat (enum built_in_function bcod
     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,10 +3810,19 @@ static void
     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;
 }
 
@@ -3641,38 +3832,58 @@ static void
    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);
+
+  /* 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;
+
+  /* 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);
@@ -3679,8 +3890,8 @@ static bool
     }
   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 +3900,7 @@ static bool
   else
     {
       gsi_remove (gsi, true);
-      release_defs (stmt2);
+      release_defs (memset_stmt);
     }
 
   return true;
@@ -4438,6 +4649,29 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_
       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 +4688,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_
 	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 +4906,8 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3
    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;
@@ -5080,12 +5315,18 @@ is_char_type (tree type)
    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 +5349,7 @@ static bool
     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 +5368,20 @@ static bool
     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 +5406,8 @@ static bool
    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 +5510,31 @@ static void
 	    }
 	}
     }
+  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,7 +5587,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi
 	}
       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);
@@ -5325,22 +5594,28 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi
 	  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: gcc/tree-ssa-strlen.h
===================================================================
--- gcc/tree-ssa-strlen.h	(revision 279247)
+++ gcc/tree-ssa-strlen.h	(working copy)
@@ -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);
 
+class vr_values;
+extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
+
 struct c_strlen_data;
-class vr_values;
 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: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 279247)
+++ gcc/tree.c	(working copy)
@@ -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_
 	  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_
 	  && 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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-11 23:23       ` Martin Sebor
@ 2019-12-14  0:56         ` Martin Sebor
  2019-12-15 11:25           ` Jeff Law
  0 siblings, 1 reply; 14+ messages in thread
From: Martin Sebor @ 2019-12-14  0:56 UTC (permalink / raw)
  To: gcc-patches, Jeff Law

[-- Attachment #1: Type: text/plain, Size: 3212 bytes --]

After more testing by Jeff's buildbot and correcting the problems
it exposed I have committed the attached patch in r279392.

Martin

On 12/11/19 4:23 PM, Martin Sebor wrote:
> Jeff's buildbot exposed a bug in the patch that caused false
> positives in cases involving negative offsets into destinations
> involving pointers pointing into multiple regions of the same
> object.  The attached revision fixes that bug, plus makes
> a few minor other fixes pointed out in PR 92868.
> 
> On 12/6/19 5:19 PM, Martin Sebor wrote:
>> With part 2 (below) of this work committed, I've rebased the patch
>> on the top of trunk and on top of the updated part 1 (also below).
>> Attached is the result, retested on x86_64-linux.
>>
>> [1] include size and offset in -Wstringop-overflow
>>      https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00392.html
>>
>> [2] extend -Wstringop-overflow to allocated objects
>>      (committed in r278983)
>>      https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00263.html
>>
>> On 11/25/19 10:54 AM, Martin Sebor wrote:
>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
>>>
>>> On 11/18/19 11:23 AM, Martin Sebor wrote:
>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00812.html
>>>>
>>>> On 11/11/19 6:27 PM, Martin Sebor wrote:
>>>>> The attached patch extends the strlen pass to detect out-of-bounds
>>>>> accesses to memory allocated by calls to other allocation functions
>>>>> besides calloc and malloc, as well as VLAs, and user-defined
>>>>> functions declared with attribute alloc_size.  There is some
>>>>> overlap with the _FORTIFY_SOURCE detection but thanks to
>>>>> the extensive use of ranges, this enhancement detects many more
>>>>> cases of overflow.
>>>>>
>>>>> The solution primarily improves warnings but some of the changes
>>>>> also improve codegen in some cases as a side-effect.  I hope to
>>>>> take better advantage of the optimization opportunities the dynamic
>>>>> memory tracking opens up (and also better buffer overflow and array
>>>>> out-of-bounds detection) in GCC 11.
>>>>>
>>>>> Although the strlen pass already tracks some dynamic memory calls
>>>>> (calloc and malloc) rather than extending the same infrastructure
>>>>> (strinfo::stmt) to others I took the approach of adding a separate
>>>>> data member for the other calls (strinfo::alloc) and tracking those
>>>>> independently.  I did this to keep the changes only minimally
>>>>> intrusive.  In the future (post GCC 10) it might be worth
>>>>> considering merging both.
>>>>>
>>>>> Besides introducing the new member and making use of it, the rest
>>>>> of the changes were prompted by weaknesses exposed by test cases
>>>>> involving dynamically allocated objects.
>>>>>
>>>>> The patch is intended to apply on top of the two related patches
>>>>> posted last week ([1] and [2]).  For all tests to pass also expects
>>>>> the fix for PR 92412 posted earlier today ([3]).
>>>>>
>>>>> Martin
>>>>>
>>>>> [1] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00429.html
>>>>> [2] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00652.html
>>>>> [3] https://gcc.gnu.org/ml/gcc-patches/2019-11/msg00800.html
>>>>
>>>
>>
> 


[-- Attachment #2: gcc-91582.diff --]
[-- Type: text/x-patch, Size: 100636 bytes --]

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.
	* gcc/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_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.
	* 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.

Index: gcc/builtins.c
===================================================================
--- gcc/builtins.c	(revision 279379)
+++ gcc/builtins.c	(working copy)
@@ -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 @@ tree
 
   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,13 +3765,10 @@ tree
      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);
@@ -3774,7 +3775,8 @@ tree
   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 @@ tree
   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 *pdec
       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 *pdec
 		  /* 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 *pdec
 	      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);
+		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::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));
+		    /* 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 *pdec
 	      && *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 *pdec
 	      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,7 +4043,7 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	      *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;
@@ -3979,7 +4050,7 @@ compute_objsize (tree dest, int ostype, tree *pdec
 
 	      /* 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,40 +4070,26 @@ compute_objsize (tree dest, int ostype, tree *pdec
 	  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);
-    }
+  /* Try simple DECLs not handled above.  */
+  if (tree size = addr_decl_size (dest, pdecl, poff))
+    return size;
 
-  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;
-    }
-
   tree type = TREE_TYPE (dest);
   if (TREE_CODE (type) == POINTER_TYPE)
     type = TREE_TYPE (type);
Index: gcc/builtins.h
===================================================================
--- gcc/builtins.h	(revision 279379)
+++ gcc/builtins.h	(working copy)
@@ -133,9 +133,13 @@ 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);
 extern unsigned HOST_WIDE_INT target_newline;
Index: gcc/testsuite/c-c++-common/Wrestrict.c
===================================================================
--- gcc/testsuite/c-c++-common/Wrestrict.c	(revision 279379)
+++ gcc/testsuite/c-c++-common/Wrestrict.c	(working copy)
@@ -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: gcc/testsuite/c-c++-common/Wstringop-truncation-4.c
===================================================================
--- gcc/testsuite/c-c++-common/Wstringop-truncation-4.c	(revision 279379)
+++ gcc/testsuite/c-c++-common/Wstringop-truncation-4.c	(working copy)
@@ -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: gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
===================================================================
--- gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C	(revision 279379)
+++ gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C	(working copy)
@@ -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: gcc/testsuite/gcc.dg/Warray-bounds-46.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-46.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/Warray-bounds-46.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/Warray-bounds-47.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-47.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/Warray-bounds-47.c	(working copy)
@@ -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*);
Index: gcc/testsuite/gcc.dg/Warray-bounds-52.c
===================================================================
--- gcc/testsuite/gcc.dg/Warray-bounds-52.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Warray-bounds-52.c	(working copy)
@@ -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]'" }
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-27.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-27.c	(working copy)
@@ -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);
+  }
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-28.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-28.c	(working copy)
@@ -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'" "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" "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" "store just past the end" { xfail *-*-* } }
+  q[1] = 3;                       // { dg-warning "\\\[-Wstringop-overflow" }
+
+  sink (p, q);
+}
Index: gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-overflow-29.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-overflow-29.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/attr-alloc_size.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-alloc_size.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/attr-alloc_size.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/attr-copy-2.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-copy-2.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/attr-copy-2.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c
===================================================================
--- gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/builtin-stringop-chk-5.c	(working copy)
@@ -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: gcc/testsuite/gcc.dg/strlenopt-86.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-86.c	(revision 279379)
+++ gcc/testsuite/gcc.dg/strlenopt-86.c	(working copy)
@@ -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 @@ void*
 }
 
 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 @@ void*
 }
 
 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: gcc/testsuite/gcc.target/i386/pr82002-1.c
===================================================================
--- gcc/testsuite/gcc.target/i386/pr82002-1.c	(revision 279379)
+++ gcc/testsuite/gcc.target/i386/pr82002-1.c	(working copy)
@@ -10,3 +10,5 @@ b ()
   a (c);
   a (c);
 }
+
+// { dg-prune-output "\\\[-Wstringop-overflow" }
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 279379)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -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 *, un
 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 @@ static void handle_builtin_stxncpy (built_in_funct
 	= (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
    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 @@ static int
 		       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
   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
 		  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
     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 b
 }
 
 /* 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))
     {
+      dest = gimple_call_arg (stmt, 0);
       writefn = gimple_call_fndecl (stmt);
-      dest = gimple_call_arg (stmt, 0);
     }
 
+  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,6 +2225,80 @@ 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.  */
@@ -2123,11 +2305,11 @@ maybe_warn_overflow (gimple *stmt, tree len,
 
 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 @@ static void
   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,25 +3407,22 @@ handle_builtin_stxncpy (built_in_function, gimple_
 /* 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)
@@ -3212,10 +3429,16 @@ static void
     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 bcod
     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,10 +3852,19 @@ static void
     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;
 }
 
@@ -3638,41 +3871,62 @@ static void
 /* 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 (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, 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;
+
+  /* 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);
@@ -3679,8 +3933,8 @@ static bool
     }
   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 @@ static bool
   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_
       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_
 	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
    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 @@ static bool
     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 @@ static bool
     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 @@ static bool
    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 @@ static void
 	    }
 	}
     }
+  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,7 +5632,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi
 	}
       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);
@@ -5325,22 +5639,28 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi
 	  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: gcc/tree-ssa-strlen.h
===================================================================
--- gcc/tree-ssa-strlen.h	(revision 279379)
+++ gcc/tree-ssa-strlen.h	(working copy)
@@ -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);
 
+class vr_values;
+extern tree get_range (tree, wide_int[2], const vr_values * = NULL);
+
 struct c_strlen_data;
-class vr_values;
 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: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 279379)
+++ gcc/tree.c	(working copy)
@@ -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_
 	  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_
 	  && 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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-14  0:56         ` Martin Sebor
@ 2019-12-15 11:25           ` Jeff Law
  2019-12-17 15:31             ` Christophe Lyon
  0 siblings, 1 reply; 14+ messages in thread
From: Jeff Law @ 2019-12-15 11:25 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches

On Fri, 2019-12-13 at 17:55 -0700, Martin Sebor wrote:
> After more testing by Jeff's buildbot and correcting the problems
> it exposed I have committed the attached patch in r279392.
And just to close the loop on this.  Your last version fixed all the
issues I saw in the tester.

jeff


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-15 11:25           ` Jeff Law
@ 2019-12-17 15:31             ` Christophe Lyon
  0 siblings, 0 replies; 14+ messages in thread
From: Christophe Lyon @ 2019-12-17 15:31 UTC (permalink / raw)
  To: Jeff Law; +Cc: Martin Sebor, gcc-patches

On Sat, 14 Dec 2019 at 22:35, Jeff Law <law@redhat.com> wrote:
>
> On Fri, 2019-12-13 at 17:55 -0700, Martin Sebor wrote:
> > After more testing by Jeff's buildbot and correcting the problems
> > it exposed I have committed the attached patch in r279392.
> And just to close the loop on this.  Your last version fixed all the
> issues I saw in the tester.
>

Hi,

On my side, I've noticed that r279392 caused regressions on arm.
On arm-none-linux-gnueabi
--with-mode arm
--with-cpu cortex-a9
I see
gcc.dg/strlenopt-8.c: pattern found 2 times
FAIL: gcc.dg/strlenopt-8.c scan-tree-dump-times strlen1 "strlen \\(" 0
FAIL: gcc.dg/tree-ssa/pr87022.c (test for excess errors)
Excess errors:
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]
/gcc/testsuite/gcc.dg/tree-ssa/pr87022.c:26:19: warning: writing 1
byte into a region of size 0 [-Wstringop-overflow=]

Christophe


> jeff
>
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2019-12-07  0:19     ` [PING 3][PATCH] " Martin Sebor
  2019-12-07  0:35       ` Jakub Jelinek
  2019-12-11 23:23       ` Martin Sebor
@ 2020-01-08 11:53       ` Andreas Schwab
  2020-01-08 18:42         ` Jeff Law
  2020-01-08 18:48         ` [committed] Make Wstringop-overflow-27 testnames unique [was Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)] Jeff Law
  2 siblings, 2 replies; 14+ messages in thread
From: Andreas Schwab @ 2020-01-08 11:53 UTC (permalink / raw)
  To: Martin Sebor; +Cc: gcc-patches, Jeff Law

On Dez 06 2019, Martin Sebor wrote:

> diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
> new file mode 100644
> index 00000000000..249ce2b6ad5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c

> +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 }

Please make the test name unique.

> +    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 }

Likewise.

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)
  2020-01-08 11:53       ` Andreas Schwab
@ 2020-01-08 18:42         ` Jeff Law
  2020-01-08 18:48         ` [committed] Make Wstringop-overflow-27 testnames unique [was Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)] Jeff Law
  1 sibling, 0 replies; 14+ messages in thread
From: Jeff Law @ 2020-01-08 18:42 UTC (permalink / raw)
  To: Andreas Schwab, Martin Sebor; +Cc: gcc-patches

On Wed, 2020-01-08 at 12:52 +0100, Andreas Schwab wrote:
> On Dez 06 2019, Martin Sebor wrote:
> 
> > diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
> > new file mode 100644
> > index 00000000000..249ce2b6ad5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
> > +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 }
> 
> Please make the test name unique.
I've got a patch to do that in my local tree.  I'll push it
momentarily.

jeff

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [committed] Make Wstringop-overflow-27 testnames unique [was Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)]
  2020-01-08 11:53       ` Andreas Schwab
  2020-01-08 18:42         ` Jeff Law
@ 2020-01-08 18:48         ` Jeff Law
  1 sibling, 0 replies; 14+ messages in thread
From: Jeff Law @ 2020-01-08 18:48 UTC (permalink / raw)
  To: Andreas Schwab, Martin Sebor; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1670 bytes --]

On Wed, 2020-01-08 at 12:52 +0100, Andreas Schwab wrote:
> On Dez 06 2019, Martin Sebor wrote:
> 
> > diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
> > new file mode 100644
> > index 00000000000..249ce2b6ad5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
> > +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 }
> 
> Please make the test name unique.
> 
> > +    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 }
> 
> Likewise.
Fixed via the attached patch which I've committed to the trunk.

jeff


[-- Attachment #2: P --]
[-- Type: text/plain, Size: 2821 bytes --]

commit 48e76be17adbf93fe264fc118adbcf2ae6a14806
Author: law <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Wed Jan 8 18:46:33 2020 +0000

            * gcc.dg/Wstringop-overflow-27.c: Make testnames unique.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@280016 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 537091ffec6..622589e3db6 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2020-01-08  Jeff Law  <law@redhat.com>
+
+	* gcc.dg/Wstringop-overflow-27.c: Make testnames unique.
+
 2020-01-08  Joel Brobecker  <brobecker@adacore.com>
             Olivier Hainque  <hainque@adacore.com>
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
index 249ce2b6ad5..8e2cfe30725 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
@@ -260,8 +260,8 @@ void test_strcpy_warn (const char *s)
        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 }
+    char *t = (char*)calloc (n, 1);     // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note 1" { xfail *-*-* } }
+                                        // { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note 2" { target *-*-* } .-1 }
     strcpy (t, a);                      // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
 
     sink (t);
@@ -270,8 +270,8 @@ void test_strcpy_warn (const char *s)
   {
     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 }
+    char *t = (char*)malloc (n);        // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note 1" { xfail *-*-* } }
+                                        // { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note 2" { target *-*-* } .-1 }
     strcpy (t, a);                      // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
     sink (t);
   }

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2020-01-08 18:48 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-12  4:53 [PATCH] track dynamic allocation in strlen (PR 91582) Martin Sebor
2019-11-18 19:04 ` Martin Sebor
2019-11-25 18:04   ` [PING 2][PATCH] " Martin Sebor
2019-12-07  0:19     ` [PING 3][PATCH] " Martin Sebor
2019-12-07  0:35       ` Jakub Jelinek
2019-12-11 23:23       ` Martin Sebor
2019-12-14  0:56         ` Martin Sebor
2019-12-15 11:25           ` Jeff Law
2019-12-17 15:31             ` Christophe Lyon
2020-01-08 11:53       ` Andreas Schwab
2020-01-08 18:42         ` Jeff Law
2020-01-08 18:48         ` [committed] Make Wstringop-overflow-27 testnames unique [was Re: [PING 3][PATCH] track dynamic allocation in strlen (PR 91582)] Jeff Law
2019-11-30 17:03 ` [PATCH] track dynamic allocation in strlen (PR 91582) Christophe Lyon
2019-12-06 18:23   ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).